* arm-linux-tdep.c (arch-utils.h, inferior.h, gdbthread.h, symfile.h):
Include files. (arm_linux_cleanup_svc, arm_linux_copy_svc): New. (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_displaced_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) (arm_process_displaced_insn, arm_displaced_init_closure) (displaced_read_reg, displaced_write_reg, arm_displaced_step_copy_insn) (arm_displaced_step_fixup): Add prototypes.
This commit is contained in:
parent
929dfd4f59
commit
cca44b1b87
|
@ -1,3 +1,39 @@
|
|||
2009-07-31 Julian Brown <julian@codesourcery.com>
|
||||
|
||||
* arm-linux-tdep.c (arch-utils.h, inferior.h, gdbthread.h, symfile.h):
|
||||
Include files.
|
||||
(arm_linux_cleanup_svc, arm_linux_copy_svc): New.
|
||||
(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_displaced_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)
|
||||
(arm_process_displaced_insn, arm_displaced_init_closure)
|
||||
(displaced_read_reg, displaced_write_reg, arm_displaced_step_copy_insn)
|
||||
(arm_displaced_step_fixup): Add prototypes.
|
||||
|
||||
2009-07-31 Pedro Alves <pedro@codesourcery.com>
|
||||
Julian Brown <julian@codesourcery.com>
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
#include "arm-linux-tdep.h"
|
||||
#include "linux-tdep.h"
|
||||
#include "glibc-tdep.h"
|
||||
#include "arch-utils.h"
|
||||
#include "inferior.h"
|
||||
#include "gdbthread.h"
|
||||
#include "symfile.h"
|
||||
|
||||
#include "gdb_string.h"
|
||||
|
||||
|
@ -597,6 +601,210 @@ arm_linux_software_single_step (struct frame_info *frame)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Support for displaced stepping of Linux SVC instructions. */
|
||||
|
||||
static void
|
||||
arm_linux_cleanup_svc (struct gdbarch *gdbarch ATTRIBUTE_UNUSED,
|
||||
struct regcache *regs,
|
||||
struct displaced_step_closure *dsc)
|
||||
{
|
||||
CORE_ADDR from = dsc->insn_addr;
|
||||
ULONGEST apparent_pc;
|
||||
int within_scratch;
|
||||
|
||||
regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &apparent_pc);
|
||||
|
||||
within_scratch = (apparent_pc >= dsc->scratch_base
|
||||
&& apparent_pc < (dsc->scratch_base
|
||||
+ DISPLACED_MODIFIED_INSNS * 4 + 4));
|
||||
|
||||
if (debug_displaced)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: PC is apparently %.8lx after "
|
||||
"SVC step ", (unsigned long) apparent_pc);
|
||||
if (within_scratch)
|
||||
fprintf_unfiltered (gdb_stdlog, "(within scratch space)\n");
|
||||
else
|
||||
fprintf_unfiltered (gdb_stdlog, "(outside scratch space)\n");
|
||||
}
|
||||
|
||||
if (within_scratch)
|
||||
displaced_write_reg (regs, dsc, ARM_PC_REGNUM, from + 4, BRANCH_WRITE_PC);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_linux_copy_svc (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
|
||||
struct regcache *regs, struct displaced_step_closure *dsc)
|
||||
{
|
||||
CORE_ADDR from = dsc->insn_addr;
|
||||
struct frame_info *frame;
|
||||
unsigned int svc_number = displaced_read_reg (regs, from, 7);
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: copying Linux svc insn %.8lx\n",
|
||||
(unsigned long) insn);
|
||||
|
||||
frame = get_current_frame ();
|
||||
|
||||
/* Is this a sigreturn or rt_sigreturn syscall? Note: these are only useful
|
||||
for EABI. */
|
||||
if (svc_number == 119 || svc_number == 173)
|
||||
{
|
||||
if (get_frame_type (frame) == SIGTRAMP_FRAME)
|
||||
{
|
||||
CORE_ADDR return_to;
|
||||
struct symtab_and_line sal;
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: found "
|
||||
"sigreturn/rt_sigreturn SVC call. PC in frame = %lx\n",
|
||||
(unsigned long) get_frame_pc (frame));
|
||||
|
||||
return_to = frame_unwind_caller_pc (frame);
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. "
|
||||
"Setting momentary breakpoint.\n", (unsigned long) return_to);
|
||||
|
||||
gdb_assert (inferior_thread ()->step_resume_breakpoint == NULL);
|
||||
|
||||
sal = find_pc_line (return_to, 0);
|
||||
sal.pc = return_to;
|
||||
sal.section = find_pc_overlay (return_to);
|
||||
sal.explicit_pc = 1;
|
||||
|
||||
frame = get_prev_frame (frame);
|
||||
|
||||
if (frame)
|
||||
{
|
||||
inferior_thread ()->step_resume_breakpoint
|
||||
= set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
|
||||
bp_step_resume);
|
||||
|
||||
/* We need to make sure we actually insert the momentary
|
||||
breakpoint set above. */
|
||||
insert_breakpoints ();
|
||||
}
|
||||
else if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stderr, "displaced: couldn't find previous "
|
||||
"frame to set momentary breakpoint for "
|
||||
"sigreturn/rt_sigreturn\n");
|
||||
}
|
||||
else if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: sigreturn/rt_sigreturn "
|
||||
"SVC call not in signal trampoline frame\n");
|
||||
}
|
||||
|
||||
/* Preparation: If we detect sigreturn, set momentary breakpoint at resume
|
||||
location, else nothing.
|
||||
Insn: unmodified svc.
|
||||
Cleanup: if pc lands in scratch space, pc <- insn_addr + 4
|
||||
else leave pc alone. */
|
||||
|
||||
dsc->modinsn[0] = insn;
|
||||
|
||||
dsc->cleanup = &arm_linux_cleanup_svc;
|
||||
/* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
|
||||
instruction. */
|
||||
dsc->wrote_to_pc = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The following two functions implement single-stepping over calls to Linux
|
||||
kernel helper routines, which perform e.g. atomic operations on architecture
|
||||
variants which don't support them natively.
|
||||
|
||||
When this function is called, the PC will be pointing at the kernel helper
|
||||
(at an address inaccessible to GDB), and r14 will point to the return
|
||||
address. Displaced stepping always executes code in the copy area:
|
||||
so, make the copy-area instruction branch back to the kernel helper (the
|
||||
"from" address), and make r14 point to the breakpoint in the copy area. In
|
||||
that way, we regain control once the kernel helper returns, and can clean
|
||||
up appropriately (as if we had just returned from the kernel helper as it
|
||||
would have been called from the non-displaced location). */
|
||||
|
||||
static void
|
||||
cleanup_kernel_helper_return (struct gdbarch *gdbarch ATTRIBUTE_UNUSED,
|
||||
struct regcache *regs,
|
||||
struct displaced_step_closure *dsc)
|
||||
{
|
||||
displaced_write_reg (regs, dsc, ARM_LR_REGNUM, dsc->tmp[0], CANNOT_WRITE_PC);
|
||||
displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->tmp[0], BRANCH_WRITE_PC);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_catch_kernel_helper_return (struct gdbarch *gdbarch, CORE_ADDR from,
|
||||
CORE_ADDR to, struct regcache *regs,
|
||||
struct displaced_step_closure *dsc)
|
||||
{
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
|
||||
dsc->numinsns = 1;
|
||||
dsc->insn_addr = from;
|
||||
dsc->cleanup = &cleanup_kernel_helper_return;
|
||||
/* Say we wrote to the PC, else cleanup will set PC to the next
|
||||
instruction in the helper, which isn't helpful. */
|
||||
dsc->wrote_to_pc = 1;
|
||||
|
||||
/* Preparation: tmp[0] <- r14
|
||||
r14 <- <scratch space>+4
|
||||
*(<scratch space>+8) <- from
|
||||
Insn: ldr pc, [r14, #4]
|
||||
Cleanup: r14 <- tmp[0], pc <- tmp[0]. */
|
||||
|
||||
dsc->tmp[0] = displaced_read_reg (regs, from, ARM_LR_REGNUM);
|
||||
displaced_write_reg (regs, dsc, ARM_LR_REGNUM, (ULONGEST) to + 4,
|
||||
CANNOT_WRITE_PC);
|
||||
write_memory_unsigned_integer (to + 8, 4, byte_order, from);
|
||||
|
||||
dsc->modinsn[0] = 0xe59ef004; /* ldr pc, [lr, #4]. */
|
||||
}
|
||||
|
||||
/* Linux-specific displaced step instruction copying function. Detects when
|
||||
the program has stepped into a Linux kernel helper routine (which must be
|
||||
handled as a special case), falling back to arm_displaced_step_copy_insn()
|
||||
if it hasn't. */
|
||||
|
||||
static struct displaced_step_closure *
|
||||
arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
|
||||
CORE_ADDR from, CORE_ADDR to,
|
||||
struct regcache *regs)
|
||||
{
|
||||
struct displaced_step_closure *dsc
|
||||
= xmalloc (sizeof (struct displaced_step_closure));
|
||||
|
||||
/* Detect when we enter an (inaccessible by GDB) Linux kernel helper, and
|
||||
stop at the return location. */
|
||||
if (from > 0xffff0000)
|
||||
{
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: detected kernel helper "
|
||||
"at %.8lx\n", (unsigned long) from);
|
||||
|
||||
arm_catch_kernel_helper_return (gdbarch, from, to, regs, dsc);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
uint32_t insn = read_memory_unsigned_integer (from, 4, byte_order);
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: stepping insn %.8lx "
|
||||
"at %.8lx\n", (unsigned long) insn,
|
||||
(unsigned long) from);
|
||||
|
||||
/* Override the default handling of SVC instructions. */
|
||||
dsc->u.svc.copy_svc_os = arm_linux_copy_svc;
|
||||
|
||||
arm_process_displaced_insn (gdbarch, insn, from, to, regs, dsc);
|
||||
}
|
||||
|
||||
arm_displaced_init_closure (gdbarch, from, to, dsc);
|
||||
|
||||
return dsc;
|
||||
}
|
||||
|
||||
static void
|
||||
arm_linux_init_abi (struct gdbarch_info info,
|
||||
struct gdbarch *gdbarch)
|
||||
|
@ -657,6 +865,14 @@ arm_linux_init_abi (struct gdbarch_info info,
|
|||
arm_linux_regset_from_core_section);
|
||||
|
||||
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
|
||||
|
||||
/* Displaced stepping. */
|
||||
set_gdbarch_displaced_step_copy_insn (gdbarch,
|
||||
arm_linux_displaced_step_copy_insn);
|
||||
set_gdbarch_displaced_step_fixup (gdbarch, arm_displaced_step_fixup);
|
||||
set_gdbarch_displaced_step_free_closure (gdbarch,
|
||||
simple_displaced_step_free_closure);
|
||||
set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point);
|
||||
}
|
||||
|
||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||
|
|
1860
gdb/arm-tdep.c
1860
gdb/arm-tdep.c
File diff suppressed because it is too large
Load Diff
102
gdb/arm-tdep.h
102
gdb/arm-tdep.h
|
@ -186,11 +186,113 @@ struct gdbarch_tdep
|
|||
struct type *neon_quad_type;
|
||||
};
|
||||
|
||||
/* Structures used for displaced stepping. */
|
||||
|
||||
/* The maximum number of temporaries available for displaced instructions. */
|
||||
#define DISPLACED_TEMPS 16
|
||||
/* The maximum number of modified instructions generated for one single-stepped
|
||||
instruction, including the breakpoint (usually at the end of the instruction
|
||||
sequence) and any scratch words, etc. */
|
||||
#define DISPLACED_MODIFIED_INSNS 8
|
||||
|
||||
struct displaced_step_closure
|
||||
{
|
||||
ULONGEST tmp[DISPLACED_TEMPS];
|
||||
int rd;
|
||||
int wrote_to_pc;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int xfersize;
|
||||
int rn; /* Writeback register. */
|
||||
unsigned int immed : 1; /* Offset is immediate. */
|
||||
unsigned int writeback : 1; /* Perform base-register writeback. */
|
||||
unsigned int restore_r4 : 1; /* Used r4 as scratch. */
|
||||
} ldst;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned long dest;
|
||||
unsigned int link : 1;
|
||||
unsigned int exchange : 1;
|
||||
unsigned int cond : 4;
|
||||
} branch;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int regmask;
|
||||
int rn;
|
||||
CORE_ADDR xfer_addr;
|
||||
unsigned int load : 1;
|
||||
unsigned int user : 1;
|
||||
unsigned int increment : 1;
|
||||
unsigned int before : 1;
|
||||
unsigned int writeback : 1;
|
||||
unsigned int cond : 4;
|
||||
} block;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int immed : 1;
|
||||
} preload;
|
||||
|
||||
struct
|
||||
{
|
||||
/* If non-NULL, override generic SVC handling (e.g. for a particular
|
||||
OS). */
|
||||
int (*copy_svc_os) (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
|
||||
struct regcache *regs,
|
||||
struct displaced_step_closure *dsc);
|
||||
} svc;
|
||||
} u;
|
||||
unsigned long modinsn[DISPLACED_MODIFIED_INSNS];
|
||||
int numinsns;
|
||||
CORE_ADDR insn_addr;
|
||||
CORE_ADDR scratch_base;
|
||||
void (*cleanup) (struct gdbarch *, struct regcache *,
|
||||
struct displaced_step_closure *);
|
||||
};
|
||||
|
||||
/* Values for the WRITE_PC argument to displaced_write_reg. If the register
|
||||
write may write to the PC, specifies the way the CPSR T bit, etc. is
|
||||
modified by the instruction. */
|
||||
|
||||
enum pc_write_style
|
||||
{
|
||||
BRANCH_WRITE_PC,
|
||||
BX_WRITE_PC,
|
||||
LOAD_WRITE_PC,
|
||||
ALU_WRITE_PC,
|
||||
CANNOT_WRITE_PC
|
||||
};
|
||||
|
||||
extern void
|
||||
arm_process_displaced_insn (struct gdbarch *gdbarch, uint32_t insn,
|
||||
CORE_ADDR from, CORE_ADDR to,
|
||||
struct regcache *regs,
|
||||
struct displaced_step_closure *dsc);
|
||||
extern void
|
||||
arm_displaced_init_closure (struct gdbarch *gdbarch, CORE_ADDR from,
|
||||
CORE_ADDR to, struct displaced_step_closure *dsc);
|
||||
extern ULONGEST
|
||||
displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno);
|
||||
extern void
|
||||
displaced_write_reg (struct regcache *regs,
|
||||
struct displaced_step_closure *dsc, int regno,
|
||||
ULONGEST val, enum pc_write_style write_pc);
|
||||
|
||||
CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
|
||||
CORE_ADDR arm_get_next_pc (struct frame_info *, CORE_ADDR);
|
||||
int arm_software_single_step (struct frame_info *);
|
||||
|
||||
extern struct displaced_step_closure *
|
||||
arm_displaced_step_copy_insn (struct gdbarch *, CORE_ADDR, CORE_ADDR,
|
||||
struct regcache *);
|
||||
extern void arm_displaced_step_fixup (struct gdbarch *,
|
||||
struct displaced_step_closure *,
|
||||
CORE_ADDR, CORE_ADDR, struct regcache *);
|
||||
|
||||
/* Functions exported from armbsd-tdep.h. */
|
||||
|
||||
/* Return the appropriate register set for the core section identified
|
||||
|
|
Loading…
Reference in New Issue