[PATCH] Displaced stepping (non-stop debugging) support for ARM Linux

Julian Brown julian@codesourcery.com
Sat May 16 18:19:00 GMT 2009


Hi,

This is a new version of the patch to support displaced stepping on
ARM. Many things are fixed from the last version posted previously
(January 20th), though we're probably not 100% of the way there yet.

Pedro Alves wrote:

> Right, you may end up with a temporary breakpoint over another
> breakpoint, though.  It would be better to use the standard software
> single-stepping (set temp break at next pc, continue, remove break)
> for standard stepping requests, and use displaced stepping only for
> stepping over breakpoints.  Unfortunately, you don't get that for
> free --- infrun.c and friends don't know how to handle multiple
> simultaneous software single-stepping requests, and that is required
> in non-stop mode.

I'm not sure what the status is here now. For testing purposes, I've
(still) been using a local patch which uses displaced stepping for all
single-step operations.

Daniel Jacobowitz <drow@false.org> wrote:

> * What's the point of executing mov<cond> on the target for BL<cond>?
> At that point it seems like we ought to skip the target step entirely;
> just simulate the instruction.  We've already got a function to check
> conditions (condition_true).

I'm now using NOP instructions and condition_true, because the current
displaced stepping support wants to execute "something" rather than
nothing.

> * Using arm_write_pc is a bit dodgy here; I don't think it's what we
> want.  That function updates the CPSR based on a number of things
> including symbol tables.  We know exactly what is supposed to happen
> to CPSR for a given instruction and should honor it.  An example of
> why this matters: people regularly get a blx in Cortex-M3 code by use
> of bad libraries, untyped ELF symbols, or other such circumstances.
> That blx had better update the CPSR even when we step over it.

Fixed, I think.

> > +/* FIXME: This should depend on the arch version.  */
> > +
> > +static ULONGEST
> > +modify_store_pc (ULONGEST pc)
> > +{
> > +  return pc + 4;
> > +}
> 
> This one we might not be able to fix in current GDB but we can at
> least expand the comment... if I remember right the +4 is correct for
> everything since ARMv5 and most ARMv4?

I've removed this function. Stores of PC now read back the offset, so
should be architecture-version independent (the strategy is slightly
different for STR vs. STM: see below).

> Yes, we just can't emulate loads or stores.  Anything that could cause
> an exception that won't be delayed till the next instruction, I think.

LDM and STM are handled substantially differently now: STM instructions
are let through unmodified, and when PC is in the register list the
cleanup routine reads back the stored value and calculates the proper
offset for PC writes. The true (non-displaced) PC value (plus offset) is
then written to the appropriate memory location.

LDM instructions shuffle registers downwards into a contiguous list (to
avoid loading PC directly), then fix up register contents afterwards in
the cleanup routine. The case with a fully-populated register list is
still emulated, for now.

> > +static int
> > +copy_svc (unsigned long insn, CORE_ADDR to, struct regcache *regs,
> > +	  struct displaced_step_closure *dsc)
> > +{
> > +  CORE_ADDR from = dsc->insn_addr;
> > +
> > +  if (debug_displaced)
> > +    fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn
> > %.8lx\n",
> > +			insn);
> > +
> > +  /* Preparation: tmp[0] <- to.
> > +     Insn: unmodified svc.
> > +     Cleanup: if (pc == <scratch>+4) pc <- insn_addr + 4;
> > +	      else leave PC alone.  */
> 
> What about the saved PC?  Don't really want the OS service routine to
> return to the scratchpad.
> 
> > +  /* FIXME: What can we do about signal trampolines?  */
> 
> Maybe this is referring to the same question I asked above?
> 
> If so, I think you get to unwind and if you find the scratchpad,
> update the saved PC.

I've tried to figure this out, and have totally drawn a blank so far.
AFAICT, the problem we're trying to solve runs as follows: sometimes, a
signal may be delivered to a process whilst it is executing a system
call. In that case, the kernel writes a signal trampoline to the user
program's stack space, and rewrites the state so that the trampoline is
executed when the system call returns.

Now: if we single-step that signal trampoline, we will see a system
call ("sigreturn") which does not return to the caller: rather, it
returns to a handler (in the user program) for the signal in question.
So, the expected result at present is that if displaced stepping is
used to single-step the sigreturn call, the debugger will lose control
of the debugged program.

Unfortunately I've been unable to figure out if the above is true, and
I can't quite figure out the mechanism in enough detail to know if
there's really anything we can do about it if so. My test program
(stolen from the internet and tweaked) runs as follows:

/*
 * signal.c - A signal-catching test program
 */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void func (int, siginfo_t *, void *);
void func2 (int, siginfo_t *, void *);

int main (int argc, char **argv) {
  struct sigaction sa;

  printf ("Starting execution\n");
  sa.sa_sigaction = func;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
  if (sigaction (SIGHUP, &sa, NULL))
   perror ("sigaction() failed");
  sa.sa_sigaction = func2;
  if (sigaction (SIGINT, &sa, NULL))
   perror ("sigaction() failed");
  printf ("sigaction() successful. Now sleeping\n");
  while (1)
   sleep (600);
  printf ("I should not come here\n");
  return 0;
}

void
func (int sig, siginfo_t *sinf, void *foo)
{
  printf ("Signal Handler: sig=%d scp=%p\n", sig, sinf);
  if (sinf)
    {
      printf ("siginfo.si_signo=%d\n", sinf->si_signo);
      printf ("siginfo.si_errno=%d\n", sinf->si_errno);
      printf ("siginfo.si_code=%d\n", sinf->si_code);
    }
  pause ();
  printf ("func() exiting\n");
  sleep (2);
}

void
func2 (int sig, siginfo_t *sinf, void *foo)
{
  printf ("Signal Handler: sig=%d scp=%p\n", sig, sinf);
  if (sinf)
    {
      printf ("siginfo.si_signo=%d\n", sinf->si_signo);
      printf ("siginfo.si_errno=%d\n", sinf->si_errno);
      printf ("siginfo.si_code=%d\n", sinf->si_code);
    }
  printf ("func2() exiting\n");
}

Without the debugger, this can be run, then sent signal 1 (which prints
the messages from func(), and then sent signal 2 (which prints the
messages from func2() -- presumably after running a signal trampoline,
though I'm not entirely certain of that), then sleeps. But with the
debugger, the program never gets beyond func(): and that's where I got
stuck.

> > +struct displaced_step_closure *
> > +arm_displaced_step_copy_insn (struct gdbarch *gdbarch,
> > +			      CORE_ADDR from, CORE_ADDR to,
> > +			      struct regcache *regs)
> > +{
> > +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> > +  const size_t len = 4;
> > +  gdb_byte *buf = xmalloc (len);
> > +  struct displaced_step_closure *dsc;
> > +  unsigned long insn;
> > +  int i;
> > +
> > +  /* A linux-specific hack.  Detect when we've entered
> > (inaccessible by GDB)
> > +     kernel helpers, and stop at the return location.  */
> > +  if (gdbarch_osabi (gdbarch) == GDB_OSABI_LINUX && from >
> > 0xffff0000)
> > +    {
> > +      if (debug_displaced)
> > +        fprintf_unfiltered (gdb_stdlog, "displaced: detected
> > kernel helper "
> > +			    "at %.8lx\n", (unsigned long) from);
> > +
> > +      dsc = arm_catch_kernel_helper_return (from, to, regs);
> > +    }
> > +  else
> > +    {
> > +      insn = read_memory_unsigned_integer (from, len);
> > +
> > +      if (debug_displaced)
> > +	fprintf_unfiltered (gdb_stdlog, "displaced: stepping insn
> > %.8lx "
> > +			    "at %.8lx\n", insn, (unsigned long)
> > from); +
> > +      dsc = arm_process_displaced_insn (insn, from, to, regs);
> > +    }
> 
> Can the Linux-specific hack go in arm-linux-tdep.c?  Shouldn't have to
> make many functions global to do that.

Moved. Other points you (Dan) raised have been dealt with, I think.

I've hit some problems testing this patch, mainly because I can't seem
to get a reliable baseline run with my current test setup. AFAICT, there
should be no affect on behaviour unless displaced stepping is in use
(differences in passes/failures with my patch only seem to be in
"unreliable" tests, after running baseline testing three times), and of
course displaced stepping isn't present for ARM without this patch
anyway.

OK to apply?

Thanks,

Julian

ChangeLog

    gdb/
    * arm-linux-tdep.c (arch-utils.h, inferior.h): Include files.
    (cleanup_kernel_helper_return, arm_catch_kernel_helper_return): New.
    (arm_linux_displaced_step_copy_insn): New.
    (arm_linux_init_abi): Initialise displaced stepping callbacks.
    * arm-tdep.c (DISPLACED_STEPPING_ARCH_VERSION): New macro.
    (ARM_NOP): New.
    (displaced_read_reg, displaced_in_arm_mode, branch_write_pc)
    (bx_write_pc, load_write_pc, alu_write_pc, displaced_write_reg)
    (insn_references_pc, copy_unmodified, cleanup_preload, copy_preload)
    (copy_preload_reg, cleanup_copro_load_store, copy_copro_load_store)
    (cleanup_branch, copy_b_bl_blx, copy_bx_blx_reg, cleanup_alu_imm)
    (copy_alu_imm, cleanup_alu_reg, copy_alu_reg)
    (cleanup_alu_shifted_reg, copy_alu_shifted_reg, cleanup_load)
    (cleanup_store, copy_extra_ld_st, copy_ldr_str_ldrb_strb)
    (cleanup_block_load_all, cleanup_block_store_pc)
    (cleanup_block_load_pc, copy_block_xfer, cleanup_svc, copy_svc)
    (copy_undef, copy_unpred): New.
    (decode_misc_memhint_neon, decode_unconditional)
    (decode_miscellaneous, decode_dp_misc, decode_ld_st_word_ubyte)
    (decode_media, decode_b_bl_ldmstm, decode_ext_reg_ld_st)
    (decode_svc_copro, arm_process_displaced_insn)
    (arm_displaced_init_closure, arm_displcaed_step_copy_insn)
    (arm_displaced_step_fixup): New.
    (arm_gdbarch_init): Initialise max insn length field.
    * arm-tdep.h (DISPLACED_TEMPS, DISPLACED_MODIFIED_INSNS): New
    macros.
    (displaced_step_closure, pc_write_style): New.
    (arm_displaced_init_closure, displaced_read_reg)
    (displaced_write_reg, arm_displaced_step_copy_insn)
    (arm_displaced_step_fixup): Add prototypes.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: fsf-arm-displaced-stepping-6.diff
Type: text/x-patch
Size: 64527 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/gdb-patches/attachments/20090516/b9d63e75/attachment.bin>


More information about the Gdb-patches mailing list