From d9311bfaf572cf14af577a66e79c51c491553552 Mon Sep 17 00:00:00 2001 From: Antoine Tremblay Date: Fri, 18 Dec 2015 11:33:59 -0500 Subject: Support software single step on ARM in GDBServer This patch teaches GDBServer how to software single step on ARM linux by sharing code with GDB. The arm_get_next_pcs function in GDB is now shared with GDBServer. So that GDBServer can use the function to return the possible addresses of the next PC. A proper shared context was also needed so that we could share the code, this context is described in the arm_get_next_pcs structure. Testing : No regressions, tested on ubuntu 14.04 ARMv7 and x86. With gdbserver-{native,extended} / { -marm -mthumb } gdb/ChangeLog: * Makefile.in (ALL_TARGET_OBS): Append arm-get-next-pcs.o, arm-linux.o. (ALLDEPFILES): Append arm-get-next-pcs.c, arm-linux.c (arm-linux.o): New rule. (arm-get-next-pcs.o): New rule. * arch/arm-get-next-pcs.c: New file. * arch/arm-get-next-pcs.h: New file. * arch/arm-linux.h: New file. * arch/arm-linux.c: New file. * arm.c: Include common-regcache.c. (thumb_advance_itstate): Moved from arm-tdep.c. (arm_instruction_changes_pc): Likewise. (thumb_instruction_changes_pc): Likewise. (thumb2_instruction_changes_pc): Likewise. (shifted_reg_val): Likewise. * arm.h (submask): Move macro from arm-tdep.h (bit): Likewise. (bits): Likewise. (sbits): Likewise. (BranchDest): Likewise. (thumb_advance_itstate): Moved declaration from arm-tdep.h (arm_instruction_changes_pc): Likewise. (thumb_instruction_changes_pc): Likewise. (thumb2_instruction_changes_pc): Likewise. (shifted_reg_val): Likewise. * arm-linux-tdep.c: Include arch/arm.h, arch/arm-get-next-pcs.h arch/arm-linux.h. (arm_linux_get_next_pcs_ops): New struct. (ARM_SIGCONTEXT_R0, ARM_UCONTEXT_SIGCONTEXT, ARM_OLD_RT_SIGFRAME_SIGINFO, ARM_OLD_RT_SIGFRAME_UCONTEXT, ARM_NEW_RT_SIGFRAME_UCONTEXT, ARM_NEW_SIGFRAME_MAGIC): Move stack layout defines to arch/arm-linux.h. (arm_linux_sigreturn_next_pc_offset): Move to arch/arm-linux.c. (arm_linux_software_single_step): Adjust for arm_get_next_pcs implementation. * arm-tdep.c: Include arch/arm-get-next-pcs.h. (arm_get_next_pcs_ops): New struct. (submask): Move macro to arm.h. (bit): Likewise. (bits): Likewise. (sbits): Likewise. (BranchDest): Likewise. (thumb_instruction_changes_pc): Move to arm.c (thumb2_instruction_changes_pc): Likewise. (arm_instruction_changes_pc): Likewise. (shifted_reg_val): Likewise. (thumb_advance_itstate): Likewise. (thumb_get_next_pc_raw): Move to arm-get-next-pcs.c. (arm_get_next_pc_raw): Likewise. (arm_get_next_pc): Likewise. (thumb_deal_with_atomic_sequence_raw): Likewise. (arm_deal_with_atomic_sequence_raw): Likewise. (arm_deal_with_atomic_sequence): Likewise. (arm_get_next_pcs_read_memory_unsigned_integer): New function. (arm_get_next_pcs_addr_bits_remove): Likewise. (arm_get_next_pcs_syscall_next_pc): Likewise. (arm_get_next_pcs_is_thumb): Likewise. (arm_software_single_step): Adjust for arm_get_next_pcs implementation. * arm-tdep.h: (arm_get_next_pc): Remove declaration. (arm_get_next_pcs_read_memory_unsigned_integer): New declaration. (arm_get_next_pcs_addr_bits_remove): Likewise. (arm_get_next_pcs_syscall_next_pc): Likewise. (arm_get_next_pcs_is_thumb): Likewise. (arm_deal_with_atomic_sequence: Remove declaration. * common/gdb_vecs.h: Add CORE_ADDR vector definition. * configure.tgt (aarch64*-*-linux): Add arm-get-next-pcs.o, arm-linux.o. (arm*-wince-pe): Add arm-get-next-pcs.o. (arm*-*-linux*): Add arm-get-next-pcs.o, arm-linux.o, arm-get-next-pcs.o (arm*-*-netbsd*,arm*-*-knetbsd*-gnu): Add arm-get-next-pcs.o. (arm*-*-openbsd*): Likewise. (arm*-*-symbianelf*): Likewise. (arm*-*-*): Likewise. * symtab.h: Move CORE_ADDR vector definition to gdb_vecs.h. gdb/gdbserver/ChangeLog: * Makefile.in (SFILES): Append arch/arm-linux.c, arch/arm-get-next-pcs.c. (arm-linux.o): New rule. (arm-get-next-pcs.o): New rule. * configure.srv (arm*-*-linux*): Add arm-get-next-pcs.o, arm-linux.o. * linux-aarch32-low.c (arm_abi_breakpoint): Remove macro. Moved to linux-aarch32-low.c. (arm_eabi_breakpoint, arm_breakpoint): Likewise. (arm_breakpoint_len, thumb_breakpoint): Likewise. (thumb_breakpoint_len, thumb2_breakpoint): Likewise. (thumb2_breakpoint_len): Likewise. (arm_is_thumb_mode): Make non-static. * linux-aarch32-low.h (arm_abi_breakpoint): New macro. Moved from linux-aarch32-low.c. (arm_eabi_breakpoint, arm_breakpoint): Likewise. (arm_breakpoint_len, thumb_breakpoint): Likewise. (thumb_breakpoint_len, thumb2_breakpoint): Likewise. (thumb2_breakpoint_len): Likewise. (arm_is_thumb_mode): New declaration. * linux-arm-low.c: Include arch/arm-linux.h aarch/arm-get-next-pcs.h, sys/syscall.h. (get_next_pcs_ops): New struct. (get_next_pcs_addr_bits_remove): New function. (get_next_pcs_is_thumb): New function. (get_next_pcs_read_memory_unsigned_integer): Likewise. (arm_sigreturn_next_pc): Likewise. (get_next_pcs_syscall_next_pc): Likewise. (arm_gdbserver_get_next_pcs): Likewise. (struct linux_target_ops) : Initialize. * linux-low.h: Move CORE_ADDR vector definition to gdb_vecs.h. * server.h: Include gdb_vecs.h. --- gdb/arm-linux-tdep.c | 133 +++++++++++++++------------------------------------ 1 file changed, 39 insertions(+), 94 deletions(-) (limited to 'gdb/arm-linux-tdep.c') diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index e06cf77..4771dae 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -35,6 +35,9 @@ #include "auxv.h" #include "xml-syscall.h" +#include "arch/arm.h" +#include "arch/arm-get-next-pcs.h" +#include "arch/arm-linux.h" #include "arm-tdep.h" #include "arm-linux-tdep.h" #include "linux-tdep.h" @@ -262,6 +265,14 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa /* Syscall number for rt_sigreturn. */ #define ARM_RT_SIGRETURN 173 +/* Operation function pointers for get_next_pcs. */ +static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = { + arm_get_next_pcs_read_memory_unsigned_integer, + arm_get_next_pcs_syscall_next_pc, + arm_get_next_pcs_addr_bits_remove, + arm_get_next_pcs_is_thumb +}; + static void arm_linux_sigtramp_cache (struct frame_info *this_frame, struct trad_frame_cache *this_cache, @@ -283,51 +294,7 @@ arm_linux_sigtramp_cache (struct frame_info *this_frame, trad_frame_set_id (this_cache, frame_id_build (sp, func)); } -/* There are a couple of different possible stack layouts that - we need to support. - - Before version 2.6.18, the kernel used completely independent - layouts for non-RT and RT signals. For non-RT signals the stack - began directly with a struct sigcontext. For RT signals the stack - began with two redundant pointers (to the siginfo and ucontext), - and then the siginfo and ucontext. - - As of version 2.6.18, the non-RT signal frame layout starts with - a ucontext and the RT signal frame starts with a siginfo and then - a ucontext. Also, the ucontext now has a designated save area - for coprocessor registers. - - For RT signals, it's easy to tell the difference: we look for - pinfo, the pointer to the siginfo. If it has the expected - value, we have an old layout. If it doesn't, we have the new - layout. - - For non-RT signals, it's a bit harder. We need something in one - layout or the other with a recognizable offset and value. We can't - use the return trampoline, because ARM usually uses SA_RESTORER, - in which case the stack return trampoline is not filled in. - We can't use the saved stack pointer, because sigaltstack might - be in use. So for now we guess the new layout... */ - -/* There are three words (trap_no, error_code, oldmask) in - struct sigcontext before r0. */ -#define ARM_SIGCONTEXT_R0 0xc - -/* There are five words (uc_flags, uc_link, and three for uc_stack) - in the ucontext_t before the sigcontext. */ -#define ARM_UCONTEXT_SIGCONTEXT 0x14 - -/* There are three elements in an rt_sigframe before the ucontext: - pinfo, puc, and info. The first two are pointers and the third - is a struct siginfo, with size 128 bytes. We could follow puc - to the ucontext, but it's simpler to skip the whole thing. */ -#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8 -#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88 - -#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80 - -#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a - +/* See arm-linux.h for stack layout details. */ static void arm_linux_sigreturn_init (const struct tramp_frame *self, struct frame_info *this_frame, @@ -810,41 +777,6 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame, return 0; } -/* Calculate the offset from stack pointer of the pc register on the stack - in the case of a sigreturn or sigreturn_rt syscall. */ -static int -arm_linux_sigreturn_next_pc_offset (unsigned long sp, - unsigned long sp_data, - unsigned long svc_number, - int is_sigreturn) -{ - /* Offset of R0 register. */ - int r0_offset = 0; - /* Offset of PC register. */ - int pc_offset = 0; - - if (is_sigreturn) - { - if (sp_data == ARM_NEW_SIGFRAME_MAGIC) - r0_offset = ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0; - else - r0_offset = ARM_SIGCONTEXT_R0; - } - else - { - if (sp_data == sp + ARM_OLD_RT_SIGFRAME_SIGINFO) - r0_offset = ARM_OLD_RT_SIGFRAME_UCONTEXT; - else - r0_offset = ARM_NEW_RT_SIGFRAME_UCONTEXT; - - r0_offset += ARM_UCONTEXT_SIGCONTEXT + ARM_SIGCONTEXT_R0; - } - - pc_offset = r0_offset + INT_REGISTER_SIZE * ARM_PC_REGNUM; - - return pc_offset; -} - /* Find the value of the next PC after a sigreturn or rt_sigreturn syscall based on current processor state. */ static CORE_ADDR @@ -984,28 +916,41 @@ arm_linux_software_single_step (struct frame_info *frame) struct regcache *regcache = get_current_regcache (); struct gdbarch *gdbarch = get_regcache_arch (regcache); struct address_space *aspace = get_regcache_aspace (regcache); - - CORE_ADDR next_pc; - - if (arm_deal_with_atomic_sequence (regcache)) - return 1; + struct arm_get_next_pcs next_pcs_ctx; + CORE_ADDR pc; + int i; + VEC (CORE_ADDR) *next_pcs = NULL; + struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs); /* If the target does have hardware single step, GDB doesn't have to bother software single step. */ if (target_can_do_single_step () == 1) return 0; - next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache)); + arm_get_next_pcs_ctor (&next_pcs_ctx, + &arm_linux_get_next_pcs_ops, + gdbarch_byte_order (gdbarch), + gdbarch_byte_order_for_code (gdbarch), + gdbarch_tdep (gdbarch)->thumb2_breakpoint, + regcache); + + next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache)); - /* 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); + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++) + { + /* 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 (pc > 0xffff0000) + pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM); + + arm_insert_single_step_breakpoint (gdbarch, aspace, pc); + } - arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc); + do_cleanups (old_chain); return 1; } -- cgit v1.1