aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHui Li <lihui@loongson.cn>2025-08-26 09:38:37 +0800
committerTiezhu Yang <yangtiezhu@loongson.cn>2025-08-26 21:58:06 +0800
commitab31109c8a9a5543c3448b98c640eb56fccc6d07 (patch)
tree80bb2dc50aad2b37d00eb7c636be3ab82e09f85d
parentd7a2fed3c29ad3ddba45d6b8bca3433d54b22c42 (diff)
downloadbinutils-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.c42
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;
}