X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-arm-low.c;h=9f046c098bf1cc067919fa26aa260d942fbdf870;hb=e8319fde715960466aca2461c74cec8907abd391;hp=fb6ff6815ff1271d4ae6bbf6372a7141770bffe8;hpb=87ce2a04c53fa7bb4fff50a41e45c0b29af06dae;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index fb6ff6815f..9f046c098b 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-2014 Free Software Foundation, Inc. + Copyright (C) 1995-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -18,30 +18,22 @@ #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 "linux-aarch32-tdesc.h" +#include "linux-arm-tdesc.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 - -/* 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); -extern const struct target_desc *tdesc_arm_with_vfpv3; - -void init_registers_arm_with_neon (void); -extern const struct target_desc *tdesc_arm_with_neon; +#include #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 22 @@ -116,8 +108,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 @@ -138,6 +128,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) { @@ -150,43 +161,17 @@ 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 (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT) return; - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8); /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */ - for (i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) collect_register (regcache, arm_num_regs + i + 16, (char *) buf + 16 * 8 + i * 4); } @@ -194,16 +179,14 @@ arm_fill_wmmxregset (struct regcache *regcache, void *buf) static void arm_store_wmmxregset (struct regcache *regcache, const void *buf) { - int i; - - if (!(arm_hwcap & HWCAP_IWMMXT)) + if (arm_linux_get_tdesc_fp_type (regcache->tdesc) != ARM_FP_TYPE_IWMMXT) return; - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8); /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */ - for (i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) supply_register (regcache, arm_num_regs + i + 16, (char *) buf + 16 * 8 + i * 4); } @@ -211,130 +194,81 @@ 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 (is_aarch32_linux_description (regcache->tdesc)) num = 32; else - num = 16; + { + arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc); + + if (fp_type == ARM_FP_TYPE_VFPV3) + num = 32; + else if (fp_type == ARM_FP_TYPE_VFPV2) + num = 16; + else + return; + } - base = find_regno (regcache->tdesc, "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; - - if (!(arm_hwcap & HWCAP_VFP)) - return; + int num; - if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) + if (is_aarch32_linux_description (regcache->tdesc)) num = 32; else - num = 16; - - base = find_regno (regcache->tdesc, "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); -} + { + arm_fp_type fp_type = arm_linux_get_tdesc_fp_type (regcache->tdesc); -extern int debug_threads; + if (fp_type == ARM_FP_TYPE_VFPV3) + num = 32; + else if (fp_type == ARM_FP_TYPE_VFPV2) + num = 16; + else + return; + } -static CORE_ADDR -arm_get_pc (struct regcache *regcache) -{ - unsigned long pc; - collect_register_by_name (regcache, "pc", &pc); - if (debug_threads) - debug_printf ("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 inferior. + 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; - - collect_register_by_name (regcache, "cpsr", &cpsr); - - 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; - } + ULONGEST res; - return 0; -} + res = 0; + target_read_memory (memaddr, (unsigned char *) &res, len); -/* 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) @@ -439,43 +373,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, 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) { @@ -527,40 +457,45 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len, /* Callback to mark a watch-/breakpoint to be updated in all threads of the current process. */ -struct update_registers_data +static void +update_registers_callback (thread_info *thread, int watch, int i) { - int watch; - int i; -}; + struct lwp_info *lwp = get_thread_lwp (thread); + + /* The actual update is done later just before resuming the lwp, + we just mark that the registers need updating. */ + if (watch) + lwp->arch_private->wpts_changed[i] = 1; + else + lwp->arch_private->bpts_changed[i] = 1; + + /* If the lwp isn't stopped, force it to momentarily pause, so + we can update its breakpoint registers. */ + if (!lwp->stopped) + linux_stop_lwp (lwp); +} static int -update_registers_callback (struct inferior_list_entry *entry, void *arg) +arm_supports_z_point_type (char z_type) { - struct lwp_info *lwp = (struct lwp_info *) entry; - 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))) + switch (z_type) { - /* The actual update is done later just before resuming the lwp, - we just mark that the registers need updating. */ - if (data->watch) - lwp->arch_private->wpts_changed[data->i] = 1; - else - lwp->arch_private->bpts_changed[data->i] = 1; - - /* If the lwp isn't stopped, force it to momentarily pause, so - we can update its breakpoint registers. */ - if (!lwp->stopped) - linux_stop_lwp (lwp); + 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; } - - 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; @@ -576,20 +511,25 @@ arm_insert_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++) if (!arm_hwbp_control_is_enabled (pts[i].control)) { - struct update_registers_data data = { watch, i }; pts[i] = p; - find_inferior (&all_lwps, update_registers_callback, &data); + + /* Only update the threads of the current process. */ + for_each_thread (current_thread->id.pid (), [&] (thread_info *thread) + { + update_registers_callback (thread, watch, i); + }); + return 0; } @@ -599,7 +539,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; @@ -615,20 +556,25 @@ 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++) if (arm_linux_hw_breakpoint_equal (&p, pts + i)) { - 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); + + /* Only update the threads of the current process. */ + for_each_thread (current_thread->id.pid (), [&] (thread_info *thread) + { + update_registers_callback (thread, watch, i); + }); + return 0; } @@ -640,7 +586,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. */ @@ -649,7 +595,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; @@ -675,7 +621,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; } @@ -683,15 +629,23 @@ 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 process is being deleted. */ + +static void +arm_delete_process (struct arch_process_info *info) +{ + xfree (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++) @@ -699,7 +653,59 @@ arm_new_thread (void) for (i = 0; i < MAX_WPTS; i++) info->wpts_changed[i] = 1; - return info; + lwp->arch_private = info; +} + +/* Function to call when a thread is being deleted. */ + +static void +arm_delete_thread (struct arch_lwp_info *arch_lwp) +{ + xfree (arch_lwp); +} + +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_t (child->pid)); + 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. @@ -707,9 +713,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; @@ -754,95 +761,215 @@ arm_prepare_to_resume (struct lwp_info *lwp) } } +/* 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); -static int -arm_get_hwcap (unsigned long *valp) + 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) { - unsigned char *data = alloca (8); - int offset = 0; + 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; - while ((*the_target->read_auxv) (offset, data, 8) == 8) + if (is_thumb) + { + collect_register (regcache, 7, &svc_number); + next_pc = pc + 2; + } + else { - unsigned int *data_p = (unsigned int *)data; - if (data_p[0] == AT_HWCAP) + 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. */ { - *valp = data_p[1]; - return 1; + svc_number = svc_operand - 0x900000; + } + else /* EABI. */ + { + collect_register (regcache, 7, &svc_number); } - offset += 8; + next_pc = pc + 4; } - *valp = 0; - return 0; + /* 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 const struct target_desc * arm_read_description (void) { - int pid = lwpid_of (get_thread_lwp (current_inferior)); - - /* Query hardware watchpoint/breakpoint capabilities. */ - arm_linux_init_hwbp_cap (pid); - - arm_hwcap = 0; - if (arm_get_hwcap (&arm_hwcap) == 0) - return tdesc_arm; + unsigned long arm_hwcap = linux_get_hwcap (4); if (arm_hwcap & HWCAP_IWMMXT) - return tdesc_arm_with_iwmmxt; + return arm_linux_read_description (ARM_FP_TYPE_IWMMXT); if (arm_hwcap & HWCAP_VFP) { - const struct target_desc *result; - char *buf; + /* Make sure that the kernel supports reading VFP registers. Support was + added in 2.6.30. */ + int pid = lwpid_of (current_thread); + errno = 0; + char *buf = (char *) alloca (ARM_VFP3_REGS_SIZE); + if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO) + return arm_linux_read_description (ARM_FP_TYPE_NONE); /* NEON implies either no VFP, or VFPv3-D32. We only support it with VFP. */ if (arm_hwcap & HWCAP_NEON) - result = tdesc_arm_with_neon; + return aarch32_linux_read_description (); else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) - result = tdesc_arm_with_vfpv3; + return arm_linux_read_description (ARM_FP_TYPE_VFPV3); else - 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); - if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 - && errno == EIO) - { - arm_hwcap = 0; - result = tdesc_arm; - } - free (buf); - - return result; + return arm_linux_read_description (ARM_FP_TYPE_VFPV2); } /* The default configuration uses legacy FPA registers, probably simulated. */ - return tdesc_arm; + return arm_linux_read_description (ARM_FP_TYPE_NONE); } static void arm_arch_setup (void) { + int tid = lwpid_of (current_thread); + int gpregs[18]; + struct iovec iov; + + /* Query hardware watchpoint/breakpoint capabilities. */ + arm_linux_init_hwbp_cap (tid); + 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 std::vector +arm_gdbserver_get_next_pcs (struct regcache *regcache) +{ + struct arm_get_next_pcs next_pcs_ctx; + + arm_get_next_pcs_ctor (&next_pcs_ctx, + &get_next_pcs_ops, + /* Byte order is ignored assumed as host. */ + 0, + 0, + 1, + regcache); + + return arm_get_next_pcs (&next_pcs_ctx); } +/* 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, + { PTRACE_GETREGS, PTRACE_SETREGS, 0, + ARM_CORE_REGS_SIZE + ARM_INT_REGISTER_SIZE, GENERAL_REGS, arm_fill_gregset, arm_store_gregset }, - { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4, - EXTENDED_REGS, + { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, IWMMXT_REGS_SIZE, EXTENDED_REGS, arm_fill_wmmxregset, arm_store_wmmxregset }, - { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4, - EXTENDED_REGS, + { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, ARM_VFP3_REGS_SIZE, EXTENDED_REGS, arm_fill_vfpregset, arm_store_vfpregset }, - { 0, 0, 0, -1, -1, NULL, NULL } + NULL_REGSET }; static struct regsets_info arm_regsets_info = @@ -858,7 +985,7 @@ static struct usrregs_info arm_usrregs_info = arm_regmap, }; -static struct regs_info regs_info = +static struct regs_info regs_info_arm = { NULL, /* regset_bitmap */ &arm_usrregs_info, @@ -868,7 +995,14 @@ static struct regs_info regs_info = static const struct regs_info * arm_regs_info (void) { - return ®s_info; + const struct target_desc *tdesc = current_process ()->tdesc; + + if (have_ptrace_getregset == 1 + && (is_aarch32_linux_description (tdesc) + || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3)) + return ®s_info_aarch32; + + return ®s_info_arm; } struct linux_target_ops the_low_target = { @@ -877,23 +1011,14 @@ struct linux_target_ops the_low_target = { 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, @@ -902,19 +1027,26 @@ struct linux_target_ops the_low_target = { NULL, /* supply_ptrace_register */ NULL, /* siginfo_fixup */ arm_new_process, + arm_delete_process, arm_new_thread, + arm_delete_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 (); - init_registers_arm_with_neon (); - + initialize_low_arch_aarch32 (); initialize_regsets_info (&arm_regsets_info); }