X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-x86-low.c;h=54bd2a26de8eb59973fb575445abfe9f036e0954;hb=9b30da15e72bd4e8265169bfd511272aa3875738;hp=94451da43f1b60aac38c9c84f3c54c254b377a03;hpb=42995dbda646ff0291a36f83a7f1a9f45e3fda8a;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index 94451da43f..54bd2a26de 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -1,6 +1,6 @@ /* GNU/Linux/x86-64 specific low level interface, for the remote server for GDB. - Copyright (C) 2002-2014 Free Software Foundation, Inc. + Copyright (C) 2002-2019 Free Software Foundation, Inc. This file is part of GDB. @@ -17,15 +17,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include +#include "server.h" #include #include #include -#include "server.h" #include "linux-low.h" #include "i387-fp.h" -#include "i386-low.h" -#include "i386-xstate.h" +#include "x86-low.h" +#include "gdbsupport/x86-xstate.h" +#include "nat/gdb_ptrace.h" + +#ifdef __x86_64__ +#include "nat/amd64-linux-siginfo.h" +#endif #include "gdb_proc_service.h" /* Don't include elf/common.h if linux/elf.h got included by @@ -34,61 +38,14 @@ #include "elf/common.h" #endif -#include "agent.h" +#include "gdbsupport/agent.h" #include "tdesc.h" #include "tracepoint.h" #include "ax.h" - -#ifdef __x86_64__ -/* Defined in auto-generated file amd64-linux.c. */ -void init_registers_amd64_linux (void); -extern const struct target_desc *tdesc_amd64_linux; - -/* Defined in auto-generated file amd64-avx-linux.c. */ -void init_registers_amd64_avx_linux (void); -extern const struct target_desc *tdesc_amd64_avx_linux; - -/* Defined in auto-generated file amd64-avx512-linux.c. */ -void init_registers_amd64_avx512_linux (void); -extern const struct target_desc *tdesc_amd64_avx512_linux; - -/* Defined in auto-generated file amd64-mpx-linux.c. */ -void init_registers_amd64_mpx_linux (void); -extern const struct target_desc *tdesc_amd64_mpx_linux; - -/* Defined in auto-generated file x32-linux.c. */ -void init_registers_x32_linux (void); -extern const struct target_desc *tdesc_x32_linux; - -/* Defined in auto-generated file x32-avx-linux.c. */ -void init_registers_x32_avx_linux (void); -extern const struct target_desc *tdesc_x32_avx_linux; - -/* Defined in auto-generated file x32-avx512-linux.c. */ -void init_registers_x32_avx512_linux (void); -extern const struct target_desc *tdesc_x32_avx512_linux; - -#endif - -/* Defined in auto-generated file i386-linux.c. */ -void init_registers_i386_linux (void); -extern const struct target_desc *tdesc_i386_linux; - -/* Defined in auto-generated file i386-mmx-linux.c. */ -void init_registers_i386_mmx_linux (void); -extern const struct target_desc *tdesc_i386_mmx_linux; - -/* Defined in auto-generated file i386-avx-linux.c. */ -void init_registers_i386_avx_linux (void); -extern const struct target_desc *tdesc_i386_avx_linux; - -/* Defined in auto-generated file i386-avx512-linux.c. */ -void init_registers_i386_avx512_linux (void); -extern const struct target_desc *tdesc_i386_avx512_linux; - -/* Defined in auto-generated file i386-mpx-linux.c. */ -void init_registers_i386_mpx_linux (void); -extern const struct target_desc *tdesc_i386_mpx_linux; +#include "nat/linux-nat.h" +#include "nat/x86-linux.h" +#include "nat/x86-linux-dregs.h" +#include "linux-x86-tdesc.h" #ifdef __x86_64__ static struct target_desc *tdesc_amd64_linux_no_xml; @@ -115,18 +72,8 @@ static const char *xmltarget_amd64_linux_no_xml = "@\ #include #include -#include #include -#ifndef PTRACE_GETREGSET -#define PTRACE_GETREGSET 0x4204 -#endif - -#ifndef PTRACE_SETREGSET -#define PTRACE_SETREGSET 0x4205 -#endif - - #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 25 #endif @@ -149,15 +96,7 @@ static const char *xmltarget_amd64_linux_no_xml = "@\ struct arch_process_info { - struct i386_debug_reg_state debug_reg_state; -}; - -/* Per-thread arch-specific data we want to keep. */ - -struct arch_lwp_info -{ - /* Non-zero if our copy differs from what's recorded in the thread. */ - int debug_registers_changed; + struct x86_debug_reg_state debug_reg_state; }; #ifdef __x86_64__ @@ -177,6 +116,7 @@ static /*const*/ int i386_regmap[] = /* So code below doesn't have to care, i386 or amd64. */ #define ORIG_EAX ORIG_RAX +#define REGSIZE 8 static const int x86_64_regmap[] = { @@ -192,6 +132,11 @@ static const int x86_64_regmap[] = -1, -1, -1, -1, -1, -1, -1, -1, -1, ORIG_RAX * 8, +#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE + 21 * 8, 22 * 8, +#else + -1, -1, +#endif -1, -1, -1, -1, /* MPX registers BND0 ... BND3. */ -1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */ -1, -1, -1, -1, -1, -1, -1, -1, /* xmm16 ... xmm31 (AVX512) */ @@ -202,7 +147,8 @@ static const int x86_64_regmap[] = -1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm31 (AVX512) */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, + -1 /* pkru */ }; #define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0])) @@ -222,6 +168,8 @@ static /*const*/ int i386_regmap[] = #define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0])) +#define REGSIZE 4 + #endif #ifdef __x86_64__ @@ -232,7 +180,7 @@ static /*const*/ int i386_regmap[] = static int is_64bit_tdesc (void) { - struct regcache *regcache = get_thread_regcache (current_inferior, 0); + struct regcache *regcache = get_thread_regcache (current_thread, 0); return register_size (regcache->tdesc, 0) == 8; } @@ -243,7 +191,7 @@ is_64bit_tdesc (void) /* Called by 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) { #ifdef __x86_64__ @@ -306,7 +254,7 @@ x86_get_thread_area (int lwpid, CORE_ADDR *addr) #endif { - struct lwp_info *lwp = find_lwp_pid (pid_to_ptid (lwpid)); + struct lwp_info *lwp = find_lwp_pid (ptid_t (lwpid)); struct thread_info *thr = get_lwp_thread (lwp); struct regcache *regcache = get_thread_regcache (thr, 1); unsigned int desc[4]; @@ -363,6 +311,20 @@ x86_fill_gregset (struct regcache *regcache, void *buf) for (i = 0; i < X86_64_NUM_REGS; i++) if (x86_64_regmap[i] != -1) collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]); + +#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE + { + unsigned long base; + int lwpid = lwpid_of (current_thread); + + collect_register_by_name (regcache, "fs_base", &base); + ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_FS); + + collect_register_by_name (regcache, "gs_base", &base); + ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_GS); + } +#endif + return; } @@ -375,7 +337,22 @@ x86_fill_gregset (struct regcache *regcache, void *buf) collect_register (regcache, i, ((char *) buf) + i386_regmap[i]); collect_register_by_name (regcache, "orig_eax", - ((char *) buf) + ORIG_EAX * 4); + ((char *) buf) + ORIG_EAX * REGSIZE); + +#ifdef __x86_64__ + /* Sign extend EAX value to avoid potential syscall restart + problems. + + See amd64_linux_collect_native_gregset() in gdb/amd64-linux-nat.c + for a detailed explanation. */ + if (register_size (regcache->tdesc, 0) == 4) + { + void *ptr = ((gdb_byte *) buf + + i386_regmap[find_regno (regcache->tdesc, "eax")]); + + *(int64_t *) ptr = *(int32_t *) ptr; + } +#endif } static void @@ -389,6 +366,19 @@ x86_store_gregset (struct regcache *regcache, const void *buf) for (i = 0; i < X86_64_NUM_REGS; i++) if (x86_64_regmap[i] != -1) supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]); + +#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE + { + unsigned long base; + int lwpid = lwpid_of (current_thread); + + if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0) + supply_register_by_name (regcache, "fs_base", &base); + + if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_GS) == 0) + supply_register_by_name (regcache, "gs_base", &base); + } +#endif return; } #endif @@ -397,7 +387,7 @@ x86_store_gregset (struct regcache *regcache, const void *buf) supply_register (regcache, i, ((char *) buf) + i386_regmap[i]); supply_register_by_name (regcache, "orig_eax", - ((char *) buf) + ORIG_EAX * 4); + ((char *) buf) + ORIG_EAX * REGSIZE); } static void @@ -474,7 +464,7 @@ static struct regset_info x86_regsets[] = FP_REGS, x86_fill_fpregset, x86_store_fpregset }, #endif /* HAVE_PTRACE_GETREGS */ - { 0, 0, 0, -1, -1, NULL, NULL } + NULL_REGSET }; static CORE_ADDR @@ -484,13 +474,15 @@ x86_get_pc (struct regcache *regcache) if (use_64bit) { - unsigned long pc; + uint64_t pc; + collect_register_by_name (regcache, "rip", &pc); return (CORE_ADDR) pc; } else { - unsigned int pc; + uint32_t pc; + collect_register_by_name (regcache, "eip", &pc); return (CORE_ADDR) pc; } @@ -503,17 +495,19 @@ x86_set_pc (struct regcache *regcache, CORE_ADDR pc) if (use_64bit) { - unsigned long newpc = pc; + uint64_t newpc = pc; + supply_register_by_name (regcache, "rip", &newpc); } else { - unsigned int newpc = pc; + uint32_t newpc = pc; + supply_register_by_name (regcache, "eip", &newpc); } } -static const unsigned char x86_breakpoint[] = { 0xCC }; +static const gdb_byte x86_breakpoint[] = { 0xCC }; #define x86_breakpoint_len 1 static int @@ -528,130 +522,14 @@ x86_breakpoint_at (CORE_ADDR pc) return 0; } -/* Support for debug registers. */ - -static unsigned long -x86_linux_dr_get (ptid_t ptid, int regnum) -{ - int tid; - unsigned long value; - - tid = ptid_get_lwp (ptid); - - errno = 0; - value = ptrace (PTRACE_PEEKUSER, tid, - offsetof (struct user, u_debugreg[regnum]), 0); - if (errno != 0) - error ("Couldn't read debug register"); - - return value; -} - -static void -x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) -{ - int tid; - - tid = ptid_get_lwp (ptid); - - errno = 0; - ptrace (PTRACE_POKEUSER, tid, - offsetof (struct user, u_debugreg[regnum]), value); - if (errno != 0) - error ("Couldn't write debug register"); -} - -static int -update_debug_registers_callback (struct inferior_list_entry *entry, - void *pid_p) -{ - struct thread_info *thr = (struct thread_info *) entry; - struct lwp_info *lwp = get_thread_lwp (thr); - int pid = *(int *) pid_p; - - /* Only update the threads of this process. */ - if (pid_of (thr) == pid) - { - /* The actual update is done later just before resuming the lwp, - we just mark that the registers need updating. */ - lwp->arch_private->debug_registers_changed = 1; - - /* If the lwp isn't stopped, force it to momentarily pause, so - we can update its debug registers. */ - if (!lwp->stopped) - linux_stop_lwp (lwp); - } - - return 0; -} - -/* Update the inferior's debug register REGNUM from STATE. */ - -static void -i386_dr_low_set_addr (int regnum, CORE_ADDR addr) -{ - /* Only update the threads of this process. */ - int pid = pid_of (current_inferior); - - if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR)) - fatal ("Invalid debug register %d", regnum); - - find_inferior (&all_threads, update_debug_registers_callback, &pid); -} - -/* Return the inferior's debug register REGNUM. */ - -static CORE_ADDR -i386_dr_low_get_addr (int regnum) -{ - ptid_t ptid = ptid_of (current_inferior); - - /* DR6 and DR7 are retrieved with some other way. */ - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - return x86_linux_dr_get (ptid, regnum); -} - -/* Update the inferior's DR7 debug control register from STATE. */ - -static void -i386_dr_low_set_control (unsigned long control) -{ - /* Only update the threads of this process. */ - int pid = pid_of (current_inferior); - - find_inferior (&all_threads, update_debug_registers_callback, &pid); -} - -/* Return the inferior's DR7 debug control register. */ - -static unsigned long -i386_dr_low_get_control (void) -{ - ptid_t ptid = ptid_of (current_inferior); - - return x86_linux_dr_get (ptid, DR_CONTROL); -} - -/* Get the value of the DR6 debug status register from the inferior - and record it in STATE. */ - -static unsigned long -i386_dr_low_get_status (void) -{ - ptid_t ptid = ptid_of (current_inferior); - - return x86_linux_dr_get (ptid, DR_STATUS); -} - /* Low-level function vector. */ -struct i386_dr_low_type i386_dr_low = +struct x86_dr_low_type x86_dr_low = { - i386_dr_low_set_control, - i386_dr_low_set_addr, - i386_dr_low_get_addr, - i386_dr_low_get_status, - i386_dr_low_get_control, + x86_linux_dr_set_control, + x86_linux_dr_set_addr, + x86_linux_dr_get_addr, + x86_linux_dr_get_status, + x86_linux_dr_get_control, sizeof (void *), }; @@ -680,19 +558,16 @@ x86_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, switch (type) { - case raw_bkpt_type_sw: - return insert_memory_breakpoint (bp); - case raw_bkpt_type_hw: case raw_bkpt_type_write_wp: case raw_bkpt_type_access_wp: { enum target_hw_bp_type hw_type = raw_bkpt_type_to_target_hw_bp_type (type); - struct i386_debug_reg_state *state - = &proc->private->arch_private->debug_reg_state; + struct x86_debug_reg_state *state + = &proc->priv->arch_private->debug_reg_state; - return i386_dr_insert_watchpoint (state, hw_type, addr, size); + return x86_dr_insert_watchpoint (state, hw_type, addr, size); } default: @@ -709,19 +584,16 @@ x86_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, switch (type) { - case raw_bkpt_type_sw: - return remove_memory_breakpoint (bp); - case raw_bkpt_type_hw: case raw_bkpt_type_write_wp: case raw_bkpt_type_access_wp: { enum target_hw_bp_type hw_type = raw_bkpt_type_to_target_hw_bp_type (type); - struct i386_debug_reg_state *state - = &proc->private->arch_private->debug_reg_state; + struct x86_debug_reg_state *state + = &proc->priv->arch_private->debug_reg_state; - return i386_dr_remove_watchpoint (state, hw_type, addr, size); + return x86_dr_remove_watchpoint (state, hw_type, addr, size); } default: /* Unsupported. */ @@ -733,7 +605,7 @@ static int x86_stopped_by_watchpoint (void) { struct process_info *proc = current_process (); - return i386_dr_stopped_by_watchpoint (&proc->private->arch_private->debug_reg_state); + return x86_dr_stopped_by_watchpoint (&proc->priv->arch_private->debug_reg_state); } static CORE_ADDR @@ -741,8 +613,8 @@ x86_stopped_data_address (void) { struct process_info *proc = current_process (); CORE_ADDR addr; - if (i386_dr_stopped_data_address (&proc->private->arch_private->debug_reg_state, - &addr)) + if (x86_dr_stopped_data_address (&proc->priv->arch_private->debug_reg_state, + &addr)) return addr; return 0; } @@ -752,504 +624,87 @@ x86_stopped_data_address (void) static struct arch_process_info * x86_linux_new_process (void) { - struct arch_process_info *info = xcalloc (1, sizeof (*info)); - - i386_low_init_dregs (&info->debug_reg_state); - - return info; -} - -/* Called when a new thread is detected. */ - -static struct arch_lwp_info * -x86_linux_new_thread (void) -{ - struct arch_lwp_info *info = xcalloc (1, sizeof (*info)); + struct arch_process_info *info = XCNEW (struct arch_process_info); - info->debug_registers_changed = 1; + x86_low_init_dregs (&info->debug_reg_state); return info; } -/* Called when resuming a thread. - If the debug regs have changed, update the thread's copies. */ +/* Called when a process is being deleted. */ static void -x86_linux_prepare_to_resume (struct lwp_info *lwp) +x86_linux_delete_process (struct arch_process_info *info) { - ptid_t ptid = ptid_of (get_lwp_thread (lwp)); - int clear_status = 0; - - if (lwp->arch_private->debug_registers_changed) - { - int i; - int pid = ptid_get_pid (ptid); - struct process_info *proc = find_process_pid (pid); - struct i386_debug_reg_state *state - = &proc->private->arch_private->debug_reg_state; - - for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) - if (state->dr_ref_count[i] > 0) - { - x86_linux_dr_set (ptid, i, state->dr_mirror[i]); - - /* If we're setting a watchpoint, any change the inferior - had done itself to the debug registers needs to be - discarded, otherwise, i386_dr_stopped_data_address can - get confused. */ - clear_status = 1; - } - - x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); - - lwp->arch_private->debug_registers_changed = 0; - } - - if (clear_status || lwp->stopped_by_watchpoint) - x86_linux_dr_set (ptid, DR_STATUS, 0); + xfree (info); } - -/* When GDBSERVER is built as a 64-bit application on linux, the - PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since - debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same - as debugging it with a 32-bit GDBSERVER, we do the 32-bit <-> 64-bit - conversion in-place ourselves. */ -/* These types below (compat_*) define a siginfo type that is layout - compatible with the siginfo type exported by the 32-bit userspace - support. */ - -#ifdef __x86_64__ - -typedef int compat_int_t; -typedef unsigned int compat_uptr_t; - -typedef int compat_time_t; -typedef int compat_timer_t; -typedef int compat_clock_t; - -struct compat_timeval -{ - compat_time_t tv_sec; - int tv_usec; -}; - -typedef union compat_sigval -{ - compat_int_t sival_int; - compat_uptr_t sival_ptr; -} compat_sigval_t; - -typedef struct compat_siginfo -{ - int si_signo; - int si_errno; - int si_code; - - union - { - int _pad[((128 / sizeof (int)) - 3)]; - - /* kill() */ - struct - { - unsigned int _pid; - unsigned int _uid; - } _kill; - - /* POSIX.1b timers */ - struct - { - compat_timer_t _tid; - int _overrun; - compat_sigval_t _sigval; - } _timer; - - /* POSIX.1b signals */ - struct - { - unsigned int _pid; - unsigned int _uid; - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct - { - unsigned int _pid; - unsigned int _uid; - int _status; - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct - { - unsigned int _addr; - } _sigfault; - - /* SIGPOLL */ - struct - { - int _band; - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - -/* For x32, clock_t in _sigchld is 64bit aligned at 4 bytes. */ -typedef long __attribute__ ((__aligned__ (4))) compat_x32_clock_t; - -typedef struct compat_x32_siginfo -{ - int si_signo; - int si_errno; - int si_code; - - union - { - int _pad[((128 / sizeof (int)) - 3)]; - - /* kill() */ - struct - { - unsigned int _pid; - unsigned int _uid; - } _kill; - - /* POSIX.1b timers */ - struct - { - compat_timer_t _tid; - int _overrun; - compat_sigval_t _sigval; - } _timer; - - /* POSIX.1b signals */ - struct - { - unsigned int _pid; - unsigned int _uid; - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct - { - unsigned int _pid; - unsigned int _uid; - int _status; - compat_x32_clock_t _utime; - compat_x32_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct - { - unsigned int _addr; - } _sigfault; - - /* SIGPOLL */ - struct - { - int _band; - int _fd; - } _sigpoll; - } _sifields; -} compat_x32_siginfo_t __attribute__ ((__aligned__ (8))); - -#define cpt_si_pid _sifields._kill._pid -#define cpt_si_uid _sifields._kill._uid -#define cpt_si_timerid _sifields._timer._tid -#define cpt_si_overrun _sifields._timer._overrun -#define cpt_si_status _sifields._sigchld._status -#define cpt_si_utime _sifields._sigchld._utime -#define cpt_si_stime _sifields._sigchld._stime -#define cpt_si_ptr _sifields._rt._sigval.sival_ptr -#define cpt_si_addr _sifields._sigfault._addr -#define cpt_si_band _sifields._sigpoll._band -#define cpt_si_fd _sifields._sigpoll._fd - -/* glibc at least up to 2.3.2 doesn't have si_timerid, si_overrun. - In their place is si_timer1,si_timer2. */ -#ifndef si_timerid -#define si_timerid si_timer1 -#endif -#ifndef si_overrun -#define si_overrun si_timer2 -#endif +/* Target routine for linux_new_fork. */ static void -compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from) +x86_linux_new_fork (struct process_info *parent, struct process_info *child) { - memset (to, 0, sizeof (*to)); - - to->si_signo = from->si_signo; - to->si_errno = from->si_errno; - to->si_code = from->si_code; - - if (to->si_code == SI_TIMER) - { - to->cpt_si_timerid = from->si_timerid; - to->cpt_si_overrun = from->si_overrun; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - } - else if (to->si_code == SI_USER) - { - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - } - else if (to->si_code < 0) - { - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - } - else - { - switch (to->si_signo) - { - case SIGCHLD: - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_status = from->si_status; - to->cpt_si_utime = from->si_utime; - to->cpt_si_stime = from->si_stime; - break; - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - to->cpt_si_addr = (intptr_t) from->si_addr; - break; - case SIGPOLL: - to->cpt_si_band = from->si_band; - to->cpt_si_fd = from->si_fd; - break; - default: - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - break; - } - } -} + /* 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); -static void -siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) -{ - memset (to, 0, sizeof (*to)); + /* 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. - to->si_signo = from->si_signo; - to->si_errno = from->si_errno; - to->si_code = from->si_code; + 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. */ - if (to->si_code == SI_TIMER) - { - to->si_timerid = from->cpt_si_timerid; - to->si_overrun = from->cpt_si_overrun; - to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; - } - else if (to->si_code == SI_USER) - { - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - } - else if (to->si_code < 0) - { - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; - } - else - { - switch (to->si_signo) - { - case SIGCHLD: - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_status = from->cpt_si_status; - to->si_utime = from->cpt_si_utime; - to->si_stime = from->cpt_si_stime; - break; - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - to->si_addr = (void *) (intptr_t) from->cpt_si_addr; - break; - case SIGPOLL: - to->si_band = from->cpt_si_band; - to->si_fd = from->cpt_si_fd; - break; - default: - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr; - break; - } - } + *child->priv->arch_private = *parent->priv->arch_private; } -static void -compat_x32_siginfo_from_siginfo (compat_x32_siginfo_t *to, - siginfo_t *from) -{ - memset (to, 0, sizeof (*to)); - - to->si_signo = from->si_signo; - to->si_errno = from->si_errno; - to->si_code = from->si_code; +/* See nat/x86-dregs.h. */ - if (to->si_code == SI_TIMER) - { - to->cpt_si_timerid = from->si_timerid; - to->cpt_si_overrun = from->si_overrun; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - } - else if (to->si_code == SI_USER) - { - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - } - else if (to->si_code < 0) - { - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - } - else - { - switch (to->si_signo) - { - case SIGCHLD: - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_status = from->si_status; - to->cpt_si_utime = from->si_utime; - to->cpt_si_stime = from->si_stime; - break; - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - to->cpt_si_addr = (intptr_t) from->si_addr; - break; - case SIGPOLL: - to->cpt_si_band = from->si_band; - to->cpt_si_fd = from->si_fd; - break; - default: - to->cpt_si_pid = from->si_pid; - to->cpt_si_uid = from->si_uid; - to->cpt_si_ptr = (intptr_t) from->si_ptr; - break; - } - } -} - -static void -siginfo_from_compat_x32_siginfo (siginfo_t *to, - compat_x32_siginfo_t *from) +struct x86_debug_reg_state * +x86_debug_reg_state (pid_t pid) { - memset (to, 0, sizeof (*to)); - - to->si_signo = from->si_signo; - to->si_errno = from->si_errno; - to->si_code = from->si_code; + struct process_info *proc = find_process_pid (pid); - if (to->si_code == SI_TIMER) - { - to->si_timerid = from->cpt_si_timerid; - to->si_overrun = from->cpt_si_overrun; - to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; - } - else if (to->si_code == SI_USER) - { - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - } - else if (to->si_code < 0) - { - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; - } - else - { - switch (to->si_signo) - { - case SIGCHLD: - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_status = from->cpt_si_status; - to->si_utime = from->cpt_si_utime; - to->si_stime = from->cpt_si_stime; - break; - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - to->si_addr = (void *) (intptr_t) from->cpt_si_addr; - break; - case SIGPOLL: - to->si_band = from->cpt_si_band; - to->si_fd = from->cpt_si_fd; - break; - default: - to->si_pid = from->cpt_si_pid; - to->si_uid = from->cpt_si_uid; - to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr; - break; - } - } + return &proc->priv->arch_private->debug_reg_state; } + +/* When GDBSERVER is built as a 64-bit application on linux, the + PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since + debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same + as debugging it with a 32-bit GDBSERVER, we do the 32-bit <-> 64-bit + conversion in-place ourselves. */ -#endif /* __x86_64__ */ - -/* Convert a native/host siginfo object, into/from the siginfo in the +/* Convert a ptrace/host siginfo object, into/from the siginfo in the layout of the inferiors' architecture. Returns true if any conversion was done; false otherwise. If DIRECTION is 1, then copy - from INF to NATIVE. If DIRECTION is 0, copy from NATIVE to + from INF to PTRACE. If DIRECTION is 0, copy from PTRACE to INF. */ static int -x86_siginfo_fixup (siginfo_t *native, void *inf, int direction) +x86_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction) { #ifdef __x86_64__ unsigned int machine; - int tid = lwpid_of (current_inferior); + int tid = lwpid_of (current_thread); int is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine); /* Is the inferior 32-bit? If so, then fixup the siginfo object. */ if (!is_64bit_tdesc ()) - { - if (sizeof (siginfo_t) != sizeof (compat_siginfo_t)) - fatal ("unexpected difference in siginfo"); - - if (direction == 0) - compat_siginfo_from_siginfo ((struct compat_siginfo *) inf, native); - else - siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf); - - return 1; - } + return amd64_linux_siginfo_fixup_common (ptrace, inf, direction, + FIXUP_32); /* No fixup for native x32 GDB. */ else if (!is_elf64 && sizeof (void *) == 8) - { - if (sizeof (siginfo_t) != sizeof (compat_x32_siginfo_t)) - fatal ("unexpected difference in siginfo"); - - if (direction == 0) - compat_x32_siginfo_from_siginfo ((struct compat_x32_siginfo *) inf, - native); - else - siginfo_from_compat_x32_siginfo (native, - (struct compat_x32_siginfo *) inf); - - return 1; - } + return amd64_linux_siginfo_fixup_common (ptrace, inf, direction, + FIXUP_X32); #endif return 0; @@ -1289,9 +744,6 @@ int have_ptrace_getfpxregs = #endif ; -/* Does the current host support PTRACE_GETREGSET? */ -static int have_ptrace_getregset = -1; - /* Get Linux/x86 target description from running target. */ static const struct target_desc * @@ -1304,7 +756,7 @@ x86_linux_read_description (void) static uint64_t xcr0; struct regset_info *regset; - tid = lwpid_of (current_inferior); + tid = lwpid_of (current_thread); is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine); @@ -1327,7 +779,7 @@ x86_linux_read_description (void) { have_ptrace_getfpxregs = 0; have_ptrace_getregset = 0; - return tdesc_i386_mmx_linux; + return i386_linux_read_description (X86_XSTATE_X87); } else have_ptrace_getfpxregs = 1; @@ -1336,7 +788,7 @@ x86_linux_read_description (void) if (!use_xml) { - x86_xcr0 = I386_XSTATE_SSE_MASK; + x86_xcr0 = X86_XSTATE_SSE_MASK; /* Don't use XML. */ #ifdef __x86_64__ @@ -1349,7 +801,7 @@ x86_linux_read_description (void) if (have_ptrace_getregset == -1) { - uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))]; + uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))]; struct iovec iov; iov.iov_base = xstateregs; @@ -1371,7 +823,7 @@ x86_linux_read_description (void) for (regset = x86_regsets; regset->fill_function != NULL; regset++) if (regset->get_request == PTRACE_GETREGSET) - regset->size = I386_XSTATE_SIZE (xcr0); + regset->size = X86_XSTATE_SIZE (xcr0); else if (regset->type != GENERAL_REGS) regset->size = 0; } @@ -1379,7 +831,7 @@ x86_linux_read_description (void) /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ xcr0_features = (have_ptrace_getregset - && (xcr0 & I386_XSTATE_ALL_MASK)); + && (xcr0 & X86_XSTATE_ALL_MASK)); if (xcr0_features) x86_xcr0 = xcr0; @@ -1387,148 +839,95 @@ x86_linux_read_description (void) if (machine == EM_X86_64) { #ifdef __x86_64__ - if (is_elf64) - { - if (xcr0_features) - { - switch (xcr0 & I386_XSTATE_ALL_MASK) - { - case I386_XSTATE_AVX512_MASK: - return tdesc_amd64_avx512_linux; - - case I386_XSTATE_MPX_MASK: - return tdesc_amd64_mpx_linux; + const target_desc *tdesc = NULL; - case I386_XSTATE_AVX_MASK: - return tdesc_amd64_avx_linux; - - default: - return tdesc_amd64_linux; - } - } - else - return tdesc_amd64_linux; - } - else + if (xcr0_features) { - if (xcr0_features) - { - switch (xcr0 & I386_XSTATE_ALL_MASK) - { - case I386_XSTATE_AVX512_MASK: - return tdesc_x32_avx512_linux; - - case I386_XSTATE_MPX_MASK: /* No MPX on x32. */ - case I386_XSTATE_AVX_MASK: - return tdesc_x32_avx_linux; - - default: - return tdesc_x32_linux; - } - } - else - return tdesc_x32_linux; + tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK, + !is_elf64); } + + if (tdesc == NULL) + tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64); + return tdesc; #endif } else { - if (xcr0_features) - { - switch (xcr0 & I386_XSTATE_ALL_MASK) - { - case (I386_XSTATE_AVX512_MASK): - return tdesc_i386_avx512_linux; + const target_desc *tdesc = NULL; - case (I386_XSTATE_MPX_MASK): - return tdesc_i386_mpx_linux; + if (xcr0_features) + tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK); - case (I386_XSTATE_AVX_MASK): - return tdesc_i386_avx_linux; + if (tdesc == NULL) + tdesc = i386_linux_read_description (X86_XSTATE_SSE); - default: - return tdesc_i386_linux; - } - } - else - return tdesc_i386_linux; + return tdesc; } gdb_assert_not_reached ("failed to return tdesc"); } -/* Callback for find_inferior. Stops iteration when a thread with a - given PID is found. */ - -static int -same_process_callback (struct inferior_list_entry *entry, void *data) -{ - int pid = *(int *) data; - - return (ptid_get_pid (entry->id) == pid); -} - -/* Callback for for_each_inferior. Calls the arch_setup routine for - each process. */ - -static void -x86_arch_setup_process_callback (struct inferior_list_entry *entry) -{ - int pid = ptid_get_pid (entry->id); - - /* Look up any thread of this processes. */ - current_inferior - = (struct thread_info *) find_inferior (&all_threads, - same_process_callback, &pid); - - the_low_target.arch_setup (); -} - /* Update all the target description of all processes; a new GDB connected, and it may or not support xml target descriptions. */ static void x86_linux_update_xmltarget (void) { - struct thread_info *save_inferior = current_inferior; + struct thread_info *saved_thread = current_thread; /* Before changing the register cache's internal layout, flush the contents of the current valid caches back to the threads, and release the current regcache objects. */ regcache_release (); - for_each_inferior (&all_processes, x86_arch_setup_process_callback); + for_each_process ([] (process_info *proc) { + int pid = proc->pid; + + /* Look up any thread of this process. */ + current_thread = find_any_thread_of_pid (pid); - current_inferior = save_inferior; + the_low_target.arch_setup (); + }); + + current_thread = saved_thread; } /* Process qSupported query, "xmlRegisters=". Update the buffer size for PTRACE_GETREGSET. */ static void -x86_linux_process_qsupported (const char *query) +x86_linux_process_qsupported (char **features, int count) { + int i; + /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters=" with "i386" in qSupported query, it supports x86 XML target descriptions. */ use_xml = 0; - if (query != NULL && strncmp (query, "xmlRegisters=", 13) == 0) + for (i = 0; i < count; i++) { - char *copy = xstrdup (query + 13); - char *p; + const char *feature = features[i]; - for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ",")) + if (startswith (feature, "xmlRegisters=")) { - if (strcmp (p, "i386") == 0) + char *copy = xstrdup (feature + 13); + + char *saveptr; + for (char *p = strtok_r (copy, ",", &saveptr); + p != NULL; + p = strtok_r (NULL, ",", &saveptr)) { - use_xml = 1; - break; + if (strcmp (p, "i386") == 0) + { + use_xml = 1; + break; + } } - } - free (copy); + free (copy); + } } - x86_linux_update_xmltarget (); } @@ -1582,6 +981,25 @@ x86_arch_setup (void) current_process ()->tdesc = x86_linux_read_description (); } +/* Fill *SYSNO and *SYSRET with the syscall nr trapped and the syscall return + code. This should only be called if LWP got a SYSCALL_SIGTRAP. */ + +static void +x86_get_syscall_trapinfo (struct regcache *regcache, int *sysno) +{ + int use_64bit = register_size (regcache->tdesc, 0) == 8; + + if (use_64bit) + { + long l_sysno; + + collect_register_by_name (regcache, "orig_rax", &l_sysno); + *sysno = (int) l_sysno; + } + else + collect_register_by_name (regcache, "orig_eax", sysno); +} + static int x86_supports_tracepoints (void) { @@ -1591,12 +1009,12 @@ x86_supports_tracepoints (void) static void append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf) { - write_inferior_memory (*to, buf, len); + target_write_memory (*to, buf, len); *to += len; } static int -push_opcode (unsigned char *buf, char *op) +push_opcode (unsigned char *buf, const char *op) { unsigned char *buf_org = buf; @@ -1664,10 +1082,10 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, buf[i++] = 0x41; buf[i++] = 0x51; /* push %r9 */ buf[i++] = 0x41; buf[i++] = 0x50; /* push %r8 */ buf[i++] = 0x9c; /* pushfq */ - buf[i++] = 0x48; /* movl ,%rdi */ + buf[i++] = 0x48; /* movabs ,%rdi */ buf[i++] = 0xbf; - *((unsigned long *)(buf + i)) = (unsigned long) tpaddr; - i += sizeof (unsigned long); + memcpy (buf + i, &tpaddr, 8); + i += 8; buf[i++] = 0x57; /* push %rdi */ append_insns (&buildaddr, i, buf); @@ -1969,7 +1387,7 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, offset = *jump_entry - (*trampoline + sizeof (jump_insn)); memcpy (buf, jump_insn, sizeof (jump_insn)); memcpy (buf + 1, &offset, 4); - write_inferior_memory (*trampoline, buf, sizeof (jump_insn)); + target_write_memory (*trampoline, buf, sizeof (jump_insn)); /* Use a 16-bit relative jump instruction to jump to the trampoline. */ offset = (*trampoline - (tpaddr + sizeof (small_jump_insn))) & 0xffff; @@ -2065,7 +1483,7 @@ x86_get_min_fast_tracepoint_insn_len (void) mention that something has gone awry. */ if (!warned_about_fast_tracepoints) { - warning ("4-byte fast tracepoints not available; %s\n", errbuf); + warning ("4-byte fast tracepoints not available; %s", errbuf); warned_about_fast_tracepoints = 1; } return 5; @@ -2364,7 +1782,7 @@ amd64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) } memcpy (buf, &diff, sizeof (int)); - write_inferior_memory (from, buf, sizeof (int)); + target_write_memory (from, buf, sizeof (int)); } static void @@ -2414,6 +1832,8 @@ amd64_emit_call (CORE_ADDR fn) else { int offset32 = offset64; /* we know we can't overflow here. */ + + buf[i++] = 0xe8; /* call */ memcpy (buf + i, &offset32, 4); i += 4; } @@ -2980,7 +2400,7 @@ i386_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) } memcpy (buf, &diff, sizeof (int)); - write_inferior_memory (from, buf, sizeof (int)); + target_write_memory (from, buf, sizeof (int)); } static void @@ -3393,12 +2813,46 @@ x86_emit_ops (void) return &i386_emit_ops; } +/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */ + +static const gdb_byte * +x86_sw_breakpoint_from_kind (int kind, int *size) +{ + *size = x86_breakpoint_len; + return x86_breakpoint; +} + static int x86_supports_range_stepping (void) { return 1; } +/* Implementation of linux_target_ops method "supports_hardware_single_step". + */ + +static int +x86_supports_hardware_single_step (void) +{ + return 1; +} + +static int +x86_get_ipa_tdesc_idx (void) +{ + struct regcache *regcache = get_thread_regcache (current_thread, 0); + const struct target_desc *tdesc = regcache->tdesc; + +#ifdef __x86_64__ + return amd64_get_ipa_tdesc_idx (tdesc); +#endif + + if (tdesc == tdesc_i386_linux_no_xml) + return X86_TDESC_SSE; + + return i386_get_ipa_tdesc_idx (tdesc); +} + /* This is initialized assuming an amd64 target. x86_arch_setup will correct it for i386 or amd64 targets. */ @@ -3411,8 +2865,8 @@ struct linux_target_ops the_low_target = NULL, /* fetch_register */ x86_get_pc, x86_set_pc, - x86_breakpoint, - x86_breakpoint_len, + NULL, /* breakpoint_kind_from_pc */ + x86_sw_breakpoint_from_kind, NULL, 1, x86_breakpoint_at, @@ -3429,7 +2883,10 @@ struct linux_target_ops the_low_target = /* need to fix up i386 siginfo if host is amd64 */ x86_siginfo_fixup, x86_linux_new_process, + x86_linux_delete_process, x86_linux_new_thread, + x86_linux_delete_thread, + x86_linux_new_fork, x86_linux_prepare_to_resume, x86_linux_process_qsupported, x86_supports_tracepoints, @@ -3438,6 +2895,10 @@ struct linux_target_ops the_low_target = x86_emit_ops, x86_get_min_fast_tracepoint_insn_len, x86_supports_range_stepping, + NULL, /* breakpoint_kind_from_current_state */ + x86_supports_hardware_single_step, + x86_get_syscall_trapinfo, + x86_get_ipa_tdesc_idx, }; void @@ -3445,27 +2906,16 @@ initialize_low_arch (void) { /* Initialize the Linux target descriptions. */ #ifdef __x86_64__ - init_registers_amd64_linux (); - init_registers_amd64_avx_linux (); - init_registers_amd64_avx512_linux (); - init_registers_amd64_mpx_linux (); - - init_registers_x32_linux (); - init_registers_x32_avx_linux (); - init_registers_x32_avx512_linux (); - - tdesc_amd64_linux_no_xml = xmalloc (sizeof (struct target_desc)); - copy_target_description (tdesc_amd64_linux_no_xml, tdesc_amd64_linux); + tdesc_amd64_linux_no_xml = allocate_target_description (); + copy_target_description (tdesc_amd64_linux_no_xml, + amd64_linux_read_description (X86_XSTATE_SSE_MASK, + false)); tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml; #endif - init_registers_i386_linux (); - init_registers_i386_mmx_linux (); - init_registers_i386_avx_linux (); - init_registers_i386_avx512_linux (); - init_registers_i386_mpx_linux (); - - tdesc_i386_linux_no_xml = xmalloc (sizeof (struct target_desc)); - copy_target_description (tdesc_i386_linux_no_xml, tdesc_i386_linux); + + tdesc_i386_linux_no_xml = allocate_target_description (); + copy_target_description (tdesc_i386_linux_no_xml, + i386_linux_read_description (X86_XSTATE_SSE_MASK)); tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml; initialize_regsets_info (&x86_regsets_info);