~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/parisc/kernel/unwind.c

Version: ~ [ linux-5.12 ] ~ [ linux-5.11.16 ] ~ [ linux-5.10.32 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.114 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.188 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.231 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.267 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.267 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Kernel unwinding support
  3  *
  4  * (c) 2002-2004 Randolph Chung <tausq@debian.org>
  5  *
  6  * Derived partially from the IA64 implementation. The PA-RISC
  7  * Runtime Architecture Document is also a useful reference to
  8  * understand what is happening here
  9  */
 10 
 11 #include <linux/kernel.h>
 12 #include <linux/init.h>
 13 #include <linux/sched.h>
 14 #include <linux/slab.h>
 15 #include <linux/kallsyms.h>
 16 #include <linux/sort.h>
 17 
 18 #include <asm/uaccess.h>
 19 #include <asm/assembly.h>
 20 #include <asm/asm-offsets.h>
 21 #include <asm/ptrace.h>
 22 
 23 #include <asm/unwind.h>
 24 
 25 /* #define DEBUG 1 */
 26 #ifdef DEBUG
 27 #define dbg(x...) printk(x)
 28 #else
 29 #define dbg(x...)
 30 #endif
 31 
 32 #define KERNEL_START (KERNEL_BINARY_TEXT_START)
 33 
 34 extern struct unwind_table_entry __start___unwind[];
 35 extern struct unwind_table_entry __stop___unwind[];
 36 
 37 static spinlock_t unwind_lock;
 38 /*
 39  * the kernel unwind block is not dynamically allocated so that
 40  * we can call unwind_init as early in the bootup process as 
 41  * possible (before the slab allocator is initialized)
 42  */
 43 static struct unwind_table kernel_unwind_table __read_mostly;
 44 static LIST_HEAD(unwind_tables);
 45 
 46 static inline const struct unwind_table_entry *
 47 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
 48 {
 49         const struct unwind_table_entry *e = NULL;
 50         unsigned long lo, hi, mid;
 51 
 52         lo = 0; 
 53         hi = table->length - 1; 
 54         
 55         while (lo <= hi) {
 56                 mid = (hi - lo) / 2 + lo;
 57                 e = &table->table[mid];
 58                 if (addr < e->region_start)
 59                         hi = mid - 1;
 60                 else if (addr > e->region_end)
 61                         lo = mid + 1;
 62                 else
 63                         return e;
 64         }
 65 
 66         return NULL;
 67 }
 68 
 69 static const struct unwind_table_entry *
 70 find_unwind_entry(unsigned long addr)
 71 {
 72         struct unwind_table *table;
 73         const struct unwind_table_entry *e = NULL;
 74 
 75         if (addr >= kernel_unwind_table.start && 
 76             addr <= kernel_unwind_table.end)
 77                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
 78         else 
 79                 list_for_each_entry(table, &unwind_tables, list) {
 80                         if (addr >= table->start && 
 81                             addr <= table->end)
 82                                 e = find_unwind_entry_in_table(table, addr);
 83                         if (e) {
 84                                 /* Move-to-front to exploit common traces */
 85                                 list_move(&table->list, &unwind_tables);
 86                                 break;
 87                         }
 88                 }
 89 
 90         return e;
 91 }
 92 
 93 static void
 94 unwind_table_init(struct unwind_table *table, const char *name,
 95                   unsigned long base_addr, unsigned long gp,
 96                   void *table_start, void *table_end)
 97 {
 98         struct unwind_table_entry *start = table_start;
 99         struct unwind_table_entry *end = 
100                 (struct unwind_table_entry *)table_end - 1;
101 
102         table->name = name;
103         table->base_addr = base_addr;
104         table->gp = gp;
105         table->start = base_addr + start->region_start;
106         table->end = base_addr + end->region_end;
107         table->table = (struct unwind_table_entry *)table_start;
108         table->length = end - start + 1;
109         INIT_LIST_HEAD(&table->list);
110 
111         for (; start <= end; start++) {
112                 if (start < end && 
113                     start->region_end > (start+1)->region_start) {
114                         printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
115                 }
116 
117                 start->region_start += base_addr;
118                 start->region_end += base_addr;
119         }
120 }
121 
122 static int cmp_unwind_table_entry(const void *a, const void *b)
123 {
124         return ((const struct unwind_table_entry *)a)->region_start
125              - ((const struct unwind_table_entry *)b)->region_start;
126 }
127 
128 static void
129 unwind_table_sort(struct unwind_table_entry *start,
130                   struct unwind_table_entry *finish)
131 {
132         sort(start, finish - start, sizeof(struct unwind_table_entry),
133              cmp_unwind_table_entry, NULL);
134 }
135 
136 struct unwind_table *
137 unwind_table_add(const char *name, unsigned long base_addr, 
138                  unsigned long gp,
139                  void *start, void *end)
140 {
141         struct unwind_table *table;
142         unsigned long flags;
143         struct unwind_table_entry *s = (struct unwind_table_entry *)start;
144         struct unwind_table_entry *e = (struct unwind_table_entry *)end;
145 
146         unwind_table_sort(s, e);
147 
148         table = kmalloc(sizeof(struct unwind_table), GFP_USER);
149         if (table == NULL)
150                 return NULL;
151         unwind_table_init(table, name, base_addr, gp, start, end);
152         spin_lock_irqsave(&unwind_lock, flags);
153         list_add_tail(&table->list, &unwind_tables);
154         spin_unlock_irqrestore(&unwind_lock, flags);
155 
156         return table;
157 }
158 
159 void unwind_table_remove(struct unwind_table *table)
160 {
161         unsigned long flags;
162 
163         spin_lock_irqsave(&unwind_lock, flags);
164         list_del(&table->list);
165         spin_unlock_irqrestore(&unwind_lock, flags);
166 
167         kfree(table);
168 }
169 
170 /* Called from setup_arch to import the kernel unwind info */
171 int __init unwind_init(void)
172 {
173         long start, stop;
174         register unsigned long gp __asm__ ("r27");
175 
176         start = (long)&__start___unwind[0];
177         stop = (long)&__stop___unwind[0];
178 
179         spin_lock_init(&unwind_lock);
180 
181         printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", 
182             start, stop,
183             (stop - start) / sizeof(struct unwind_table_entry));
184 
185         unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
186                           gp, 
187                           &__start___unwind[0], &__stop___unwind[0]);
188 #if 0
189         {
190                 int i;
191                 for (i = 0; i < 10; i++)
192                 {
193                         printk("region 0x%x-0x%x\n", 
194                                 __start___unwind[i].region_start, 
195                                 __start___unwind[i].region_end);
196                 }
197         }
198 #endif
199         return 0;
200 }
201 
202 #ifdef CONFIG_64BIT
203 #define get_func_addr(fptr) fptr[2]
204 #else
205 #define get_func_addr(fptr) fptr[0]
206 #endif
207 
208 static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
209 {
210         extern void handle_interruption(int, struct pt_regs *);
211         static unsigned long *hi = (unsigned long *)&handle_interruption;
212 
213         if (pc == get_func_addr(hi)) {
214                 struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
215                 dbg("Unwinding through handle_interruption()\n");
216                 info->prev_sp = regs->gr[30];
217                 info->prev_ip = regs->iaoq[0];
218 
219                 return 1;
220         }
221 
222         return 0;
223 }
224 
225 static void unwind_frame_regs(struct unwind_frame_info *info)
226 {
227         const struct unwind_table_entry *e;
228         unsigned long npc;
229         unsigned int insn;
230         long frame_size = 0;
231         int looking_for_rp, rpoffset = 0;
232 
233         e = find_unwind_entry(info->ip);
234         if (e == NULL) {
235                 unsigned long sp;
236 
237                 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
238 
239 #ifdef CONFIG_KALLSYMS
240                 /* Handle some frequent special cases.... */
241                 {
242                         char symname[KSYM_NAME_LEN];
243                         char *modname;
244 
245                         kallsyms_lookup(info->ip, NULL, NULL, &modname,
246                                 symname);
247 
248                         dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
249 
250                         if (strcmp(symname, "_switch_to_ret") == 0) {
251                                 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
252                                 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
253                                 dbg("_switch_to_ret @ %lx - setting "
254                                     "prev_sp=%lx prev_ip=%lx\n", 
255                                     info->ip, info->prev_sp, 
256                                     info->prev_ip);
257                                 return;
258                         } else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
259                                    strcmp(symname, "syscall_exit") == 0) {
260                                 info->prev_ip = info->prev_sp = 0;
261                                 return;
262                         }
263                 }
264 #endif
265 
266                 /* Since we are doing the unwinding blind, we don't know if
267                    we are adjusting the stack correctly or extracting the rp
268                    correctly. The rp is checked to see if it belongs to the
269                    kernel text section, if not we assume we don't have a 
270                    correct stack frame and we continue to unwind the stack.
271                    This is not quite correct, and will fail for loadable
272                    modules. */
273                 sp = info->sp & ~63;
274                 do {
275                         unsigned long tmp;
276 
277                         info->prev_sp = sp - 64;
278                         info->prev_ip = 0;
279                         if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
280                                 break;
281                         info->prev_ip = tmp;
282                         sp = info->prev_sp;
283                 } while (!kernel_text_address(info->prev_ip));
284 
285                 info->rp = 0;
286 
287                 dbg("analyzing func @ %lx with no unwind info, setting "
288                     "prev_sp=%lx prev_ip=%lx\n", info->ip, 
289                     info->prev_sp, info->prev_ip);
290         } else {
291                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
292                     "Save_RP = %d, Millicode = %d size = %u\n", 
293                     e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
294                     e->Millicode, e->Total_frame_size);
295 
296                 looking_for_rp = e->Save_RP;
297 
298                 for (npc = e->region_start; 
299                      (frame_size < (e->Total_frame_size << 3) || 
300                       looking_for_rp) && 
301                      npc < info->ip; 
302                      npc += 4) {
303 
304                         insn = *(unsigned int *)npc;
305 
306                         if ((insn & 0xffffc000) == 0x37de0000 ||
307                             (insn & 0xffe00000) == 0x6fc00000) {
308                                 /* ldo X(sp), sp, or stwm X,D(sp) */
309                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
310                                         ((insn & 0x3fff) >> 1);
311                                 dbg("analyzing func @ %lx, insn=%08x @ "
312                                     "%lx, frame_size = %ld\n", info->ip,
313                                     insn, npc, frame_size);
314                         } else if ((insn & 0xffe00008) == 0x73c00008) {
315                                 /* std,ma X,D(sp) */
316                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
317                                         (((insn >> 4) & 0x3ff) << 3);
318                                 dbg("analyzing func @ %lx, insn=%08x @ "
319                                     "%lx, frame_size = %ld\n", info->ip,
320                                     insn, npc, frame_size);
321                         } else if (insn == 0x6bc23fd9) { 
322                                 /* stw rp,-20(sp) */
323                                 rpoffset = 20;
324                                 looking_for_rp = 0;
325                                 dbg("analyzing func @ %lx, insn=stw rp,"
326                                     "-20(sp) @ %lx\n", info->ip, npc);
327                         } else if (insn == 0x0fc212c1) {
328                                 /* std rp,-16(sr0,sp) */
329                                 rpoffset = 16;
330                                 looking_for_rp = 0;
331                                 dbg("analyzing func @ %lx, insn=std rp,"
332                                     "-16(sp) @ %lx\n", info->ip, npc);
333                         }
334                 }
335 
336                 if (!unwind_special(info, e->region_start, frame_size)) {
337                         info->prev_sp = info->sp - frame_size;
338                         if (e->Millicode)
339                                 info->rp = info->r31;
340                         else if (rpoffset)
341                                 info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
342                         info->prev_ip = info->rp;
343                         info->rp = 0;
344                 }
345 
346                 dbg("analyzing func @ %lx, setting prev_sp=%lx "
347                     "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
348                     info->prev_ip, npc);
349         }
350 }
351 
352 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
353                        struct pt_regs *regs)
354 {
355         memset(info, 0, sizeof(struct unwind_frame_info));
356         info->t = t;
357         info->sp = regs->gr[30];
358         info->ip = regs->iaoq[0];
359         info->rp = regs->gr[2];
360         info->r31 = regs->gr[31];
361 
362         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
363             t ? (int)t->pid : -1, info->sp, info->ip);
364 }
365 
366 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
367 {
368         struct pt_regs *r = &t->thread.regs;
369         struct pt_regs *r2;
370 
371         r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
372         if (!r2)
373                 return;
374         *r2 = *r;
375         r2->gr[30] = r->ksp;
376         r2->iaoq[0] = r->kpc;
377         unwind_frame_init(info, t, r2);
378         kfree(r2);
379 }
380 
381 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
382 {
383         unwind_frame_init(info, current, regs);
384 }
385 
386 int unwind_once(struct unwind_frame_info *next_frame)
387 {
388         unwind_frame_regs(next_frame);
389 
390         if (next_frame->prev_sp == 0 ||
391             next_frame->prev_ip == 0)
392                 return -1;
393 
394         next_frame->sp = next_frame->prev_sp;
395         next_frame->ip = next_frame->prev_ip;
396         next_frame->prev_sp = 0;
397         next_frame->prev_ip = 0;
398 
399         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
400             next_frame->t ? (int)next_frame->t->pid : -1, 
401             next_frame->sp, next_frame->ip);
402 
403         return 0;
404 }
405 
406 int unwind_to_user(struct unwind_frame_info *info)
407 {
408         int ret;
409         
410         do {
411                 ret = unwind_once(info);
412         } while (!ret && !(info->ip & 3));
413 
414         return ret;
415 }
416 
417 unsigned long return_address(unsigned int level)
418 {
419         struct unwind_frame_info info;
420         struct pt_regs r;
421         unsigned long sp;
422 
423         /* initialize unwind info */
424         asm volatile ("copy %%r30, %0" : "=r"(sp));
425         memset(&r, 0, sizeof(struct pt_regs));
426         r.iaoq[0] = (unsigned long) current_text_addr();
427         r.gr[2] = (unsigned long) __builtin_return_address(0);
428         r.gr[30] = sp;
429         unwind_frame_init(&info, current, &r);
430 
431         /* unwind stack */
432         ++level;
433         do {
434                 if (unwind_once(&info) < 0 || info.ip == 0)
435                         return 0;
436                 if (!kernel_text_address(info.ip))
437                         return 0;
438         } while (info.ip && level--);
439 
440         return info.ip;
441 }
442 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp