(arm_linux_init_abi): Use it.
* arm-tdep.c (arm_get_next_pc): Make global. Handle all-ones
condition correctly.
* arm-tdep.h (arm_get_next_pc): Declare.
* Makefile.in (arm-linux-tdep.o): Update.
+2007-09-27 Daniel Jacobowitz <dan@codesourcery.com>
+
+ * arm-linux-tdep.c (arm_linux_software_single_step): New.
+ (arm_linux_init_abi): Use it.
+ * arm-tdep.c (arm_get_next_pc): Make global. Handle all-ones
+ condition correctly.
+ * arm-tdep.h (arm_get_next_pc): Declare.
+ * Makefile.in (arm-linux-tdep.o): Update.
+
2007-09-26 Vladimir Prus <vladimir@codesourcery.com>
* varobj.c (install_new_value): Don't
arm-linux-tdep.o: arm-linux-tdep.c $(defs_h) $(target_h) $(value_h) \
$(gdbtypes_h) $(floatformat_h) $(gdbcore_h) $(frame_h) $(regcache_h) \
$(doublest_h) $(solib_svr4_h) $(osabi_h) $(arm_tdep_h) \
- $(regset_h) $(arm_linux_tdep_h) \
+ $(regset_h) $(arm_linux_tdep_h) $(breakpoint_h) \
$(glibc_tdep_h) $(trad_frame_h) $(tramp_frame_h) $(gdb_string_h)
armnbsd-nat.o: armnbsd-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) \
$(regcache_h) $(target_h) $(gdb_string_h) $(arm_tdep_h) $(inf_ptrace_h)
#include "regset.h"
#include "trad-frame.h"
#include "tramp-frame.h"
+#include "breakpoint.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
return NULL;
}
+/* Insert a single step breakpoint at the next executed instruction. */
+
+int
+arm_linux_software_single_step (struct frame_info *frame)
+{
+ CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+
+ /* The Linux kernel offers some user-mode helpers in a high page. We can
+ not read this page (as of 2.6.23), and even if we could then we couldn't
+ set breakpoints in it, and even if we could then the atomic operations
+ would fail when interrupted. They are all called as functions and return
+ to the address in LR, so step to there instead. */
+ if (next_pc > 0xffff0000)
+ next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+
+ insert_single_step_breakpoint (next_pc);
+
+ return 1;
+}
+
static void
arm_linux_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
(gdbarch, svr4_ilp32_fetch_link_map_offsets);
/* Single stepping. */
- set_gdbarch_software_single_step (gdbarch, arm_software_single_step);
+ set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step);
/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
return nextpc;
}
-static CORE_ADDR
+CORE_ADDR
arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
{
unsigned long pc_val;
status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */
- if (condition_true (bits (this_instr, 28, 31), status))
+ if (bits (this_instr, 28, 31) == INST_NV)
+ switch (bits (this_instr, 24, 27))
+ {
+ case 0xa:
+ case 0xb:
+ {
+ /* Branch with Link and change to Thumb. */
+ nextpc = BranchDest (pc, this_instr);
+ nextpc |= bit (this_instr, 24) << 1;
+
+ nextpc = gdbarch_addr_bits_remove (current_gdbarch, nextpc);
+ if (nextpc == pc)
+ error (_("Infinite loop detected"));
+ break;
+ }
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ /* Coprocessor register transfer. */
+ if (bits (this_instr, 12, 15) == 15)
+ error (_("Invalid update to pc in instruction"));
+ break;
+ }
+ else if (condition_true (bits (this_instr, 28, 31), status))
{
switch (bits (this_instr, 24, 27))
{
{
nextpc = BranchDest (pc, this_instr);
- /* BLX */
- if (bits (this_instr, 28, 31) == INST_NV)
- nextpc |= bit (this_instr, 24) << 1;
-
nextpc = gdbarch_addr_bits_remove (current_gdbarch, nextpc);
if (nextpc == pc)
error (_("Infinite loop detected"));
#endif
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 *);
/* Functions exported from armbsd-tdep.h. */