X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-arm-low.c;h=fc2b34870a14b00fb201103521e6204bf01e7815;hb=694b382c67a4fc656f30acbc74776c5e9cb53622;hp=bf1792bf9e08d1f6fd0a47ddfebfaebb9bffe884;hpb=c14dfd32064a2213d57d8ef551a9c64841e61032;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index bf1792bf9e..fc2b34870a 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -1,5 +1,5 @@ /* GNU/Linux/ARM specific low level interface, for the remote server for GDB. - Copyright (C) 1995-1996, 1998-2012 Free Software Foundation, Inc. + Copyright (C) 1995-2017 Free Software Foundation, Inc. This file is part of GDB. @@ -18,21 +18,33 @@ #include "server.h" #include "linux-low.h" +#include "arch/arm.h" +#include "arch/arm-linux.h" +#include "arch/arm-get-next-pcs.h" +#include "linux-aarch32-low.h" +#include /* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h. On Bionic elf.h and linux/elf.h have conflicting definitions. */ #ifndef ELFMAG0 #include #endif -#include +#include "nat/gdb_ptrace.h" #include +#include /* Defined in auto-generated files. */ void init_registers_arm (void); +extern const struct target_desc *tdesc_arm; + void init_registers_arm_with_iwmmxt (void); +extern const struct target_desc *tdesc_arm_with_iwmmxt; + void init_registers_arm_with_vfpv2 (void); +extern const struct target_desc *tdesc_arm_with_vfpv2; + void init_registers_arm_with_vfpv3 (void); -void init_registers_arm_with_neon (void); +extern const struct target_desc *tdesc_arm_with_vfpv3; #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 22 @@ -107,8 +119,6 @@ struct arch_lwp_info CORE_ADDR stopped_data_address; }; -static unsigned long arm_hwcap; - /* These are in in current kernels. */ #define HWCAP_VFP 64 #define HWCAP_IWMMXT 512 @@ -129,6 +139,27 @@ static int arm_regmap[] = { 64 }; +/* Forward declarations needed for get_next_pcs ops. */ +static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, + int len, + int byte_order); + +static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, + CORE_ADDR val); + +static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self); + +static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self); + +/* get_next_pcs operations. */ +static struct arm_get_next_pcs_ops get_next_pcs_ops = { + get_next_pcs_read_memory_unsigned_integer, + get_next_pcs_syscall_next_pc, + get_next_pcs_addr_bits_remove, + get_next_pcs_is_thumb, + arm_linux_get_next_pcs_fixup, +}; + static int arm_cannot_store_register (int regno) { @@ -141,36 +172,12 @@ arm_cannot_fetch_register (int regno) return (regno >= arm_num_regs); } -static void -arm_fill_gregset (struct regcache *regcache, void *buf) -{ - int i; - - for (i = 0; i < arm_num_regs; i++) - if (arm_regmap[i] != -1) - collect_register (regcache, i, ((char *) buf) + arm_regmap[i]); -} - -static void -arm_store_gregset (struct regcache *regcache, const void *buf) -{ - int i; - char zerobuf[8]; - - memset (zerobuf, 0, 8); - for (i = 0; i < arm_num_regs; i++) - if (arm_regmap[i] != -1) - supply_register (regcache, i, ((char *) buf) + arm_regmap[i]); - else - supply_register (regcache, i, zerobuf); -} - static void arm_fill_wmmxregset (struct regcache *regcache, void *buf) { int i; - if (!(arm_hwcap & HWCAP_IWMMXT)) + if (regcache->tdesc != tdesc_arm_with_iwmmxt) return; for (i = 0; i < 16; i++) @@ -187,7 +194,7 @@ arm_store_wmmxregset (struct regcache *regcache, const void *buf) { int i; - if (!(arm_hwcap & HWCAP_IWMMXT)) + if (regcache->tdesc != tdesc_arm_with_iwmmxt) return; for (i = 0; i < 16; i++) @@ -202,130 +209,69 @@ arm_store_wmmxregset (struct regcache *regcache, const void *buf) static void arm_fill_vfpregset (struct regcache *regcache, void *buf) { - int i, num, base; - - if (!(arm_hwcap & HWCAP_VFP)) - return; + int num; - if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) + if (regcache->tdesc == tdesc_arm_with_neon + || regcache->tdesc == tdesc_arm_with_vfpv3) num = 32; - else + else if (regcache->tdesc == tdesc_arm_with_vfpv2) num = 16; + else + return; - base = find_regno ("d0"); - for (i = 0; i < num; i++) - collect_register (regcache, base + i, (char *) buf + i * 8); + arm_fill_vfpregset_num (regcache, buf, num); +} - collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8); +/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs. */ +static CORE_ADDR +get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val) +{ + return UNMAKE_THUMB_ADDR (val); } static void arm_store_vfpregset (struct regcache *regcache, const void *buf) { - int i, num, base; + int num; - if (!(arm_hwcap & HWCAP_VFP)) - return; - - if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) + if (regcache->tdesc == tdesc_arm_with_neon + || regcache->tdesc == tdesc_arm_with_vfpv3) num = 32; - else + else if (regcache->tdesc == tdesc_arm_with_vfpv2) num = 16; + else + return; - base = find_regno ("d0"); - for (i = 0; i < num; i++) - supply_register (regcache, base + i, (char *) buf + i * 8); - - supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8); -} - -extern int debug_threads; - -static CORE_ADDR -arm_get_pc (struct regcache *regcache) -{ - unsigned long pc; - collect_register_by_name (regcache, "pc", &pc); - if (debug_threads) - fprintf (stderr, "stop pc is %08lx\n", pc); - return pc; + arm_store_vfpregset_num (regcache, buf, num); } -static void -arm_set_pc (struct regcache *regcache, CORE_ADDR pc) +/* Wrapper of arm_is_thumb_mode for get_next_pcs. */ +static int +get_next_pcs_is_thumb (struct arm_get_next_pcs *self) { - unsigned long newpc = pc; - supply_register_by_name (regcache, "pc", &newpc); + return arm_is_thumb_mode (); } -/* Correct in either endianness. */ -static const unsigned long arm_breakpoint = 0xef9f0001; -#define arm_breakpoint_len 4 -static const unsigned short thumb_breakpoint = 0xde01; -static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 }; - -/* For new EABI binaries. We recognize it regardless of which ABI - is used for gdbserver, so single threaded debugging should work - OK, but for multi-threaded debugging we only insert the current - ABI's breakpoint instruction. For now at least. */ -static const unsigned long arm_eabi_breakpoint = 0xe7f001f0; - -static int -arm_breakpoint_at (CORE_ADDR where) +/* Read memory from the inferiror. + BYTE_ORDER is ignored and there to keep compatiblity with GDB's + read_memory_unsigned_integer. */ +static ULONGEST +get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, + int len, + int byte_order) { - struct regcache *regcache = get_thread_regcache (current_inferior, 1); - unsigned long cpsr; + ULONGEST res; - collect_register_by_name (regcache, "cpsr", &cpsr); + res = 0; + target_read_memory (memaddr, (unsigned char *) &res, len); - if (cpsr & 0x20) - { - /* Thumb mode. */ - unsigned short insn; - - (*the_target->read_memory) (where, (unsigned char *) &insn, 2); - if (insn == thumb_breakpoint) - return 1; - - if (insn == thumb2_breakpoint[0]) - { - (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2); - if (insn == thumb2_breakpoint[1]) - return 1; - } - } - else - { - /* ARM mode. */ - unsigned long insn; - - (*the_target->read_memory) (where, (unsigned char *) &insn, 4); - if (insn == arm_breakpoint) - return 1; - - if (insn == arm_eabi_breakpoint) - return 1; - } - - return 0; -} - -/* We only place breakpoints in empty marker functions, and thread locking - is outside of the function. So rather than importing software single-step, - we can just run until exit. */ -static CORE_ADDR -arm_reinsert_addr (void) -{ - struct regcache *regcache = get_thread_regcache (current_inferior, 1); - unsigned long pc; - collect_register_by_name (regcache, "lr", &pc); - return pc; + return res; } /* Fetch the thread-local storage pointer for libthread_db. */ ps_err_e -ps_get_thread_area (const struct ps_prochandle *ph, +ps_get_thread_area (struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) @@ -430,42 +376,39 @@ arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1, return p1->address == p2->address && p1->control == p2->control; } +/* Convert a raw breakpoint type to an enum arm_hwbp_type. */ + +static arm_hwbp_type +raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type) +{ + switch (raw_type) + { + case raw_bkpt_type_hw: + return arm_hwbp_break; + case raw_bkpt_type_write_wp: + return arm_hwbp_store; + case raw_bkpt_type_read_wp: + return arm_hwbp_load; + case raw_bkpt_type_access_wp: + return arm_hwbp_access; + default: + gdb_assert_not_reached ("unhandled raw type"); + } +} + /* Initialize the hardware breakpoint structure P for a breakpoint or watchpoint at ADDR to LEN. The type of watchpoint is given in TYPE. - Returns -1 if TYPE is unsupported, 0 if TYPE represents a breakpoint, - and 1 if type represents a watchpoint. */ + Returns -1 if TYPE is unsupported, or -2 if the particular combination + of ADDR and LEN cannot be implemented. Otherwise, returns 0 if TYPE + represents a breakpoint and 1 if type represents a watchpoint. */ static int -arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len, - struct arm_linux_hw_breakpoint *p) +arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr, + int len, struct arm_linux_hw_breakpoint *p) { arm_hwbp_type hwbp_type; unsigned mask; - /* Breakpoint/watchpoint types (GDB terminology): - 0 = memory breakpoint for instructions - (not supported; done via memory write instead) - 1 = hardware breakpoint for instructions (supported) - 2 = write watchpoint (supported) - 3 = read watchpoint (supported) - 4 = access watchpoint (supported). */ - switch (type) - { - case '1': - hwbp_type = arm_hwbp_break; - break; - case '2': - hwbp_type = arm_hwbp_store; - break; - case '3': - hwbp_type = arm_hwbp_load; - break; - case '4': - hwbp_type = arm_hwbp_access; - break; - default: - /* Unsupported. */ - return -1; - } + hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type); if (hwbp_type == arm_hwbp_break) { @@ -474,17 +417,17 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len, { case 2: /* 16-bit Thumb mode breakpoint */ case 3: /* 32-bit Thumb mode breakpoint */ - mask = 0x3 << (addr & 2); + mask = 0x3; + addr &= ~1; break; case 4: /* 32-bit ARM mode breakpoint */ mask = 0xf; + addr &= ~3; break; default: /* Unsupported. */ - return -1; + return -2; } - - addr &= ~3; } else { @@ -493,17 +436,17 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len, /* Can not set watchpoints for zero or negative lengths. */ if (len <= 0) - return -1; + return -2; /* The current ptrace interface can only handle watchpoints that are a power of 2. */ if ((len & (len - 1)) != 0) - return -1; + return -2; /* Test that the range [ADDR, ADDR + LEN) fits into the largest address range covered by a watchpoint. */ aligned_addr = addr & ~(max_wp_length - 1); if (aligned_addr + max_wp_length < addr + len) - return -1; + return -2; mask = (1 << len) - 1; } @@ -526,11 +469,12 @@ struct update_registers_data static int update_registers_callback (struct inferior_list_entry *entry, void *arg) { - struct lwp_info *lwp = (struct lwp_info *) entry; + struct thread_info *thread = (struct thread_info *) entry; + struct lwp_info *lwp = get_thread_lwp (thread); struct update_registers_data *data = (struct update_registers_data *) arg; /* Only update the threads of the current process. */ - if (pid_of (lwp) == pid_of (get_thread_lwp (current_inferior))) + if (pid_of (thread) == pid_of (current_thread)) { /* The actual update is done later just before resuming the lwp, we just mark that the registers need updating. */ @@ -548,9 +492,27 @@ update_registers_callback (struct inferior_list_entry *entry, void *arg) return 0; } +static int +arm_supports_z_point_type (char z_type) +{ + switch (z_type) + { + case Z_PACKET_SW_BP: + case Z_PACKET_HW_BP: + case Z_PACKET_WRITE_WP: + case Z_PACKET_READ_WP: + case Z_PACKET_ACCESS_WP: + return 1; + default: + /* Leave the handling of sw breakpoints with the gdb client. */ + return 0; + } +} + /* Insert hardware break-/watchpoint. */ static int -arm_insert_point (char type, CORE_ADDR addr, int len) +arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, + int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; @@ -560,18 +522,18 @@ arm_insert_point (char type, CORE_ADDR addr, int len) if (watch < 0) { /* Unsupported. */ - return 1; + return watch == -1 ? 1 : -1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); - pts = proc->private->arch_private->wpts; + pts = proc->priv->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); - pts = proc->private->arch_private->bpts; + pts = proc->priv->arch_private->bpts; } for (i = 0; i < count; i++) @@ -579,7 +541,7 @@ arm_insert_point (char type, CORE_ADDR addr, int len) { struct update_registers_data data = { watch, i }; pts[i] = p; - find_inferior (&all_lwps, update_registers_callback, &data); + find_inferior (&all_threads, update_registers_callback, &data); return 0; } @@ -589,7 +551,8 @@ arm_insert_point (char type, CORE_ADDR addr, int len) /* Remove hardware break-/watchpoint. */ static int -arm_remove_point (char type, CORE_ADDR addr, int len) +arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, + int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; @@ -605,12 +568,12 @@ arm_remove_point (char type, CORE_ADDR addr, int len) if (watch) { count = arm_linux_get_hw_watchpoint_count (); - pts = proc->private->arch_private->wpts; + pts = proc->priv->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); - pts = proc->private->arch_private->bpts; + pts = proc->priv->arch_private->bpts; } for (i = 0; i < count; i++) @@ -618,7 +581,7 @@ arm_remove_point (char type, CORE_ADDR addr, int len) { struct update_registers_data data = { watch, i }; pts[i].control = arm_hwbp_control_disable (pts[i].control); - find_inferior (&all_lwps, update_registers_callback, &data); + find_inferior (&all_threads, update_registers_callback, &data); return 0; } @@ -630,7 +593,7 @@ arm_remove_point (char type, CORE_ADDR addr, int len) static int arm_stopped_by_watchpoint (void) { - struct lwp_info *lwp = get_thread_lwp (current_inferior); + struct lwp_info *lwp = get_thread_lwp (current_thread); siginfo_t siginfo; /* We must be able to set hardware watchpoints. */ @@ -639,7 +602,7 @@ arm_stopped_by_watchpoint (void) /* Retrieve siginfo. */ errno = 0; - ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &siginfo); + ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo); if (errno != 0) return 0; @@ -665,7 +628,7 @@ arm_stopped_by_watchpoint (void) static CORE_ADDR arm_stopped_data_address (void) { - struct lwp_info *lwp = get_thread_lwp (current_inferior); + struct lwp_info *lwp = get_thread_lwp (current_thread); return lwp->arch_private->stopped_data_address; } @@ -673,15 +636,15 @@ arm_stopped_data_address (void) static struct arch_process_info * arm_new_process (void) { - struct arch_process_info *info = xcalloc (1, sizeof (*info)); + struct arch_process_info *info = XCNEW (struct arch_process_info); return info; } /* Called when a new thread is detected. */ -static struct arch_lwp_info * -arm_new_thread (void) +static void +arm_new_thread (struct lwp_info *lwp) { - struct arch_lwp_info *info = xcalloc (1, sizeof (*info)); + struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); int i; for (i = 0; i < MAX_BPTS; i++) @@ -689,7 +652,51 @@ arm_new_thread (void) for (i = 0; i < MAX_WPTS; i++) info->wpts_changed[i] = 1; - return info; + lwp->arch_private = info; +} + +static void +arm_new_fork (struct process_info *parent, struct process_info *child) +{ + struct arch_process_info *parent_proc_info; + struct arch_process_info *child_proc_info; + struct lwp_info *child_lwp; + struct arch_lwp_info *child_lwp_info; + int i; + + /* These are allocated by linux_add_process. */ + gdb_assert (parent->priv != NULL + && parent->priv->arch_private != NULL); + gdb_assert (child->priv != NULL + && child->priv->arch_private != NULL); + + parent_proc_info = parent->priv->arch_private; + child_proc_info = child->priv->arch_private; + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + *child_proc_info = *parent_proc_info; + + /* Mark all the hardware breakpoints and watchpoints as changed to + make sure that the registers will be updated. */ + child_lwp = find_lwp_pid (ptid_of (child)); + child_lwp_info = child_lwp->arch_private; + for (i = 0; i < MAX_BPTS; i++) + child_lwp_info->bpts_changed[i] = 1; + for (i = 0; i < MAX_WPTS; i++) + child_lwp_info->wpts_changed[i] = 1; } /* Called when resuming a thread. @@ -697,9 +704,10 @@ arm_new_thread (void) static void arm_prepare_to_resume (struct lwp_info *lwp) { - int pid = lwpid_of (lwp); - struct process_info *proc = find_process_pid (pid_of (lwp)); - struct arch_process_info *proc_info = proc->private->arch_private; + struct thread_info *thread = get_lwp_thread (lwp); + int pid = lwpid_of (thread); + struct process_info *proc = find_process_pid (pid_of (thread)); + struct arch_process_info *proc_info = proc->priv->arch_private; struct arch_lwp_info *lwp_info = lwp->arch_private; int i; @@ -709,13 +717,15 @@ arm_prepare_to_resume (struct lwp_info *lwp) errno = 0; if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control)) - if (ptrace (PTRACE_SETHBPREGS, pid, ((i << 1) + 1), - &proc_info->bpts[i].address) < 0) + if (ptrace (PTRACE_SETHBPREGS, pid, + (PTRACE_TYPE_ARG3) ((i << 1) + 1), + &proc_info->bpts[i].address) < 0) perror_with_name ("Unexpected error setting breakpoint address"); if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control)) - if (ptrace (PTRACE_SETHBPREGS, pid, ((i << 1) + 2), - &proc_info->bpts[i].control) < 0) + if (ptrace (PTRACE_SETHBPREGS, pid, + (PTRACE_TYPE_ARG3) ((i << 1) + 2), + &proc_info->bpts[i].control) < 0) perror_with_name ("Unexpected error setting breakpoint"); lwp_info->bpts_changed[i] = 0; @@ -727,24 +737,108 @@ arm_prepare_to_resume (struct lwp_info *lwp) errno = 0; if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control)) - if (ptrace (PTRACE_SETHBPREGS, pid, -((i << 1) + 1), - &proc_info->wpts[i].address) < 0) + if (ptrace (PTRACE_SETHBPREGS, pid, + (PTRACE_TYPE_ARG3) -((i << 1) + 1), + &proc_info->wpts[i].address) < 0) perror_with_name ("Unexpected error setting watchpoint address"); if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control)) - if (ptrace (PTRACE_SETHBPREGS, pid, -((i << 1) + 2), - &proc_info->wpts[i].control) < 0) + if (ptrace (PTRACE_SETHBPREGS, pid, + (PTRACE_TYPE_ARG3) -((i << 1) + 2), + &proc_info->wpts[i].control) < 0) perror_with_name ("Unexpected error setting watchpoint"); lwp_info->wpts_changed[i] = 0; } } +/* Find the next pc for a sigreturn or rt_sigreturn syscall. In + addition, set IS_THUMB depending on whether we will return to ARM + or Thumb code. + See arm-linux.h for stack layout details. */ +static CORE_ADDR +arm_sigreturn_next_pc (struct regcache *regcache, int svc_number, + int *is_thumb) +{ + unsigned long sp; + unsigned long sp_data; + /* Offset of PC register. */ + int pc_offset = 0; + CORE_ADDR next_pc = 0; + uint32_t cpsr; + + gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn); + + collect_register_by_name (regcache, "sp", &sp); + (*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4); + + pc_offset = arm_linux_sigreturn_next_pc_offset + (sp, sp_data, svc_number, __NR_sigreturn == svc_number ? 1 : 0); + + (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4); + + /* Set IS_THUMB according the CPSR saved on the stack. */ + (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4); + *is_thumb = ((cpsr & CPSR_T) != 0); + + return next_pc; +} + +/* When PC is at a syscall instruction, return the PC of the next + instruction to be executed. */ +static CORE_ADDR +get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self) +{ + CORE_ADDR next_pc = 0; + CORE_ADDR pc = regcache_read_pc (self->regcache); + int is_thumb = arm_is_thumb_mode (); + ULONGEST svc_number = 0; + struct regcache *regcache = self->regcache; + + if (is_thumb) + { + collect_register (regcache, 7, &svc_number); + next_pc = pc + 2; + } + else + { + unsigned long this_instr; + unsigned long svc_operand; + + target_read_memory (pc, (unsigned char *) &this_instr, 4); + svc_operand = (0x00ffffff & this_instr); + + if (svc_operand) /* OABI. */ + { + svc_number = svc_operand - 0x900000; + } + else /* EABI. */ + { + collect_register (regcache, 7, &svc_number); + } + + next_pc = pc + 4; + } + + /* This is a sigreturn or sigreturn_rt syscall. */ + if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn) + { + /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so + update IS_THUMB. */ + next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb); + } + + /* Addresses for calling Thumb functions have the bit 0 set. */ + if (is_thumb) + next_pc = MAKE_THUMB_ADDR (next_pc); + + return next_pc; +} static int arm_get_hwcap (unsigned long *valp) { - unsigned char *data = alloca (8); + unsigned char *data = (unsigned char *) alloca (8); int offset = 0; while ((*the_target->read_auxv) (offset, data, 8) == 8) @@ -763,61 +857,138 @@ arm_get_hwcap (unsigned long *valp) return 0; } -static void -arm_arch_setup (void) +static const struct target_desc * +arm_read_description (void) { - int pid = lwpid_of (get_thread_lwp (current_inferior)); + int pid = lwpid_of (current_thread); + unsigned long arm_hwcap = 0; /* Query hardware watchpoint/breakpoint capabilities. */ arm_linux_init_hwbp_cap (pid); - arm_hwcap = 0; if (arm_get_hwcap (&arm_hwcap) == 0) - { - init_registers_arm (); - return; - } + return tdesc_arm; if (arm_hwcap & HWCAP_IWMMXT) - { - init_registers_arm_with_iwmmxt (); - return; - } + return tdesc_arm_with_iwmmxt; if (arm_hwcap & HWCAP_VFP) { + const struct target_desc *result; char *buf; /* NEON implies either no VFP, or VFPv3-D32. We only support it with VFP. */ if (arm_hwcap & HWCAP_NEON) - init_registers_arm_with_neon (); + result = tdesc_arm_with_neon; else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) - init_registers_arm_with_vfpv3 (); + result = tdesc_arm_with_vfpv3; else - init_registers_arm_with_vfpv2 (); + result = tdesc_arm_with_vfpv2; /* Now make sure that the kernel supports reading these registers. Support was added in 2.6.30. */ errno = 0; - buf = xmalloc (32 * 8 + 4); + buf = (char *) xmalloc (32 * 8 + 4); if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO) - { - arm_hwcap = 0; - init_registers_arm (); - } + result = tdesc_arm; + free (buf); - return; + return result; } /* The default configuration uses legacy FPA registers, probably simulated. */ - init_registers_arm (); + return tdesc_arm; } -struct regset_info target_regsets[] = { +static void +arm_arch_setup (void) +{ + int tid = lwpid_of (current_thread); + int gpregs[18]; + struct iovec iov; + + current_process ()->tdesc = arm_read_description (); + + iov.iov_base = gpregs; + iov.iov_len = sizeof (gpregs); + + /* Check if PTRACE_GETREGSET works. */ + if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0) + have_ptrace_getregset = 1; + else + have_ptrace_getregset = 0; +} + +/* Fetch the next possible PCs after the current instruction executes. */ + +static VEC (CORE_ADDR) * +arm_gdbserver_get_next_pcs (struct regcache *regcache) +{ + struct arm_get_next_pcs next_pcs_ctx; + VEC (CORE_ADDR) *next_pcs = NULL; + + arm_get_next_pcs_ctor (&next_pcs_ctx, + &get_next_pcs_ops, + /* Byte order is ignored assumed as host. */ + 0, + 0, + 1, + regcache); + + next_pcs = arm_get_next_pcs (&next_pcs_ctx); + + return next_pcs; +} + +/* Support for hardware single step. */ + +static int +arm_supports_hardware_single_step (void) +{ + return 0; +} + +/* Implementation of linux_target_ops method "get_syscall_trapinfo". */ + +static void +arm_get_syscall_trapinfo (struct regcache *regcache, int *sysno) +{ + if (arm_is_thumb_mode ()) + collect_register_by_name (regcache, "r7", sysno); + else + { + unsigned long pc; + unsigned long insn; + + collect_register_by_name (regcache, "pc", &pc); + + if ((*the_target->read_memory) (pc - 4, (unsigned char *) &insn, 4)) + *sysno = UNKNOWN_SYSCALL; + else + { + unsigned long svc_operand = (0x00ffffff & insn); + + if (svc_operand) + { + /* OABI */ + *sysno = svc_operand - 0x900000; + } + else + { + /* EABI */ + collect_register_by_name (regcache, "r7", sysno); + } + } + } +} + +/* Register sets without using PTRACE_GETREGSET. */ + +static struct regset_info arm_regsets[] = { { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4, GENERAL_REGS, arm_fill_gregset, arm_store_gregset }, @@ -827,34 +998,55 @@ struct regset_info target_regsets[] = { { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4, EXTENDED_REGS, arm_fill_vfpregset, arm_store_vfpregset }, - { 0, 0, 0, -1, -1, NULL, NULL } + NULL_REGSET }; +static struct regsets_info arm_regsets_info = + { + arm_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +static struct usrregs_info arm_usrregs_info = + { + arm_num_regs, + arm_regmap, + }; + +static struct regs_info regs_info_arm = + { + NULL, /* regset_bitmap */ + &arm_usrregs_info, + &arm_regsets_info + }; + +static const struct regs_info * +arm_regs_info (void) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + + if (have_ptrace_getregset == 1 + && (tdesc == tdesc_arm_with_neon || tdesc == tdesc_arm_with_vfpv3)) + return ®s_info_aarch32; + else + return ®s_info_arm; +} + struct linux_target_ops the_low_target = { arm_arch_setup, - arm_num_regs, - arm_regmap, - NULL, + arm_regs_info, arm_cannot_fetch_register, arm_cannot_store_register, NULL, /* fetch_register */ - arm_get_pc, - arm_set_pc, - - /* Define an ARM-mode breakpoint; we only set breakpoints in the C - library, which is most likely to be ARM. If the kernel supports - clone events, we will never insert a breakpoint, so even a Thumb - C library will work; so will mixing EABI/non-EABI gdbserver and - application. */ -#ifndef __ARM_EABI__ - (const unsigned char *) &arm_breakpoint, -#else - (const unsigned char *) &arm_eabi_breakpoint, -#endif - arm_breakpoint_len, - arm_reinsert_addr, + linux_get_pc_32bit, + linux_set_pc_32bit, + arm_breakpoint_kind_from_pc, + arm_sw_breakpoint_from_kind, + arm_gdbserver_get_next_pcs, 0, arm_breakpoint_at, + arm_supports_z_point_type, arm_insert_point, arm_remove_point, arm_stopped_by_watchpoint, @@ -864,5 +1056,30 @@ struct linux_target_ops the_low_target = { NULL, /* siginfo_fixup */ arm_new_process, arm_new_thread, + arm_new_fork, arm_prepare_to_resume, + NULL, /* process_qsupported */ + NULL, /* supports_tracepoints */ + NULL, /* get_thread_area */ + NULL, /* install_fast_tracepoint_jump_pad */ + NULL, /* emit_ops */ + NULL, /* get_min_fast_tracepoint_insn_len */ + NULL, /* supports_range_stepping */ + arm_breakpoint_kind_from_current_state, + arm_supports_hardware_single_step, + arm_get_syscall_trapinfo, }; + +void +initialize_low_arch (void) +{ + /* Initialize the Linux target descriptions. */ + init_registers_arm (); + init_registers_arm_with_iwmmxt (); + init_registers_arm_with_vfpv2 (); + init_registers_arm_with_vfpv3 (); + + initialize_low_arch_aarch32 (); + + initialize_regsets_info (&arm_regsets_info); +}