X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-arm-low.c;h=22b6be4acc6c6276f972e8d9956bd064ac704548;hb=f1d293cc58bfe5f6b507dc2351f17632df8ab677;hp=57826f17d7639d5b616b08bcec783c3821bdeb94;hpb=553cb5270f28725de86636340574644e45318fe1;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index 57826f17d7..22b6be4acc 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-2016 Free Software Foundation, Inc. + Copyright (C) 1995-2019 Free Software Foundation, Inc. This file is part of GDB. @@ -22,6 +22,8 @@ #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. @@ -33,19 +35,6 @@ #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); -extern const struct target_desc *tdesc_arm_with_vfpv3; - #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 22 #endif @@ -175,16 +164,14 @@ arm_cannot_fetch_register (int regno) static void arm_fill_wmmxregset (struct regcache *regcache, void *buf) { - int i; - - if (regcache->tdesc != tdesc_arm_with_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); } @@ -192,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 (regcache->tdesc != tdesc_arm_with_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,13 +196,19 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf) { int num; - if (regcache->tdesc == tdesc_arm_with_neon - || regcache->tdesc == tdesc_arm_with_vfpv3) + if (is_aarch32_linux_description (regcache->tdesc)) num = 32; - else if (regcache->tdesc == tdesc_arm_with_vfpv2) - num = 16; else - return; + { + 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; + } arm_fill_vfpregset_num (regcache, buf, num); } @@ -234,13 +225,19 @@ arm_store_vfpregset (struct regcache *regcache, const void *buf) { int num; - if (regcache->tdesc == tdesc_arm_with_neon - || regcache->tdesc == tdesc_arm_with_vfpv3) + if (is_aarch32_linux_description (regcache->tdesc)) num = 32; - else if (regcache->tdesc == tdesc_arm_with_vfpv2) - num = 16; else - return; + { + 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; + } arm_store_vfpregset_num (regcache, buf, num); } @@ -252,7 +249,7 @@ get_next_pcs_is_thumb (struct arm_get_next_pcs *self) return arm_is_thumb_mode (); } -/* Read memory from the inferiror. +/* Read memory from the inferior. BYTE_ORDER is ignored and there to keep compatiblity with GDB's read_memory_unsigned_integer. */ static ULONGEST @@ -262,14 +259,16 @@ get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, { ULONGEST res; - (*the_target->read_memory) (memaddr, (unsigned char *) &res, len); + res = 0; + target_read_memory (memaddr, (unsigned char *) &res, len); + 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) @@ -458,36 +457,22 @@ arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr, /* Callback to mark a watch-/breakpoint to be updated in all threads of the current process. */ -struct update_registers_data -{ - int watch; - int i; -}; - -static int -update_registers_callback (struct inferior_list_entry *entry, void *arg) +static void +update_registers_callback (thread_info *thread, int watch, int i) { - 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 (thread) == pid_of (current_thread)) - { - /* 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); - } + /* 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; - return 0; + /* 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 @@ -537,9 +522,14 @@ arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, 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_threads, 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; } @@ -577,9 +567,14 @@ arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, 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_threads, 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; } @@ -638,6 +633,14 @@ arm_new_process (void) 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 void arm_new_thread (struct lwp_info *lwp) @@ -653,6 +656,14 @@ arm_new_thread (struct lwp_info *lwp) 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) { @@ -689,7 +700,7 @@ arm_new_fork (struct process_info *parent, struct process_info *child) /* 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 = 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; @@ -763,7 +774,7 @@ arm_sigreturn_next_pc (struct regcache *regcache, int svc_number, /* Offset of PC register. */ int pc_offset = 0; CORE_ADDR next_pc = 0; - CORE_ADDR cpsr; + uint32_t cpsr; gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn); @@ -803,7 +814,7 @@ get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self) unsigned long this_instr; unsigned long svc_operand; - (*the_target->read_memory) (pc, (unsigned char *) &this_instr, 4); + target_read_memory (pc, (unsigned char *) &this_instr, 4); svc_operand = (0x00ffffff & this_instr); if (svc_operand) /* OABI. */ @@ -833,73 +844,37 @@ get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self) return next_pc; } -static int -arm_get_hwcap (unsigned long *valp) -{ - unsigned char *data = (unsigned char *) alloca (8); - int offset = 0; - - while ((*the_target->read_auxv) (offset, data, 8) == 8) - { - unsigned int *data_p = (unsigned int *)data; - if (data_p[0] == AT_HWCAP) - { - *valp = data_p[1]; - return 1; - } - - offset += 8; - } - - *valp = 0; - return 0; -} - static const struct target_desc * arm_read_description (void) { - int pid = lwpid_of (current_thread); - unsigned long arm_hwcap = 0; - - /* Query hardware watchpoint/breakpoint capabilities. */ - arm_linux_init_hwbp_cap (pid); - - 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 = (char *) xmalloc (32 * 8 + 4); - if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 - && errno == EIO) - 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 @@ -909,6 +884,9 @@ arm_arch_setup (void) 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; @@ -923,11 +901,10 @@ arm_arch_setup (void) /* Fetch the next possible PCs after the current instruction executes. */ -static VEC (CORE_ADDR) * +static std::vector 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, @@ -937,9 +914,7 @@ arm_gdbserver_get_next_pcs (struct regcache *regcache) 1, regcache); - next_pcs = arm_get_next_pcs (&next_pcs_ctx); - - return next_pcs; + return arm_get_next_pcs (&next_pcs_ctx); } /* Support for hardware single step. */ @@ -950,17 +925,49 @@ 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 }, NULL_REGSET }; @@ -991,10 +998,11 @@ 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)) + && (is_aarch32_linux_description (tdesc) + || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3)) return ®s_info_aarch32; - else - return ®s_info_arm; + + return ®s_info_arm; } struct linux_target_ops the_low_target = { @@ -1019,7 +1027,9 @@ 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 */ @@ -1030,19 +1040,13 @@ struct linux_target_ops the_low_target = { NULL, /* get_min_fast_tracepoint_insn_len */ NULL, /* supports_range_stepping */ arm_breakpoint_kind_from_current_state, - arm_supports_hardware_single_step + 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); }