]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: LoongArch: Record correct frame base address
authorHui Li <lihui@loongson.cn>
Wed, 17 Sep 2025 08:39:06 +0000 (16:39 +0800)
committerTiezhu Yang <yangtiezhu@loongson.cn>
Thu, 18 Sep 2025 15:20:12 +0000 (23:20 +0800)
(1) Description of Problem:

The frame_base structure is defined in gdb/frame-base.h, a typical
implementation of frame_base is return the same value for frame base,
locals base and args base. When debugging following code on LoongArch,
the outputs of locals base and args base are not equal to frame base
address in frame base register. The frame base register is sp(r3) or
fp(r22) on LoongArch. This problem only occurs when frame base register
is sp, there is no problem when fp is used as frame base register. When
using gcc option -fomit-frame-pointer or writing asm code without using
fp, the frame base register is sp.

$ cat test.c
int main()
{
  unsigned long a= 1, b = 1;
  a = 2;
  b = 2;
  return 0;
}
$ gcc -g -fomit-frame-pointer test.c -o test
$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:4
4   unsigned long a= 1, b = 1;
(gdb) disas
Dump of assembler code for function main:
   0x0000555555554740 <+0>: addi.d       $sp, $sp, -16
=> 0x0000555555554744 <+4>: li.w         $t0, 1
   0x0000555555554748 <+8>: st.d         $t0, $sp, 8
   0x000055555555474c <+12>: li.w         $t0, 1
   0x0000555555554750 <+16>: stptr.d      $t0, $sp, 0
   0x0000555555554754 <+20>: li.w         $t0, 2
   0x0000555555554758 <+24>: st.d         $t0, $sp, 8
   0x000055555555475c <+28>: li.w         $t0, 2
   0x0000555555554760 <+32>: stptr.d      $t0, $sp, 0
   0x0000555555554764 <+36>: move         $t0, $zero
   0x0000555555554768 <+40>: move         $a0, $t0
   0x000055555555476c <+44>: addi.d       $sp, $sp, 16
   0x0000555555554770 <+48>: ret
End of assembler dump.
(gdb) p $sp
$1 = (void *) 0x7ffffffeb130
(gdb) info frame
Stack level 0, frame at 0x7ffffffeb140:
 pc = 0x555555554744 in main (test.c:4); saved pc = 0x7ffff7e3d874
 source language c.
 Arglist at 0x7ffffffeb140, args:
 Locals at 0x7ffffffeb140, Previous frame's sp is 0x7ffffffeb140

(2) Root Cause Analysis:

When we use the info frame command, the addresses of previous frame's sp,
arglist and locals will be printed, the arglist and locals addresses are
calculated by member function of frame_base. Because LoongArch does not
implement its own frame_base, a default_frame_base will be used, it will
return stack address of the frame ID for frame base, locals base and args
base. However, on LoongArch or other architectures, the frame base address
does not always equal the stack address of the frame ID.

(3) Solution:

Implement loongarch_frame_base structure and loongarch_frame_base_address()
to record the correct frame base address stored in sp or fp register. It can
be calculated by subtracting the framebase_offset from the prev_sp recorded
in loongarch_frame_cache. And locals base and args base here are equal to
frame base.

(4) Test:

(gdb) p $sp
$1 = (void *) 0x7ffffffeb130
(gdb) info frame
Stack level 0, frame at 0x7ffffffeb140:
 pc = 0x555555554744 in main (test.c:4); saved pc = 0x7ffff7e3d874
 source language c.
 Arglist at 0x7ffffffeb130, args:
 Locals at 0x7ffffffeb130, Previous frame's sp is 0x7ffffffeb140

This modification only change the output of the info frame command when sp
is used as frame base register, and has no affect other functions of gdb on
LoongArch.

Signed-off-by: Hui Li <lihui@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
gdb/loongarch-tdep.c

index f88fb5f0b09ef9439e039560041c5693f4f83815..e94b88bc010aebd408f1d6670fbb1e99fcf95895 100644 (file)
@@ -22,6 +22,7 @@
 #include "dwarf2/frame.h"
 #include "elf-bfd.h"
 #include "extract-store-integer.h"
+#include "frame-base.h"
 #include "frame-unwind.h"
 #include "gdbcore.h"
 #include "linux-record.h"
@@ -787,6 +788,26 @@ static const struct frame_unwind_legacy loongarch_frame_unwind (
   /*.prev_arch    =*/nullptr
 );
 
+/* Return the frame base address of *THIS_FRAME.  */
+
+static CORE_ADDR
+loongarch_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
+{
+  struct loongarch_frame_cache *cache
+    = loongarch_frame_cache (this_frame, this_cache);
+
+  return cache->prev_sp - cache->framebase_offset;
+}
+
+/* LoongArch default frame base information.  */
+static frame_base loongarch_frame_base =
+{
+  &loongarch_frame_unwind,
+  loongarch_frame_base_address,
+  loongarch_frame_base_address,
+  loongarch_frame_base_address
+};
+
 /* Write the contents of buffer VAL into the general-purpose argument
    register defined by GAR in REGCACHE.  GAR indicates the available
    general-purpose argument registers which should be a value in the
@@ -2207,6 +2228,8 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   dwarf2_append_unwinders (gdbarch);
   frame_unwind_append_unwinder (gdbarch, &loongarch_frame_unwind);
 
+  frame_base_set_default (gdbarch, &loongarch_frame_base);
+
   /* Hook in OS ABI-specific overrides, if they have been registered.  */
   gdbarch_init_osabi (info, gdbarch);
   set_gdbarch_register_reggroup_p (gdbarch, loongarch_register_reggroup_p);