/* GNU/Linux on ARM target support.
- Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2016 Free Software Foundation, Inc.
This file is part of GDB.
#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"
#define ARM_LDR_PC_SP_12 0xe49df00c
#define ARM_LDR_PC_SP_4 0xe49df004
+/* Syscall number for sigreturn. */
+#define ARM_SIGRETURN 119
+/* Syscall number for rt_sigreturn. */
+#define ARM_RT_SIGRETURN 173
+
+static CORE_ADDR
+ arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+ CORE_ADDR pc);
+
+/* 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_linux_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,
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,
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- const gdb_byte *gregs = gregs_buf;
+ const gdb_byte *gregs = (const gdb_byte *) gregs_buf;
int regno;
CORE_ADDR reg_pc;
gdb_byte pc_buf[INT_REGISTER_SIZE];
const struct regcache *regcache,
int regnum, void *gregs_buf, size_t len)
{
- gdb_byte *gregs = gregs_buf;
+ gdb_byte *gregs = (gdb_byte *) gregs_buf;
int regno;
for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
struct regcache *regcache,
int regnum, const void *regs_buf, size_t len)
{
- const gdb_byte *regs = regs_buf;
+ const gdb_byte *regs = (const gdb_byte *) regs_buf;
int regno;
if (regnum == ARM_FPS_REGNUM || regnum == -1)
const struct regcache *regcache,
int regnum, void *regs_buf, size_t len)
{
- gdb_byte *regs = regs_buf;
+ gdb_byte *regs = (gdb_byte *) regs_buf;
int regno;
for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++)
struct regcache *regcache,
int regnum, const void *regs_buf, size_t len)
{
- const gdb_byte *regs = regs_buf;
+ const gdb_byte *regs = (const gdb_byte *) regs_buf;
int regno;
if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
const struct regcache *regcache,
int regnum, void *regs_buf, size_t len)
{
- gdb_byte *regs = regs_buf;
+ gdb_byte *regs = (gdb_byte *) regs_buf;
int regno;
if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
cb (".reg", ARM_LINUX_SIZEOF_GREGSET, &arm_linux_gregset, NULL, cb_data);
- if (tdep->have_vfp_registers)
+ if (tdep->vfp_register_count > 0)
cb (".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, &arm_linux_vfpregset,
"VFP floating-point", cb_data);
else if (tdep->have_fpa_registers)
return 0;
}
+/* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
+ based on current processor state. In addition, set IS_THUMB depending
+ on whether we will return to ARM or Thumb code. */
+
+static CORE_ADDR
+arm_linux_sigreturn_next_pc (struct regcache *regcache,
+ unsigned long svc_number, int *is_thumb)
+{
+ ULONGEST sp;
+ unsigned long sp_data;
+ CORE_ADDR next_pc = 0;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int pc_offset = 0;
+ int is_sigreturn = 0;
+ CORE_ADDR cpsr;
+
+ gdb_assert (svc_number == ARM_SIGRETURN
+ || svc_number == ARM_RT_SIGRETURN);
+
+ is_sigreturn = (svc_number == ARM_SIGRETURN);
+ regcache_cooked_read_unsigned (regcache, ARM_SP_REGNUM, &sp);
+ sp_data = read_memory_unsigned_integer (sp, 4, byte_order);
+
+ pc_offset = arm_linux_sigreturn_next_pc_offset (sp, sp_data, svc_number,
+ is_sigreturn);
+
+ next_pc = read_memory_unsigned_integer (sp + pc_offset, 4, byte_order);
+
+ /* Set IS_THUMB according the CPSR saved on the stack. */
+ cpsr = read_memory_unsigned_integer (sp + pc_offset + 4, 4, byte_order);
+ *is_thumb = ((cpsr & arm_psr_thumb_bit (gdbarch)) != 0);
+
+ return next_pc;
+}
+
/* At a ptrace syscall-stop, return the syscall number. This either
comes from the SWI instruction (OABI) or from r7 (EABI).
return svc_number;
}
-/* When FRAME is at a syscall instruction, return the PC of the next
- instruction to be executed. */
-
static CORE_ADDR
-arm_linux_syscall_next_pc (struct frame_info *frame)
+arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+ CORE_ADDR pc)
{
- CORE_ADDR pc = get_frame_pc (frame);
- CORE_ADDR return_addr = 0;
- int is_thumb = arm_frame_is_thumb (frame);
+ CORE_ADDR next_pc = 0;
+ int is_thumb = arm_is_thumb (self->regcache);
ULONGEST svc_number = 0;
+ struct gdbarch *gdbarch = get_regcache_arch (self->regcache);
if (is_thumb)
{
- svc_number = get_frame_register_unsigned (frame, 7);
- return_addr = pc + 2;
+ svc_number = regcache_raw_get_unsigned (self->regcache, 7);
+ next_pc = pc + 2;
}
else
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch *gdbarch = get_regcache_arch (self->regcache);
enum bfd_endian byte_order_for_code =
gdbarch_byte_order_for_code (gdbarch);
unsigned long this_instr =
}
else /* EABI. */
{
- svc_number = get_frame_register_unsigned (frame, 7);
+ svc_number = regcache_raw_get_unsigned (self->regcache, 7);
}
- return_addr = pc + 4;
+ next_pc = pc + 4;
}
- arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
+ if (svc_number == ARM_SIGRETURN || svc_number == ARM_RT_SIGRETURN)
+ {
+ /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+ update IS_THUMB. */
+ next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number,
+ &is_thumb);
+ }
/* Addresses for calling Thumb functions have the bit 0 set. */
if (is_thumb)
- return_addr |= 1;
+ next_pc = MAKE_THUMB_ADDR (next_pc);
- return return_addr;
+ return next_pc;
}
static int
arm_linux_software_single_step (struct frame_info *frame)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc;
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct address_space *aspace = get_regcache_aspace (regcache);
+ 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 (arm_deal_with_atomic_sequence (frame))
- return 1;
+ /* 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;
+
+ arm_get_next_pcs_ctor (&next_pcs_ctx,
+ &arm_linux_get_next_pcs_ops,
+ gdbarch_byte_order (gdbarch),
+ gdbarch_byte_order_for_code (gdbarch),
+ 1,
+ regcache);
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ next_pcs = arm_get_next_pcs (&next_pcs_ctx);
- /* 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;
}
struct regcache *regs,
struct displaced_step_closure *dsc)
{
- CORE_ADDR from = dsc->insn_addr;
ULONGEST apparent_pc;
int within_scratch;
}
if (within_scratch)
- displaced_write_reg (regs, dsc, ARM_PC_REGNUM, from + 4, BRANCH_WRITE_PC);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM,
+ dsc->insn_addr + dsc->insn_size, BRANCH_WRITE_PC);
}
static int
&return_to, &is_thumb);
if (is_sigreturn)
{
- 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));
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. "
- "Setting momentary breakpoint.\n", (unsigned long) return_to);
-
- gdb_assert (inferior_thread ()->control.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 ()->control.step_resume_breakpoint
- = set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
- bp_step_resume);
-
- /* set_momentary_breakpoint invalidates FRAME. */
- frame = NULL;
-
- /* 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");
+ 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));
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. "
+ "Setting momentary breakpoint.\n",
+ (unsigned long) return_to);
+
+ gdb_assert (inferior_thread ()->control.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 ()->control.step_resume_breakpoint
+ = set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
+ bp_step_resume);
+
+ /* set_momentary_breakpoint invalidates FRAME. */
+ frame = NULL;
+
+ /* We need to make sure we actually insert the momentary
+ breakpoint set above. */
+ insert_breakpoints ();
}
else if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: sigreturn/rt_sigreturn "
- "SVC call not in signal trampoline frame\n");
-
+ 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: found SVC call\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
+ Cleanup: if pc lands in scratch space, pc <- insn_addr + insn_size
else leave pc alone. */
CORE_ADDR from, CORE_ADDR to,
struct regcache *regs)
{
- struct displaced_step_closure *dsc
- = xmalloc (sizeof (struct displaced_step_closure));
+ struct displaced_step_closure *dsc = XNEW (struct displaced_step_closure);
/* Detect when we enter an (inaccessible by GDB) Linux kernel helper, and
stop at the return location. */
return 0;
len = tmp - start;
- regname = alloca (len + 2);
+ regname = (char *) alloca (len + 2);
offset = 0;
if (isdigit (*start))
enum { sys_process_vm_writev = 377 };
if (syscall <= gdb_sys_sched_getaffinity)
- return syscall;
+ return (enum gdb_syscall) syscall;
else if (syscall >= 243 && syscall <= 247)
- return syscall + 2;
+ return (enum gdb_syscall) (syscall + 2);
else if (syscall >= 248 && syscall <= 253)
- return syscall + 4;
+ return (enum gdb_syscall) (syscall + 4);
- return -1;
+ return gdb_sys_no_syscall;
}
/* Record all registers but PC register for process-record. */
syscall_gdb = arm_canonicalize_syscall (svc_number);
- if (syscall_gdb < 0)
+ if (syscall_gdb == gdb_sys_no_syscall)
{
printf_unfiltered (_("Process record and replay target doesn't "
"support syscall number %s\n"),
(gdbarch, arm_linux_iterate_over_regset_sections);
set_gdbarch_core_read_description (gdbarch, arm_linux_core_read_description);
- 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);
+ set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
/* Reversible debugging, process record. */
set_gdbarch_process_record (gdbarch, arm_process_record);
set_gdbarch_stap_parse_special_token (gdbarch,
arm_stap_parse_special_token);
- tdep->syscall_next_pc = arm_linux_syscall_next_pc;
-
/* `catch syscall' */
set_xml_syscall_file_name (gdbarch, "syscalls/arm-linux.xml");
set_gdbarch_get_syscall_number (gdbarch, arm_linux_get_syscall_number);
arm_linux_record_tdep.size_flock = 16;
arm_linux_record_tdep.size_oldold_utsname = 45;
arm_linux_record_tdep.size_ustat = 20;
- arm_linux_record_tdep.size_old_sigaction = 140;
- arm_linux_record_tdep.size_old_sigset_t = 128;
+ arm_linux_record_tdep.size_old_sigaction = 16;
+ arm_linux_record_tdep.size_old_sigset_t = 4;
arm_linux_record_tdep.size_rlimit = 8;
arm_linux_record_tdep.size_rusage = 72;
arm_linux_record_tdep.size_timeval = 8;
arm_linux_record_tdep.size_old_gid_t = 2;
arm_linux_record_tdep.size_old_uid_t = 2;
arm_linux_record_tdep.size_fd_set = 128;
- arm_linux_record_tdep.size_dirent = 268;
- arm_linux_record_tdep.size_dirent64 = 276;
+ arm_linux_record_tdep.size_old_dirent = 268;
arm_linux_record_tdep.size_statfs = 64;
arm_linux_record_tdep.size_statfs64 = 84;
arm_linux_record_tdep.size_sockaddr = 16;
arm_linux_record_tdep.size_NFS_FHSIZE = 32;
arm_linux_record_tdep.size_knfsd_fh = 132;
arm_linux_record_tdep.size_TASK_COMM_LEN = 16;
- arm_linux_record_tdep.size_sigaction = 140;
+ arm_linux_record_tdep.size_sigaction = 20;
arm_linux_record_tdep.size_sigset_t = 8;
arm_linux_record_tdep.size_siginfo_t = 128;
arm_linux_record_tdep.size_cap_user_data_t = 12;
arm_linux_record_tdep.size_stack_t = 12;
arm_linux_record_tdep.size_off_t = arm_linux_record_tdep.size_long;
arm_linux_record_tdep.size_stat64 = 96;
- arm_linux_record_tdep.size_gid_t = 2;
- arm_linux_record_tdep.size_uid_t = 2;
+ arm_linux_record_tdep.size_gid_t = 4;
+ arm_linux_record_tdep.size_uid_t = 4;
arm_linux_record_tdep.size_PAGE_SIZE = 4096;
arm_linux_record_tdep.size_flock64 = 24;
arm_linux_record_tdep.size_user_desc = 16;
arm_linux_record_tdep.size_itimerspec
= arm_linux_record_tdep.size_timespec * 2;
arm_linux_record_tdep.size_mq_attr = 32;
- arm_linux_record_tdep.size_siginfo = 128;
arm_linux_record_tdep.size_termios = 36;
arm_linux_record_tdep.size_termios2 = 44;
arm_linux_record_tdep.size_pid_t = 4;
arm_linux_record_tdep.size_hayes_esp_config = 12;
arm_linux_record_tdep.size_size_t = 4;
arm_linux_record_tdep.size_iovec = 8;
+ arm_linux_record_tdep.size_time_t = 4;
/* These values are the second argument of system call "sys_ioctl".
They are obtained from Linux Kernel source. */