2 * Kernel unwinding support
4 * (c) 2002-2004 Randolph Chung <tausq@debian.org>
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
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>
18 #include <asm/uaccess.h>
19 #include <asm/assembly.h>
20 #include <asm/asm-offsets.h>
21 #include <asm/ptrace.h>
23 #include <asm/unwind.h>
27 #define dbg(x...) printk(x)
32 #define KERNEL_START (KERNEL_BINARY_TEXT_START)
34 extern struct unwind_table_entry __start___unwind
[];
35 extern struct unwind_table_entry __stop___unwind
[];
37 static spinlock_t unwind_lock
;
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)
43 static struct unwind_table kernel_unwind_table __read_mostly
;
44 static LIST_HEAD(unwind_tables
);
46 static inline const struct unwind_table_entry
*
47 find_unwind_entry_in_table(const struct unwind_table
*table
, unsigned long addr
)
49 const struct unwind_table_entry
*e
= NULL
;
50 unsigned long lo
, hi
, mid
;
53 hi
= table
->length
- 1;
56 mid
= (hi
- lo
) / 2 + lo
;
57 e
= &table
->table
[mid
];
58 if (addr
< e
->region_start
)
60 else if (addr
> e
->region_end
)
69 static const struct unwind_table_entry
*
70 find_unwind_entry(unsigned long addr
)
72 struct unwind_table
*table
;
73 const struct unwind_table_entry
*e
= NULL
;
75 if (addr
>= kernel_unwind_table
.start
&&
76 addr
<= kernel_unwind_table
.end
)
77 e
= find_unwind_entry_in_table(&kernel_unwind_table
, addr
);
81 spin_lock_irqsave(&unwind_lock
, flags
);
82 list_for_each_entry(table
, &unwind_tables
, list
) {
83 if (addr
>= table
->start
&&
85 e
= find_unwind_entry_in_table(table
, addr
);
87 /* Move-to-front to exploit common traces */
88 list_move(&table
->list
, &unwind_tables
);
92 spin_unlock_irqrestore(&unwind_lock
, flags
);
99 unwind_table_init(struct unwind_table
*table
, const char *name
,
100 unsigned long base_addr
, unsigned long gp
,
101 void *table_start
, void *table_end
)
103 struct unwind_table_entry
*start
= table_start
;
104 struct unwind_table_entry
*end
=
105 (struct unwind_table_entry
*)table_end
- 1;
108 table
->base_addr
= base_addr
;
110 table
->start
= base_addr
+ start
->region_start
;
111 table
->end
= base_addr
+ end
->region_end
;
112 table
->table
= (struct unwind_table_entry
*)table_start
;
113 table
->length
= end
- start
+ 1;
114 INIT_LIST_HEAD(&table
->list
);
116 for (; start
<= end
; start
++) {
118 start
->region_end
> (start
+1)->region_start
) {
119 printk("WARNING: Out of order unwind entry! %p and %p\n", start
, start
+1);
122 start
->region_start
+= base_addr
;
123 start
->region_end
+= base_addr
;
127 static int cmp_unwind_table_entry(const void *a
, const void *b
)
129 return ((const struct unwind_table_entry
*)a
)->region_start
130 - ((const struct unwind_table_entry
*)b
)->region_start
;
134 unwind_table_sort(struct unwind_table_entry
*start
,
135 struct unwind_table_entry
*finish
)
137 sort(start
, finish
- start
, sizeof(struct unwind_table_entry
),
138 cmp_unwind_table_entry
, NULL
);
141 struct unwind_table
*
142 unwind_table_add(const char *name
, unsigned long base_addr
,
144 void *start
, void *end
)
146 struct unwind_table
*table
;
148 struct unwind_table_entry
*s
= (struct unwind_table_entry
*)start
;
149 struct unwind_table_entry
*e
= (struct unwind_table_entry
*)end
;
151 unwind_table_sort(s
, e
);
153 table
= kmalloc(sizeof(struct unwind_table
), GFP_USER
);
156 unwind_table_init(table
, name
, base_addr
, gp
, start
, end
);
157 spin_lock_irqsave(&unwind_lock
, flags
);
158 list_add_tail(&table
->list
, &unwind_tables
);
159 spin_unlock_irqrestore(&unwind_lock
, flags
);
164 void unwind_table_remove(struct unwind_table
*table
)
168 spin_lock_irqsave(&unwind_lock
, flags
);
169 list_del(&table
->list
);
170 spin_unlock_irqrestore(&unwind_lock
, flags
);
175 /* Called from setup_arch to import the kernel unwind info */
176 int __init
unwind_init(void)
179 register unsigned long gp
__asm__ ("r27");
181 start
= (long)&__start___unwind
[0];
182 stop
= (long)&__stop___unwind
[0];
184 spin_lock_init(&unwind_lock
);
186 printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
188 (stop
- start
) / sizeof(struct unwind_table_entry
));
190 unwind_table_init(&kernel_unwind_table
, "kernel", KERNEL_START
,
192 &__start___unwind
[0], &__stop___unwind
[0]);
196 for (i
= 0; i
< 10; i
++)
198 printk("region 0x%x-0x%x\n",
199 __start___unwind
[i
].region_start
,
200 __start___unwind
[i
].region_end
);
208 #define get_func_addr(fptr) fptr[2]
210 #define get_func_addr(fptr) fptr[0]
213 static int unwind_special(struct unwind_frame_info
*info
, unsigned long pc
, int frame_size
)
215 extern void handle_interruption(int, struct pt_regs
*);
216 static unsigned long *hi
= (unsigned long *)&handle_interruption
;
218 if (pc
== get_func_addr(hi
)) {
219 struct pt_regs
*regs
= (struct pt_regs
*)(info
->sp
- frame_size
- PT_SZ_ALGN
);
220 dbg("Unwinding through handle_interruption()\n");
221 info
->prev_sp
= regs
->gr
[30];
222 info
->prev_ip
= regs
->iaoq
[0];
230 static void unwind_frame_regs(struct unwind_frame_info
*info
)
232 const struct unwind_table_entry
*e
;
236 int looking_for_rp
, rpoffset
= 0;
238 e
= find_unwind_entry(info
->ip
);
242 dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info
->ip
);
244 #ifdef CONFIG_KALLSYMS
245 /* Handle some frequent special cases.... */
247 char symname
[KSYM_NAME_LEN
];
250 kallsyms_lookup(info
->ip
, NULL
, NULL
, &modname
,
253 dbg("info->ip = 0x%lx, name = %s\n", info
->ip
, symname
);
255 if (strcmp(symname
, "_switch_to_ret") == 0) {
256 info
->prev_sp
= info
->sp
- CALLEE_SAVE_FRAME_SIZE
;
257 info
->prev_ip
= *(unsigned long *)(info
->prev_sp
- RP_OFFSET
);
258 dbg("_switch_to_ret @ %lx - setting "
259 "prev_sp=%lx prev_ip=%lx\n",
260 info
->ip
, info
->prev_sp
,
263 } else if (strcmp(symname
, "ret_from_kernel_thread") == 0 ||
264 strcmp(symname
, "syscall_exit") == 0) {
265 info
->prev_ip
= info
->prev_sp
= 0;
271 /* Since we are doing the unwinding blind, we don't know if
272 we are adjusting the stack correctly or extracting the rp
273 correctly. The rp is checked to see if it belongs to the
274 kernel text section, if not we assume we don't have a
275 correct stack frame and we continue to unwind the stack.
276 This is not quite correct, and will fail for loadable
282 info
->prev_sp
= sp
- 64;
284 if (get_user(tmp
, (unsigned long *)(info
->prev_sp
- RP_OFFSET
)))
288 } while (!kernel_text_address(info
->prev_ip
));
292 dbg("analyzing func @ %lx with no unwind info, setting "
293 "prev_sp=%lx prev_ip=%lx\n", info
->ip
,
294 info
->prev_sp
, info
->prev_ip
);
296 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
297 "Save_RP = %d, Millicode = %d size = %u\n",
298 e
->region_start
, e
->region_end
, e
->Save_SP
, e
->Save_RP
,
299 e
->Millicode
, e
->Total_frame_size
);
301 looking_for_rp
= e
->Save_RP
;
303 for (npc
= e
->region_start
;
304 (frame_size
< (e
->Total_frame_size
<< 3) ||
309 insn
= *(unsigned int *)npc
;
311 if ((insn
& 0xffffc001) == 0x37de0000 ||
312 (insn
& 0xffe00001) == 0x6fc00000) {
313 /* ldo X(sp), sp, or stwm X,D(sp) */
314 frame_size
+= (insn
& 0x3fff) >> 1;
315 dbg("analyzing func @ %lx, insn=%08x @ "
316 "%lx, frame_size = %ld\n", info
->ip
,
317 insn
, npc
, frame_size
);
318 } else if ((insn
& 0xffe00009) == 0x73c00008) {
320 frame_size
+= ((insn
>> 4) & 0x3ff) << 3;
321 dbg("analyzing func @ %lx, insn=%08x @ "
322 "%lx, frame_size = %ld\n", info
->ip
,
323 insn
, npc
, frame_size
);
324 } else if (insn
== 0x6bc23fd9) {
328 dbg("analyzing func @ %lx, insn=stw rp,"
329 "-20(sp) @ %lx\n", info
->ip
, npc
);
330 } else if (insn
== 0x0fc212c1) {
331 /* std rp,-16(sr0,sp) */
334 dbg("analyzing func @ %lx, insn=std rp,"
335 "-16(sp) @ %lx\n", info
->ip
, npc
);
339 if (frame_size
> e
->Total_frame_size
<< 3)
340 frame_size
= e
->Total_frame_size
<< 3;
342 if (!unwind_special(info
, e
->region_start
, frame_size
)) {
343 info
->prev_sp
= info
->sp
- frame_size
;
345 info
->rp
= info
->r31
;
347 info
->rp
= *(unsigned long *)(info
->prev_sp
- rpoffset
);
348 info
->prev_ip
= info
->rp
;
352 dbg("analyzing func @ %lx, setting prev_sp=%lx "
353 "prev_ip=%lx npc=%lx\n", info
->ip
, info
->prev_sp
,
358 void unwind_frame_init(struct unwind_frame_info
*info
, struct task_struct
*t
,
359 struct pt_regs
*regs
)
361 memset(info
, 0, sizeof(struct unwind_frame_info
));
363 info
->sp
= regs
->gr
[30];
364 info
->ip
= regs
->iaoq
[0];
365 info
->rp
= regs
->gr
[2];
366 info
->r31
= regs
->gr
[31];
368 dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
369 t
? (int)t
->pid
: -1, info
->sp
, info
->ip
);
372 void unwind_frame_init_from_blocked_task(struct unwind_frame_info
*info
, struct task_struct
*t
)
374 struct pt_regs
*r
= &t
->thread
.regs
;
377 r2
= kmalloc(sizeof(struct pt_regs
), GFP_ATOMIC
);
382 r2
->iaoq
[0] = r
->kpc
;
383 unwind_frame_init(info
, t
, r2
);
387 void unwind_frame_init_running(struct unwind_frame_info
*info
, struct pt_regs
*regs
)
389 unwind_frame_init(info
, current
, regs
);
392 int unwind_once(struct unwind_frame_info
*next_frame
)
394 unwind_frame_regs(next_frame
);
396 if (next_frame
->prev_sp
== 0 ||
397 next_frame
->prev_ip
== 0)
400 next_frame
->sp
= next_frame
->prev_sp
;
401 next_frame
->ip
= next_frame
->prev_ip
;
402 next_frame
->prev_sp
= 0;
403 next_frame
->prev_ip
= 0;
405 dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n",
406 next_frame
->t
? (int)next_frame
->t
->pid
: -1,
407 next_frame
->sp
, next_frame
->ip
);
412 int unwind_to_user(struct unwind_frame_info
*info
)
417 ret
= unwind_once(info
);
418 } while (!ret
&& !(info
->ip
& 3));
423 unsigned long return_address(unsigned int level
)
425 struct unwind_frame_info info
;
429 /* initialize unwind info */
430 asm volatile ("copy %%r30, %0" : "=r"(sp
));
431 memset(&r
, 0, sizeof(struct pt_regs
));
432 r
.iaoq
[0] = (unsigned long) current_text_addr();
433 r
.gr
[2] = (unsigned long) __builtin_return_address(0);
435 unwind_frame_init(&info
, current
, &r
);
440 if (unwind_once(&info
) < 0 || info
.ip
== 0)
442 if (!kernel_text_address(info
.ip
))
444 } while (info
.ip
&& level
--);