* 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:
Julian Brown 2009-07-30 23:05:05 +00:00
parent 929dfd4f59
commit cca44b1b87
4 changed files with 2214 additions and 0 deletions

View File

@ -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>

View File

@ -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. */

File diff suppressed because it is too large Load Diff

View File

@ -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