diff options
author | Hui Li <lihui@loongson.cn> | 2025-08-26 09:45:57 +0800 |
---|---|---|
committer | Tiezhu Yang <yangtiezhu@loongson.cn> | 2025-08-26 21:58:36 +0800 |
commit | aa2b674af942a4d9defa1a1d53707684dd393095 (patch) | |
tree | 43f36c9bf0642f987f1542090a37269dcdc3c05e | |
parent | ab31109c8a9a5543c3448b98c640eb56fccc6d07 (diff) | |
download | binutils-aa2b674af942a4d9defa1a1d53707684dd393095.zip binutils-aa2b674af942a4d9defa1a1d53707684dd393095.tar.gz binutils-aa2b674af942a4d9defa1a1d53707684dd393095.tar.bz2 |
gdb: LoongArch: Improve loongarch_scan_prologue for correct backtrace
(1) Description of Problem:
When debugging the following code, the execution result of
the backtrace command is incorrect.
$ cat test.S
.text
.globl fun1
.type fun1, @function
fun1:
or $r12,$r0,$r0
or $r4,$r12,$r0
jr $r1
.globl fun
.type fun, @function
fun:
addi.d $r3,$r3,-16
st.d $r1,$r3,8
bl fun1
or $r12,$r4,$r0
or $r4,$r12,$r0
ld.d $r1,$r3,8
addi.d $r3,$r3,16
jr $r1
.globl main
.type main, @function
main:
addi.d $r3,$r3,-16
st.d $r1,$r3,8
bl fun
nop
ld.d $r1,$r3,8
addi.d $r3,$r3,16
jr $r1
$ gcc test.S -o test
$ gdb test
...
(gdb) b fun1
Breakpoint 1 at 0x748
(gdb) r
Breakpoint 1, 0x0000555555554748 in fun1 ()
(gdb) bt
#0 0x0000555555554748 in fun1 ()
#1 0x0000555555554758 in fun ()
#2 0x0000555555554758 in fun ()
#3 0x0000555555554758 in fun ()
....
--Type <RET> for more, q to quit, c to continue without paging
(2) Root Cause Analysis:
The return address of fun() in r1(ra) is saved on the stack:
addi.d $r3,$r3,-16
st.d $r1,$r3,8
The bl instruction in fun () will call the fun1 () and save
the value of pc+4 to r1(ra).
bl fun1
or $r12,$r4,$r0
Because registers such as fp and ra saved in the stack of the sub-function
are not recorded in current code. When trace back fun() to main(), the pc
of the previous frame to be read from ra register instead of the saved location
on the stack. At this time, the value of ra register in fun() is already the
address of the next instruction after the bl. So it is impossible to trace
back to the main().
(3) Solution:
Record the location of ra, fp, s0 to s8 on the stack to ensure the correct
execution of backtrace.
(4) Test:
$ gdb test
...
(gdb) b fun1
Breakpoint 1 at 0x748
(gdb) r
Breakpoint 1, 0x0000555555554748 in fun1 ()
(gdb) bt
#0 0x0000555555554748 in fun1 ()
#1 0x0000555555554758 in fun ()
#2 0x0000555555554778 in main ()
Signed-off-by: Hui Li <lihui@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
-rw-r--r-- | gdb/loongarch-tdep.c | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index 27b815f..9ee0eb3 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -158,6 +158,7 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, CORE_ADDR cur_pc = start_pc, prologue_end = 0; int32_t sp = LOONGARCH_SP_REGNUM; int32_t fp = LOONGARCH_FP_REGNUM; + int32_t ra = LOONGARCH_RA_REGNUM; int32_t reg_value[32] = {0}; int32_t reg_used[32] = {1, 0}; int i; @@ -168,6 +169,8 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, for (i = 0; i < LOONGARCH_USED_NUM_GREGSET; i++) regs[i] = pv_register (i, 0); + pv_area stack (LOONGARCH_SP_REGNUM, gdbarch_addr_bit (gdbarch)); + while (cur_pc < limit_pc) { insn_t insn = loongarch_fetch_instruction (cur_pc); @@ -176,6 +179,7 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, int32_t rj = loongarch_decode_imm ("5:5", insn, 0); int32_t rk = loongarch_decode_imm ("10:5", insn, 0); int32_t si12 = loongarch_decode_imm ("10:12", insn, 1); + int32_t si14 = loongarch_decode_imm ("10:14", insn, 1); int32_t si20 = loongarch_decode_imm ("5:20", insn, 1); if ((insn & 0xffc00000) == 0x02c00000 /* addi.d sp,sp,si12 */ @@ -194,11 +198,21 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, && rj == sp) { prologue_end = cur_pc + insn_len; + /* ra, fp, s0~s8 are saved by callee with sp as a base */ + if ((rd >= fp && rd <= fp + 9) || rd == ra) + { + stack.store (pv_add_constant (regs[sp], si12), register_size (gdbarch, rd), regs[rd]); + } } else if ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,sp,si14 */ && rj == sp) { prologue_end = cur_pc + insn_len; + /* ra, fp, s0~s8 are saved by callee with sp as a base */ + if ((rd >= fp && rd <= fp + 9) || rd == ra) + { + stack.store (pv_add_constant (regs[sp], si14), register_size (gdbarch, rd), regs[rd]); + } } else if ((insn & 0xfe000000) == 0x14000000) /* lu12i.w rd,si20 */ { @@ -255,6 +269,16 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, this_cache->framebase_offset = 0; } + for (i = 0; i < LOONGARCH_USED_NUM_GREGSET; i++) + { + CORE_ADDR offset; + + if (stack.find_reg (gdbarch, i, &offset)) + { + this_cache->saved_regs[i].set_addr (offset); + } + } + return prologue_end; } @@ -554,6 +578,7 @@ loongarch_frame_cache_1 (const frame_info_ptr &this_frame, struct loongarch_frame_cache *cache) { CORE_ADDR unwound_fp; + int reg; loongarch_analyze_prologue (this_frame, cache); @@ -567,6 +592,13 @@ loongarch_frame_cache_1 (const frame_info_ptr &this_frame, cache->prev_sp = unwound_fp; cache->prev_sp += cache->framebase_offset; + /* Calculate actual addresses of saved registers using offsets + determined by loongarch_scan_prologue. */ + for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (this_frame)); reg++) + if (cache->saved_regs[reg].is_addr ()) + cache->saved_regs[reg].set_addr (cache->saved_regs[reg].addr () + + cache->prev_sp); + cache->func = get_frame_func (this_frame); cache->available_p = 1; |