1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2019-03-29 Jesven the first version
9 */
10
11 #ifndef __ICCARM__
12 #ifndef __CHECKER__
13 #if !defined (__ARM_EABI__)
14 #warning Your compiler does not have EABI support.
15 #warning ARM unwind is known to compile only with EABI compilers.
16 #warning Change compiler or disable ARM_UNWIND option.
17 #elif (__GNUC__ == 4 && __GNUC_MINOR__ <= 2) && !defined(__clang__)
18 #warning Your compiler is too buggy; it is known to not compile ARM unwind support.
19 #warning Change compiler or disable ARM_UNWIND option.
20 #endif
21 #endif /* __CHECKER__ */
22
23 #include <rtthread.h>
24 #include <rthw.h>
25 #include <backtrace.h>
26
27 #define DBG_TAG "BACKTRACE"
28 #define DBG_LVL DBG_INFO
29 #include <rtdbg.h>
30
31 #ifdef RT_USING_SMART
32 #include <lwp.h>
33 #include <lwp_user_mm.h>
34 #include <lwp_arch.h>
35 #endif
36
arm_get_current_stackframe(struct pt_regs * regs,struct stackframe * frame)37 rt_inline void arm_get_current_stackframe(struct pt_regs *regs, struct stackframe *frame)
38 {
39 frame->fp = frame_pointer(regs);
40 frame->sp = regs->ARM_sp;
41 frame->lr = regs->ARM_lr;
42 frame->pc = regs->ARM_pc;
43 }
44
45 struct unwind_ctrl_block {
46 unsigned long vrs[16]; /* virtual register set */
47 const unsigned long *insn; /* pointer to the current instructions word */
48 unsigned long sp_high; /* highest value of sp allowed */
49 /*
50 * 1 : check for stack overflow for each register pop.
51 * 0 : save overhead if there is plenty of stack remaining.
52 */
53 int check_each_pop;
54 int entries; /* number of entries left to interpret */
55 int byte; /* current byte number in the instructions word */
56 };
57
58 enum regs
59 {
60 #ifdef CONFIG_THUMB2_KERNEL
61 FP = 7,
62 #else
63 FP = 11,
64 #endif
65 SP = 13,
66 LR = 14,
67 PC = 15
68 };
69
core_kernel_text(unsigned long addr)70 static int core_kernel_text(unsigned long addr)
71 {
72 return 1;
73 }
74
75 /* Convert a prel31 symbol to an absolute address */
76 #define prel31_to_addr(ptr) \
77 ({ \
78 /* sign-extend to 32 bits */ \
79 long offset = (((long)*(ptr)) << 1) >> 1; \
80 (unsigned long)(ptr) + offset; \
81 })
82
83 /*
84 * Binary search in the unwind index. The entries are
85 * guaranteed to be sorted in ascending order by the linker.
86 *
87 * start = first entry
88 * origin = first entry with positive offset (or stop if there is no such entry)
89 * stop - 1 = last entry
90 */
search_index(unsigned long addr,const struct unwind_idx * start,const struct unwind_idx * origin,const struct unwind_idx * stop)91 static const struct unwind_idx *search_index(unsigned long addr,
92 const struct unwind_idx *start,
93 const struct unwind_idx *origin,
94 const struct unwind_idx *stop)
95 {
96 unsigned long addr_prel31;
97
98 LOG_D("%s(%08lx, %x, %x, %x)",
99 __func__, addr, start, origin, stop);
100
101 /*
102 * only search in the section with the matching sign. This way the
103 * prel31 numbers can be compared as unsigned longs.
104 */
105 if (addr < (unsigned long)start)
106 /* negative offsets: [start; origin) */
107 stop = origin;
108 else
109 /* positive offsets: [origin; stop) */
110 start = origin;
111
112 /* prel31 for address relavive to start */
113 addr_prel31 = (addr - (unsigned long)start) & 0x7fffffff;
114
115 while (start < stop - 1)
116 {
117 const struct unwind_idx *mid = start + ((stop - start) >> 1);
118
119 /*
120 * As addr_prel31 is relative to start an offset is needed to
121 * make it relative to mid.
122 */
123 if (addr_prel31 - ((unsigned long)mid - (unsigned long)start) <
124 mid->addr_offset)
125 stop = mid;
126 else
127 {
128 /* keep addr_prel31 relative to start */
129 addr_prel31 -= ((unsigned long)mid -
130 (unsigned long)start);
131 start = mid;
132 }
133 }
134
135 if (start->addr_offset <= addr_prel31)
136 return start;
137 else
138 {
139 LOG_W("unwind: Unknown symbol address %08lx", addr);
140 return RT_NULL;
141 }
142 }
143
unwind_find_origin(const struct unwind_idx * start,const struct unwind_idx * stop)144 static const struct unwind_idx *unwind_find_origin(
145 const struct unwind_idx *start, const struct unwind_idx *stop)
146 {
147 LOG_D("%s(%x, %x)", __func__, start, stop);
148 while (start < stop)
149 {
150 const struct unwind_idx *mid = start + ((stop - start) >> 1);
151
152 if (mid->addr_offset >= 0x40000000)
153 /* negative offset */
154 start = mid + 1;
155 else
156 /* positive offset */
157 stop = mid;
158 }
159 LOG_D("%s -> %x", __func__, stop);
160 return stop;
161 }
162
unwind_find_idx(unsigned long addr,const struct unwind_idx ** origin_idx,const struct unwind_idx exidx_start[],const struct unwind_idx exidx_end[])163 static const struct unwind_idx *unwind_find_idx(unsigned long addr, const struct unwind_idx **origin_idx, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
164 {
165 const struct unwind_idx *idx = RT_NULL;
166
167 LOG_D("%s(%08lx)", __func__, addr);
168
169 if (core_kernel_text(addr))
170 {
171 if (!*origin_idx)
172 *origin_idx =
173 unwind_find_origin(exidx_start,
174 exidx_end);
175
176 /* main unwind table */
177 idx = search_index(addr, exidx_start,
178 *origin_idx,
179 exidx_end);
180 }
181
182 LOG_D("%s: idx = %x", __func__, idx);
183 return idx;
184 }
185
unwind_get_byte(struct unwind_ctrl_block * ctrl)186 static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl)
187 {
188 unsigned long ret;
189
190 if (ctrl->entries <= 0)
191 {
192 LOG_W("unwind: Corrupt unwind table");
193 return 0;
194 }
195
196 ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff;
197
198 if (ctrl->byte == 0)
199 {
200 ctrl->insn++;
201 ctrl->entries--;
202 ctrl->byte = 3;
203 }
204 else
205 ctrl->byte--;
206
207 return ret;
208 }
209
210 /* Before poping a register check whether it is feasible or not */
unwind_pop_register(struct unwind_ctrl_block * ctrl,unsigned long ** vsp,unsigned int reg)211 static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
212 unsigned long **vsp, unsigned int reg)
213 {
214 if (ctrl->check_each_pop)
215 if (*vsp >= (unsigned long *)ctrl->sp_high)
216 return -URC_FAILURE;
217
218 ctrl->vrs[reg] = *(*vsp)++;
219 return URC_OK;
220 }
221
222 /* Helper functions to execute the instructions */
unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block * ctrl,unsigned long mask)223 static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
224 unsigned long mask)
225 {
226 unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
227 int load_sp, reg = 4;
228
229 load_sp = mask & (1 << (13 - 4));
230 while (mask)
231 {
232 if (mask & 1)
233 if (unwind_pop_register(ctrl, &vsp, reg))
234 return -URC_FAILURE;
235 mask >>= 1;
236 reg++;
237 }
238 if (!load_sp)
239 ctrl->vrs[SP] = (unsigned long)vsp;
240
241 return URC_OK;
242 }
243
unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block * ctrl,unsigned long insn)244 static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl,
245 unsigned long insn)
246 {
247 unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
248 int reg;
249
250 /* pop R4-R[4+bbb] */
251 for (reg = 4; reg <= 4 + (insn & 7); reg++)
252 if (unwind_pop_register(ctrl, &vsp, reg))
253 return -URC_FAILURE;
254
255 if (insn & 0x8)
256 if (unwind_pop_register(ctrl, &vsp, 14))
257 return -URC_FAILURE;
258
259 ctrl->vrs[SP] = (unsigned long)vsp;
260
261 return URC_OK;
262 }
263
unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block * ctrl,unsigned long mask)264 static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl,
265 unsigned long mask)
266 {
267 unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
268 int reg = 0;
269
270 /* pop R0-R3 according to mask */
271 while (mask)
272 {
273 if (mask & 1)
274 if (unwind_pop_register(ctrl, &vsp, reg))
275 return -URC_FAILURE;
276 mask >>= 1;
277 reg++;
278 }
279 ctrl->vrs[SP] = (unsigned long)vsp;
280
281 return URC_OK;
282 }
283
284 /*
285 * Execute the current unwind instruction.
286 */
unwind_exec_insn(struct unwind_ctrl_block * ctrl)287 static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
288 {
289 unsigned long insn = unwind_get_byte(ctrl);
290 int ret = URC_OK;
291
292 LOG_D("%s: insn = %08lx", __func__, insn);
293
294 if ((insn & 0xc0) == 0x00)
295 ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
296 else if ((insn & 0xc0) == 0x40)
297 ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
298 else if ((insn & 0xf0) == 0x80)
299 {
300 unsigned long mask;
301
302 insn = (insn << 8) | unwind_get_byte(ctrl);
303 mask = insn & 0x0fff;
304 if (mask == 0)
305 {
306 LOG_W("unwind: 'Refuse to unwind' instruction %04lx",
307 insn);
308 return -URC_FAILURE;
309 }
310
311 ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask);
312 if (ret)
313 goto error;
314 }
315 else if ((insn & 0xf0) == 0x90 &&
316 (insn & 0x0d) != 0x0d)
317 ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
318 else if ((insn & 0xf0) == 0xa0)
319 {
320 ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
321 if (ret)
322 goto error;
323 }
324 else if (insn == 0xb0)
325 {
326 if (ctrl->vrs[PC] == 0)
327 ctrl->vrs[PC] = ctrl->vrs[LR];
328 /* no further processing */
329 ctrl->entries = 0;
330 }
331 else if (insn == 0xb1)
332 {
333 unsigned long mask = unwind_get_byte(ctrl);
334
335 if (mask == 0 || mask & 0xf0)
336 {
337 LOG_W("unwind: Spare encoding %04lx",
338 (insn << 8) | mask);
339 return -URC_FAILURE;
340 }
341
342 ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask);
343 if (ret)
344 goto error;
345 }
346 else if (insn == 0xb2)
347 {
348 unsigned long uleb128 = unwind_get_byte(ctrl);
349
350 ctrl->vrs[SP] += 0x204 + (uleb128 << 2);
351 }
352 else
353 {
354 LOG_W("unwind: Unhandled instruction %02lx", insn);
355 return -URC_FAILURE;
356 }
357
358 LOG_D("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx", __func__,
359 ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]);
360
361 error:
362 return ret;
363 }
364
365 #ifdef RT_BACKTRACE_FUNCTION_NAME
unwind_get_function_name(void * address)366 static char *unwind_get_function_name(void *address)
367 {
368 uint32_t flag_word = *(uint32_t *)((char*)address - 4);
369
370 if ((flag_word & 0xff000000) == 0xff000000)
371 {
372 return (char *)((char*)address - 4 - (flag_word & 0x00ffffff));
373 }
374 return RT_NULL;
375 }
376 #endif
377
378 /*
379 * Unwind a single frame starting with *sp for the symbol at *pc. It
380 * updates the *pc and *sp with the new values.
381 */
unwind_frame(struct stackframe * frame,const struct unwind_idx ** origin_idx,const struct unwind_idx exidx_start[],const struct unwind_idx exidx_end[])382 int unwind_frame(struct stackframe *frame, const struct unwind_idx **origin_idx, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
383 {
384 unsigned long low;
385 const struct unwind_idx *idx;
386 struct unwind_ctrl_block ctrl;
387 struct rt_thread *rt_c_thread;
388
389 /* store the highest address on the stack to avoid crossing it*/
390 low = frame->sp;
391 rt_c_thread = rt_thread_self();
392 ctrl.sp_high = (unsigned long)((char*)rt_c_thread->stack_addr + rt_c_thread->stack_size);
393
394 LOG_D("%s(pc = %08lx lr = %08lx sp = %08lx)", __func__,
395 frame->pc, frame->lr, frame->sp);
396
397 idx = unwind_find_idx(frame->pc, origin_idx, exidx_start, exidx_end);
398 if (!idx)
399 {
400 LOG_W("unwind: Index not found %08lx", frame->pc);
401 return -URC_FAILURE;
402 }
403
404 #ifdef RT_BACKTRACE_FUNCTION_NAME
405 {
406 char *fun_name;
407 fun_name = unwind_get_function_name((void *)prel31_to_addr(&idx->addr_offset));
408 if (fun_name)
409 {
410 rt_kprintf("0x%08x @ %s\n", frame->pc, fun_name);
411 }
412 }
413 #endif
414
415 ctrl.vrs[FP] = frame->fp;
416 ctrl.vrs[SP] = frame->sp;
417 ctrl.vrs[LR] = frame->lr;
418 ctrl.vrs[PC] = 0;
419
420 if (idx->insn == 1)
421 /* can't unwind */
422 return -URC_FAILURE;
423 else if ((idx->insn & 0x80000000) == 0)
424 /* prel31 to the unwind table */
425 ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn);
426 else if ((idx->insn & 0xff000000) == 0x80000000)
427 /* only personality routine 0 supported in the index */
428 ctrl.insn = &idx->insn;
429 else
430 {
431 LOG_W("unwind: Unsupported personality routine %08lx in the index at %x",
432 idx->insn, idx);
433 return -URC_FAILURE;
434 }
435
436 /* check the personality routine */
437 if ((*ctrl.insn & 0xff000000) == 0x80000000)
438 {
439 ctrl.byte = 2;
440 ctrl.entries = 1;
441 }
442 else if ((*ctrl.insn & 0xff000000) == 0x81000000)
443 {
444 ctrl.byte = 1;
445 ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16);
446 }
447 else
448 {
449 LOG_W("unwind: Unsupported personality routine %08lx at %x",
450 *ctrl.insn, ctrl.insn);
451 return -URC_FAILURE;
452 }
453
454 ctrl.check_each_pop = 0;
455
456 while (ctrl.entries > 0)
457 {
458 int urc;
459 if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
460 ctrl.check_each_pop = 1;
461 urc = unwind_exec_insn(&ctrl);
462 if (urc < 0)
463 return urc;
464 if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high)
465 return -URC_FAILURE;
466 }
467
468 if (ctrl.vrs[PC] == 0)
469 ctrl.vrs[PC] = ctrl.vrs[LR];
470
471 /* check for infinite loop */
472 if (frame->pc == ctrl.vrs[PC])
473 return -URC_FAILURE;
474
475 frame->fp = ctrl.vrs[FP];
476 frame->sp = ctrl.vrs[SP];
477 frame->lr = ctrl.vrs[LR];
478 frame->pc = ctrl.vrs[PC];
479
480 return URC_OK;
481 }
482
unwind_backtrace(struct pt_regs * regs,const struct unwind_idx exidx_start[],const struct unwind_idx exidx_end[])483 void unwind_backtrace(struct pt_regs *regs, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
484 {
485 struct stackframe frame;
486 const struct unwind_idx *origin_idx = RT_NULL;
487
488 LOG_D("%s(regs = %x)", __func__, regs);
489
490 arm_get_current_stackframe(regs, &frame);
491
492 #ifndef RT_BACKTRACE_FUNCTION_NAME
493 rt_kprintf("please use: addr2line -e rtthread.elf -a -f %08x\n", frame.pc);
494 #endif
495 LOG_D("pc = %08x, sp = %08x", frame.pc, frame.sp);
496
497 while (1)
498 {
499 int urc;
500
501 urc = unwind_frame(&frame, &origin_idx, exidx_start, exidx_end);
502 if (urc < 0)
503 break;
504 //dump_backtrace_entry(where, frame.pc, frame.sp - 4);
505 #ifndef RT_BACKTRACE_FUNCTION_NAME
506 rt_kprintf(" %08x", frame.pc);
507 #endif
508 LOG_D("from: pc = %08x, frame = %08x", frame.pc, frame.sp - 4);
509 }
510 rt_kprintf("\n");
511 }
512
513 extern const struct unwind_idx __exidx_start[];
514 extern const struct unwind_idx __exidx_end[];
515
rt_unwind(struct rt_hw_exp_stack * regs,unsigned int pc_adj)516 void rt_unwind(struct rt_hw_exp_stack *regs, unsigned int pc_adj)
517 {
518 struct pt_regs e_regs;
519
520 e_regs.ARM_fp = regs->fp;
521 e_regs.ARM_sp = regs->sp;
522 e_regs.ARM_lr = regs->lr;
523 e_regs.ARM_pc = regs->pc - pc_adj;
524 #ifdef RT_USING_SMART
525 if (!lwp_user_accessable((void *)e_regs.ARM_pc, sizeof (void *)))
526 {
527 e_regs.ARM_pc = regs->lr - sizeof(void *);
528 }
529 #endif
530 rt_kprintf("backtrace:\n");
531 unwind_backtrace(&e_regs, __exidx_start, __exidx_end);
532 }
533
rt_backtrace(void)534 rt_err_t rt_backtrace(void)
535 {
536 struct rt_hw_exp_stack regs;
537
538 __asm volatile ("mov %0, fp":"=r"(regs.fp));
539 __asm volatile ("mov %0, sp":"=r"(regs.sp));
540 __asm volatile ("mov %0, lr":"=r"(regs.lr));
541 __asm volatile ("mov %0, pc":"=r"(regs.pc));
542 rt_unwind(®s, 8);
543 return RT_EOK;
544 }
545 #endif // (__ICCARM__) undefined
546