(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>
#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"
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)
{
&& 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)
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;
}
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;
}