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

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

Version: ~ [ linux-5.4-rc7 ] ~ [ linux-5.3.11 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.84 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.154 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.201 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.201 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.77 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ 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 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                 extern char _stext[], _etext[];
237 
238                 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
239 
240 #ifdef CONFIG_KALLSYMS
241                 /* Handle some frequent special cases.... */
242                 {
243                         char symname[KSYM_NAME_LEN];
244                         char *modname;
245 
246                         kallsyms_lookup(info->ip, NULL, NULL, &modname,
247                                 symname);
248 
249                         dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
250 
251                         if (strcmp(symname, "_switch_to_ret") == 0) {
252                                 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
253                                 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
254                                 dbg("_switch_to_ret @ %lx - setting "
255                                     "prev_sp=%lx prev_ip=%lx\n", 
256                                     info->ip, info->prev_sp, 
257                                     info->prev_ip);
258                                 return;
259                         } else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
260                                    strcmp(symname, "syscall_exit") == 0) {
261                                 info->prev_ip = info->prev_sp = 0;
262                                 return;
263                         }
264                 }
265 #endif
266 
267                 /* Since we are doing the unwinding blind, we don't know if
268                    we are adjusting the stack correctly or extracting the rp
269                    correctly. The rp is checked to see if it belongs to the
270                    kernel text section, if not we assume we don't have a 
271                    correct stack frame and we continue to unwind the stack.
272                    This is not quite correct, and will fail for loadable
273                    modules. */
274                 sp = info->sp & ~63;
275                 do {
276                         unsigned long tmp;
277 
278                         info->prev_sp = sp - 64;
279                         info->prev_ip = 0;
280                         if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
281                                 break;
282                         info->prev_ip = tmp;
283                         sp = info->prev_sp;
284                 } while (info->prev_ip < (unsigned long)_stext ||
285                          info->prev_ip > (unsigned long)_etext);
286 
287                 info->rp = 0;
288 
289                 dbg("analyzing func @ %lx with no unwind info, setting "
290                     "prev_sp=%lx prev_ip=%lx\n", info->ip, 
291                     info->prev_sp, info->prev_ip);
292         } else {
293                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
294                     "Save_RP = %d, Millicode = %d size = %u\n", 
295                     e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
296                     e->Millicode, e->Total_frame_size);
297 
298                 looking_for_rp = e->Save_RP;
299 
300                 for (npc = e->region_start; 
301                      (frame_size < (e->Total_frame_size << 3) || 
302                       looking_for_rp) && 
303                      npc < info->ip; 
304                      npc += 4) {
305 
306                         insn = *(unsigned int *)npc;
307 
308                         if ((insn & 0xffffc000) == 0x37de0000 ||
309                             (insn & 0xffe00000) == 0x6fc00000) {
310                                 /* ldo X(sp), sp, or stwm X,D(sp) */
311                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
312                                         ((insn & 0x3fff) >> 1);
313                                 dbg("analyzing func @ %lx, insn=%08x @ "
314                                     "%lx, frame_size = %ld\n", info->ip,
315                                     insn, npc, frame_size);
316                         } else if ((insn & 0xffe00008) == 0x73c00008) {
317                                 /* std,ma X,D(sp) */
318                                 frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
319                                         (((insn >> 4) & 0x3ff) << 3);
320                                 dbg("analyzing func @ %lx, insn=%08x @ "
321                                     "%lx, frame_size = %ld\n", info->ip,
322                                     insn, npc, frame_size);
323                         } else if (insn == 0x6bc23fd9) { 
324                                 /* stw rp,-20(sp) */
325                                 rpoffset = 20;
326                                 looking_for_rp = 0;
327                                 dbg("analyzing func @ %lx, insn=stw rp,"
328                                     "-20(sp) @ %lx\n", info->ip, npc);
329                         } else if (insn == 0x0fc212c1) {
330                                 /* std rp,-16(sr0,sp) */
331                                 rpoffset = 16;
332                                 looking_for_rp = 0;
333                                 dbg("analyzing func @ %lx, insn=std rp,"
334                                     "-16(sp) @ %lx\n", info->ip, npc);
335                         }
336                 }
337 
338                 if (!unwind_special(info, e->region_start, frame_size)) {
339                         info->prev_sp = info->sp - frame_size;
340                         if (e->Millicode)
341                                 info->rp = info->r31;
342                         else if (rpoffset)
343                                 info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
344                         info->prev_ip = info->rp;
345                         info->rp = 0;
346                 }
347 
348                 dbg("analyzing func @ %lx, setting prev_sp=%lx "
349                     "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
350                     info->prev_ip, npc);
351         }
352 }
353 
354 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
355                        struct pt_regs *regs)
356 {
357         memset(info, 0, sizeof(struct unwind_frame_info));
358         info->t = t;
359         info->sp = regs->gr[30];
360         info->ip = regs->iaoq[0];
361         info->rp = regs->gr[2];
362         info->r31 = regs->gr[31];
363 
364         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
365             t ? (int)t->pid : -1, info->sp, info->ip);
366 }
367 
368 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
369 {
370         struct pt_regs *r = &t->thread.regs;
371         struct pt_regs *r2;
372 
373         r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
374         if (!r2)
375                 return;
376         *r2 = *r;
377         r2->gr[30] = r->ksp;
378         r2->iaoq[0] = r->kpc;
379         unwind_frame_init(info, t, r2);
380         kfree(r2);
381 }
382 
383 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
384 {
385         unwind_frame_init(info, current, regs);
386 }
387 
388 int unwind_once(struct unwind_frame_info *next_frame)
389 {
390         unwind_frame_regs(next_frame);
391 
392         if (next_frame->prev_sp == 0 ||
393             next_frame->prev_ip == 0)
394                 return -1;
395 
396         next_frame->sp = next_frame->prev_sp;
397         next_frame->ip = next_frame->prev_ip;
398         next_frame->prev_sp = 0;
399         next_frame->prev_ip = 0;
400 
401         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
402             next_frame->t ? (int)next_frame->t->pid : -1, 
403             next_frame->sp, next_frame->ip);
404 
405         return 0;
406 }
407 
408 int unwind_to_user(struct unwind_frame_info *info)
409 {
410         int ret;
411         
412         do {
413                 ret = unwind_once(info);
414         } while (!ret && !(info->ip & 3));
415 
416         return ret;
417 }
418 
419 unsigned long return_address(unsigned int level)
420 {
421         struct unwind_frame_info info;
422         struct pt_regs r;
423         unsigned long sp;
424 
425         /* initialize unwind info */
426         asm volatile ("copy %%r30, %0" : "=r"(sp));
427         memset(&r, 0, sizeof(struct pt_regs));
428         r.iaoq[0] = (unsigned long) current_text_addr();
429         r.gr[2] = (unsigned long) __builtin_return_address(0);
430         r.gr[30] = sp;
431         unwind_frame_init(&info, current, &r);
432 
433         /* unwind stack */
434         ++level;
435         do {
436                 if (unwind_once(&info) < 0 || info.ip == 0)
437                         return 0;
438                 if (!__kernel_text_address(info.ip)) {
439                         return 0;
440                 }
441         } while (info.ip && level--);
442 
443         return info.ip;
444 }
445 

~ [ 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