aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHui Li <lihui@loongson.cn>2025-08-26 09:45:57 +0800
committerTiezhu Yang <yangtiezhu@loongson.cn>2025-08-26 21:58:36 +0800
commitaa2b674af942a4d9defa1a1d53707684dd393095 (patch)
tree43f36c9bf0642f987f1542090a37269dcdc3c05e
parentab31109c8a9a5543c3448b98c640eb56fccc6d07 (diff)
downloadbinutils-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.c32
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;