diff options
author | Hui Li <lihui@loongson.cn> | 2025-08-26 09:38:37 +0800 |
---|---|---|
committer | Tiezhu Yang <yangtiezhu@loongson.cn> | 2025-08-26 21:58:06 +0800 |
commit | ab31109c8a9a5543c3448b98c640eb56fccc6d07 (patch) | |
tree | 80bb2dc50aad2b37d00eb7c636be3ab82e09f85d | |
parent | d7a2fed3c29ad3ddba45d6b8bca3433d54b22c42 (diff) | |
download | binutils-ab31109c8a9a5543c3448b98c640eb56fccc6d07.zip binutils-ab31109c8a9a5543c3448b98c640eb56fccc6d07.tar.gz binutils-ab31109c8a9a5543c3448b98c640eb56fccc6d07.tar.bz2 |
gdb: LoongArch: Improve loongarch_scan_prologue to record stack information
(1) Description of Problem:
When debugging the following code, the execution result of
nexti command is incorrect.
$ cat test.S
.text
.globl fun
.type fun, @function
fun:
or $r12,$r0,$r0
or $r4,$r12,$r0
jr $r1
.globl main
.type main, @function
main:
addi.d $r3,$r3,-16
st.d $r1,$r3,8
bl fun
or $r12,$r4,$r0
or $r4,$r12,$r0
ld.d $r1,$r3,8
addi.d $r3,$r3,16
jr $r1
$ gcc test.S -o test
$ gdb test
...
(gdb) set disassemble-next-line on
(gdb) start
...
Temporary breakpoint 1, 0x0000555555554754 in main ()
=> 0x0000555555554754 <main+8>: 57ffefff bl -20 # 0x555555554740 <fun>
(gdb) ni
0x0000555555554740 in fun ()
=> 0x0000555555554740 <fun+0>: 0015000c move $t0, $zero
(2) Root Cause Analysis:
In the internal execution flow of the ni command, a single-step will be
executed first. After that, it will enter process_event_stop_test (),
some conditions are judged in this function.
if ((get_stack_frame_id (frame)
!= ecs->event_thread->control.step_stack_frame_id)
&& get_frame_type (frame) != SIGTRAMP_FRAME
&& ((frame_unwind_caller_id (frame)
== ecs->event_thread->control.step_stack_frame_id)
&& ((ecs->event_thread->control.step_stack_frame_id
!= outer_frame_id)
|| (ecs->event_thread->control.step_start_function
!= find_pc_function (ecs->event_thread->stop_pc ())))))
{
...
if (ecs->event_thread->control.step_over_calls == STEP_OVER_ALL)
...
else
insert_step_resume_breakpoint_at_caller (frame);
}
Here, it will be judged whether a sub-function has been called based on
whether the frame id before the single step is not equal to the current
frame id and whether there is a calling relationship.
If a sub-function is called at this time and the current operation is nexti,
it will not stop immediately. Instead, insert_step_resume_breakpoint_at_caller()
will be called to complete the execution of the sub-function and then stop.
In above debugging examples, the executable program being debugged is compiled
from an asm source file that does not contain dwarf information. Therefore, the
frame id of the function is calculated by loongarch_frame_unwind rather than
dwarf2_frame_unwind. However, loongarch_scan_prologue() has not yet recorded
stack information in loongarch_frame_cache, this will cause problems in some
operations related to the frame id information.
(3) Solution:
Improve loongarch_scan_prologue() to record the stack information in
loongarch_frame_cache. And improve the loongarch_frame_unwind_stop_reason()
through the information recorded in loongarch_frame_cache.
(4) Test:
After this patch:
$ gdb test
(gdb) set disassemble-next-line on
(gdb) start
Temporary breakpoint 1, 0x0000555555554754 in main ()
=> 0x0000555555554754 <main+8>: 57ffefff bl -20 # 0x555555554740 <fun>
(gdb) ni
0x0000555555554758 in main ()
=> 0x0000555555554758 <main+12>: 0015008c move $t0, $a0
(gdb) ni
0x000055555555475c in main ()
=> 0x000055555555475c <main+16>: 00150184 move $a0, $t0
Signed-off-by: Hui Li <lihui@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
-rw-r--r-- | gdb/loongarch-tdep.c | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index 5c4c5d9..27b815f 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -26,6 +26,7 @@ #include "gdbcore.h" #include "linux-record.h" #include "loongarch-tdep.h" +#include "prologue-value.h" #include "record.h" #include "record-full.h" #include "reggroups.h" @@ -159,6 +160,13 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, int32_t fp = LOONGARCH_FP_REGNUM; int32_t reg_value[32] = {0}; int32_t reg_used[32] = {1, 0}; + int i; + + /* Track 32 GPR, ORIG_A0, PC, BADV in prologue. */ + pv_t regs[LOONGARCH_USED_NUM_GREGSET]; + + for (i = 0; i < LOONGARCH_USED_NUM_GREGSET; i++) + regs[i] = pv_register (i, 0); while (cur_pc < limit_pc) { @@ -174,11 +182,13 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, && rd == sp && rj == sp && si12 < 0) { prologue_end = cur_pc + insn_len; + regs[rd] = pv_add_constant (regs[rj], si12); } else if ((insn & 0xffc00000) == 0x02c00000 /* addi.d fp,sp,si12 */ && rd == fp && rj == sp && si12 > 0) { prologue_end = cur_pc + insn_len; + regs[rd] = pv_add_constant (regs[rj], si12); } else if ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,sp,si12 */ && rj == sp) @@ -223,6 +233,28 @@ loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, if (prologue_end == 0) prologue_end = cur_pc; + if (this_cache == NULL) + return prologue_end; + + if (pv_is_register (regs[LOONGARCH_FP_REGNUM], LOONGARCH_SP_REGNUM)) + { + /* Frame pointer is fp. */ + this_cache->framebase_reg = LOONGARCH_FP_REGNUM; + this_cache->framebase_offset = -regs[LOONGARCH_FP_REGNUM].k; + } + else if (pv_is_register (regs[LOONGARCH_SP_REGNUM], LOONGARCH_SP_REGNUM)) + { + /* Try the stack pointer. */ + this_cache->framebase_reg = LOONGARCH_SP_REGNUM; + this_cache->framebase_offset = -regs[LOONGARCH_SP_REGNUM].k; + } + else + { + /* We're just out of luck. We don't know where the frame is. */ + this_cache->framebase_reg = -1; + this_cache->framebase_offset = 0; + } + return prologue_end; } @@ -573,6 +605,16 @@ static enum unwind_stop_reason loongarch_frame_unwind_stop_reason (const frame_info_ptr &this_frame, void **this_cache) { + struct loongarch_frame_cache *cache + = loongarch_frame_cache (this_frame, this_cache); + + if (!cache->available_p) + return UNWIND_UNAVAILABLE; + + /* We've hit a wall, stop. */ + if (cache->prev_sp == 0) + return UNWIND_OUTERMOST; + return UNWIND_NO_REASON; } |