[prev in list] [next in list] [prev in thread] [next in thread] 

List:       netbsd-port-arm
Subject:    [PATCH] - gdb crashes in call command
From:       Gopikrishnan S <gokrix () yahoo ! com>
Date:       2019-09-26 5:44:20
Message-ID: 3b0f1c78-2383-d333-54f7-abf2baa3b972 () yahoo ! com
[Download RAW message or body]

Hello,

I have NetBSD 8.99.51 running on a Raspberry Pi 3B. When invoking a 
function in the inferior from the gdb command line with the call command 
gdb crashes:

Reading symbols from test...
(gdb) b main
Breakpoint 1 at 0x106f0: file test.c, line 13.
(gdb) r
Starting program: /home/gopikris/C/test

Breakpoint 1, main () at test.c:13
13              printf("Hello, World!\n");
(gdb) li 0
1       #include <stdlib.h>
2       #include <stdio.h>
3
4       static int m = 10;
5
6       int func(int p)
7       {
8               return m + p;
9       }
10
(gdb)
11      int main(void)
12      {
13              printf("Hello, World!\n");
14              return EXIT_SUCCESS;
15      }
(gdb) call func(1)
[ 547105.5763672] sorry, pid 10467 was killed: orphaned traced process
Segmentation fault (core dumped)

We never get a proper stack-trace for this crash, since it is caused by 
a stack corruption as described below.

gdb is crashing in the function regcache::restore() in regcache.c, on 
the 19th iteration of the following loop:

  /* Copy over any registers, being careful to only restore those that
      were both saved and need to be restored.  The full [0 .. 
gdbarch_num_regs
      + gdbarch_num_pseudo_regs) range is checked since some 
architectures need
      to save/restore `cooked' registers that live in memory.  */
   for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
     {
       if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
     {
       if (src->m_register_status[regnum] == REG_VALID)
         cooked_write (regnum, src->register_buffer (regnum));
     }
     }
The loop executes from regnum == 0 to regnum == 90, since 
m_descr->nr_cooked_registers is 91, by virtue of the constant 
ARM_NUM_REGS in the enum gdb_regnum in arch/arm.h being 91.

cooked_write() ultimately calls arm_nbsd_nat_target::store_registers() 
with regno == 19, which in turn passes this regno to store_register().

In store_reg() the default case (cases other than special registers like 
cpsr, sp, lr, and pc) is to update the regno-th member of the array 
regs.r. But for arm, regs.r is an array of length 13 and so when regno 
is greater than 12 this overwrites various parts of the stack. In 
particular, it overwrites the area of the stack where the r11 register 
is stored at function entry, and thus at function exit gdb goes off into 
the weeds with a wrong pc.

The solution in the patch pasted below is for 
arm_nbsd_nat_target::store_registers() to call store_register() only 
when we know that regnum corresponds to a register defined in struct 
reg, i.e., only when the regnum corresponds to ARM_A1_REGNUM (reg.r[0]) 
to ARM_PC_REGNUM(reg.r_pc) and for ARM_PS_REGNUM (reg.r_cpsr).

A corresponding change has been made in fetch_registers as well.

This change applies only to the NetBSD version of gdb. The upstream code 
is subtly different and thus does not have this bug (but may have other 
bugs).

-------------Begin patch-----------------

diff --git a/usr/src/external/gpl3/gdb/dist/gdb/arm-nbsd-nat.c 
b/usr/src/external/gpl3/gdb/dist/gdb/arm-nbsd-nat.c
index e4b6119a16..d3040c2cd2 100644
--- a/usr/src/external/gpl3/gdb/dist/gdb/arm-nbsd-nat.c
+++ b/usr/src/external/gpl3/gdb/dist/gdb/arm-nbsd-nat.c
@@ -47,6 +47,7 @@ public:
    void store_registers (struct regcache *, int) override;
  };

+static int arm_nbsd_nat_debug;
  static arm_nbsd_nat_target the_arm_nbsd_nat_target;

  extern int arm_apcs_32;
@@ -257,9 +258,13 @@ arm_nbsd_nat_target::fetch_registers (struct 
regcache *regcache, int regno)
    if (regno >= 0)
      {
        if (regno >= ARM_D0_REGNUM && regno <= ARM_FPSCR_REGNUM)
-    fetch_fp_register (regcache, regno);
+        fetch_fp_register (regcache, regno);
+      else if ((regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) ||
+              (regno == ARM_PS_REGNUM))
+        fetch_register (regcache, regno);
        else
-    fetch_register (regcache, regno);
+        if (arm_nbsd_nat_debug)
+          warning (_("Cannot access register %d on NetBSD."), regno);
      }
    else
      {
@@ -443,9 +448,13 @@ arm_nbsd_nat_target::store_registers (struct 
regcache *regcache, int regno)
    if (regno >= 0)
      {
        if (regno >= ARM_D0_REGNUM && regno <= ARM_FPSCR_REGNUM)
-    store_fp_register (regcache, regno);
+        store_fp_register (regcache, regno);
+      else if ((regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) ||
+              (regno == ARM_PS_REGNUM))
+        store_register (regcache, regno);
        else
-    store_register (regcache, regno);
+        if (arm_nbsd_nat_debug)
+            warning (_("Cannot access register %d on NetBSD."), regno);
      }
    else
      {
---------------End patch-------------------


Thanks,
--Gopikrishnan Sidhardhan


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic