This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: GDB crash in Thumb prologue when executing register movs to SP


Hi All,

I recently encountered some behavior with the Thumb prologue analyzer that I
would like to get some feedback on.  In particular, GDB crashes when a hand
written assembly function attempts to write the SP in a function prologue.

(Working off of GDB from git HEAD.)

For example, consider (yes, I know this is non-nonsensical, but it illustrates
the problem with GDB):

  extern void write_sp(unsigned int v);

  int
  main (int argc, char **argv)
  {
    write_sp (0xdeadbeef);
    return 0;
  }

  asm(".text\n"
      "	.align 2\n"
      "	.thumb_func\n"
      "	.code 16\n"
      "write_sp:\n"
      "	mov	sp, r0\n"
      "	bx	lr\n");

when debugging this program the following happens:

  (gdb) disassemble
  Dump of assembler code for function write_sp:
  => 0x0000025c <+0>:	mov	sp, r0
     0x0000025e <+2>:	bx	lr
  End of assembler dump.
  (gdb) stepi
  ../../gdb/gdb/regcache.c:179: internal-error: register_size: Assertion
  `regnum >= 0 && regnum < (gdbarch_num_regs (gdbarch) +
  gdbarch_num_pseudo_regs (gdbarch))' failed.
  A problem internal to GDB has been detected, further debugging may prove
  unreliable.
  Quit this debugging session? (y or n) y
  Create a core file of GDB? (y or n) y
  Aborted (core dumped)


In 'arm-tdep.c:thumb_analyze_prologue' there is:

  else if ((insn & 0xff00) == 0x4600)	/* mov hi, lo or mov lo, hi */
    {
      int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4);
      int src_reg = (insn & 0x78) >> 3;
      regs[dst_reg] = regs[src_reg];
    }

which will set SP equal to R0 for the case above.  However, once
the prologue scan loop is exited the following is executed:

  if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
    {
      /* Frame pointer is fp.  Frame size is constant.  */
      cache->framereg = ARM_FP_REGNUM;
      cache->framesize = -regs[ARM_FP_REGNUM].k;
    }
  else if (pv_is_register (regs[THUMB_FP_REGNUM], ARM_SP_REGNUM))
    {
      /* Frame pointer is r7.  Frame size is constant.  */
      cache->framereg = THUMB_FP_REGNUM;
      cache->framesize = -regs[THUMB_FP_REGNUM].k;
    }
  else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
    {
      /* Try the stack pointer... this is a bit desperate.  */
      cache->framereg = ARM_SP_REGNUM;
      cache->framesize = -regs[ARM_SP_REGNUM].k;
    }
  else
    {
      /* We're just out of luck.  We don't know where the frame is.  */
      cache->framereg = -1;
      cache->framesize = 0;
    }

therefore the analysis is left with 'cache->framereg == -1'.

Now when 'cache->framereg' is used in 'arm-tdep.c:arm_make_prologue_cache':

  arm_scan_prologue (this_frame, cache);

  unwound_fp = get_frame_register_unsigned (this_frame, cache->framereg);

GDB crashes.

One way to fix the problem would be to exit the scanning loop when code writes
the SP:

@@ -766,6 +766,14 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
        {
          int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4);
          int src_reg = (insn & 0x78) >> 3;
+
+         /* If the SP is directly written to, then we can't be sure where the
+            stack has gone.  Therefore it is better to just bail out.  This
+            type of case happens in code that is switching stacks around
+            (e.g. OSes, threading libraries, etc...). */
+         if (dst_reg == ARM_SP_REGNUM)
+           break;
+
          regs[dst_reg] = regs[src_reg];
        }
       else if ((insn & 0xf800) == 0x9000)      /* str rd, [sp, #off] */


However, this raises the question of whether there are other cases that can end
up in that final 'else' branch.  I am still analyzing whether or not that can
happen.

With that in mind, though: is there any particular reason why the frame
register is set to -1?  It seems like the code could be written to just always
fall back on having the frame register equal to the SP.  That would fix this
problem and presumably other cases without special casing moves to SP.

(Also, similar logic exist in 'arm-tdep.c:arm_analyze_prologue'.)

-- 
Meador Inge
CodeSourcery / Mentor Embedded
http://www.mentor.com/embedded-software


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]