X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-x86-low.c;h=feaee92bf9687b68ff258d551e293bd016e53fd9;hb=173981bc49c9e8fce9271cb47714952dbe2ec627;hp=c6a2accde90347eed42c9b6c13b1d6eba3c90ccb;hpb=14b0bc68e8a9f8e8fc3d27c64c7cf5f7c676cea1;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index c6a2accde9..feaee92bf9 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-2015 Free Software Foundation, Inc. + Copyright (C) 2002-2016 Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +25,11 @@ #include "i387-fp.h" #include "x86-low.h" #include "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 @@ -39,57 +44,8 @@ #include "ax.h" #include "nat/linux-nat.h" #include "nat/x86-linux.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/x86-linux-dregs.h" +#include "linux-x86-tdesc.h" #ifdef __x86_64__ static struct target_desc *tdesc_amd64_linux_no_xml; @@ -116,18 +72,9 @@ static const char *xmltarget_amd64_linux_no_xml = "@\ #include #include -#include +#include "nat/gdb_ptrace.h" #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 @@ -470,7 +417,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 @@ -509,7 +456,7 @@ x86_set_pc (struct regcache *regcache, CORE_ADDR pc) } } -static const unsigned char x86_breakpoint[] = { 0xCC }; +static const gdb_byte x86_breakpoint[] = { 0xCC }; #define x86_breakpoint_len 1 static int @@ -524,125 +471,6 @@ x86_breakpoint_at (CORE_ADDR pc) return 0; } - -/* Return the offset of REGNUM in the u_debugreg field of struct - user. */ - -static int -u_debugreg_offset (int regnum) -{ - return (offsetof (struct user, u_debugreg) - + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); -} - - -/* Support for debug registers. */ - -/* Get debug register REGNUM value from the LWP specified by PTID. */ - -static unsigned long -x86_linux_dr_get (ptid_t ptid, int regnum) -{ - int tid; - unsigned long value; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0); - if (errno != 0) - perror_with_name (_("Couldn't read debug register")); - - return value; -} - -/* Set debug register REGNUM to VALUE in the LWP specified by PTID. */ - -static void -x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) -{ - int tid; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value); - if (errno != 0) - perror_with_name (_("Couldn't write debug register")); -} - -/* Callback for iterate_over_lwps. Mark that our local mirror of - LWP's debug registers has been changed, and cause LWP to stop if - it isn't already. Values are written from our local mirror to - the actual debug registers immediately prior to LWP resuming. */ - -static int -update_debug_registers_callback (struct lwp_info *lwp, void *arg) -{ - lwp_set_debug_registers_changed (lwp, 1); - - if (!lwp_is_stopped (lwp)) - linux_stop_lwp (lwp); - - /* Continue the iteration. */ - return 0; -} - -/* Store ADDR in debug register REGNUM of all LWPs of the current - inferior. */ - -static void -x86_linux_dr_set_addr (int regnum, CORE_ADDR addr) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Return the address stored in the current inferior's debug register - REGNUM. */ - -static CORE_ADDR -x86_linux_dr_get_addr (int regnum) -{ - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - return x86_linux_dr_get (current_lwp_ptid (), regnum); -} - -/* Store CONTROL in the debug control registers of all LWPs of the - current inferior. */ - -static void -x86_linux_dr_set_control (unsigned long control) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Return the value stored in the current inferior's debug control - register. */ - -static unsigned long -x86_linux_dr_get_control (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_CONTROL); -} - -/* Return the value stored in the current inferior's debug status - register. */ - -static unsigned long -x86_linux_dr_get_status (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_STATUS); -} - /* Low-level function vector. */ struct x86_dr_low_type x86_dr_low = { @@ -679,9 +507,6 @@ 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: @@ -708,9 +533,6 @@ 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: @@ -758,12 +580,32 @@ x86_linux_new_process (void) return info; } -/* Called when a new thread is detected. */ +/* Target routine for linux_new_fork. */ static void -x86_linux_new_thread (struct lwp_info *lwp) +x86_linux_new_fork (struct process_info *parent, struct process_info *child) { - lwp_set_debug_registers_changed (lwp, 1); + /* 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); + + /* 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->priv->arch_private = *parent->priv->arch_private; } /* See nat/x86-dregs.h. */ @@ -775,52 +617,6 @@ x86_debug_reg_state (pid_t pid) return &proc->priv->arch_private->debug_reg_state; } - -/* Called prior to resuming a thread. Updates the thread's debug - registers if the values in our local mirror have been changed. */ - -static void -x86_linux_prepare_to_resume (struct lwp_info *lwp) -{ - ptid_t ptid = ptid_of_lwp (lwp); - int clear_status = 0; - - if (lwp_debug_registers_changed (lwp)) - { - struct x86_debug_reg_state *state - = x86_debug_reg_state (ptid_get_pid (ptid)); - int i; - - /* Prior to Linux kernel 2.6.33 commit - 72f674d203cd230426437cdcf7dd6f681dad8b0d, setting DR0-3 to - a value that did not match what was enabled in DR_CONTROL - resulted in EINVAL. To avoid this we zero DR_CONTROL before - writing address registers, only writing DR_CONTROL's actual - value once all the addresses are in place. */ - x86_linux_dr_set (ptid, DR_CONTROL, 0); - - ALL_DEBUG_ADDRESS_REGISTERS (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 - has made to its debug registers needs to be discarded - to avoid x86_stopped_data_address getting confused. */ - clear_status = 1; - } - - /* If DR_CONTROL is supposed to be zero then it's already set. */ - if (state->dr_control_mirror != 0) - x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); - - lwp_set_debug_registers_changed (lwp, 0); - } - - if (clear_status - || lwp_stop_reason (lwp) == TARGET_STOPPED_BY_WATCHPOINT) - x86_linux_dr_set (ptid, DR_STATUS, 0); -} /* When GDBSERVER is built as a 64-bit application on linux, the PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since @@ -828,399 +624,6 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp) 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 - -static void -compat_siginfo_from_siginfo (compat_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; - - 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_siginfo (siginfo_t *to, compat_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; - - 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; - } - } -} - -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; - - 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) -{ - 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->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; - } - } -} - -#endif /* __x86_64__ */ - /* Convert a native/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 @@ -1228,7 +631,7 @@ siginfo_from_compat_x32_siginfo (siginfo_t *to, INF. */ static int -x86_siginfo_fixup (siginfo_t *native, void *inf, int direction) +x86_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction) { #ifdef __x86_64__ unsigned int machine; @@ -1237,30 +640,12 @@ x86_siginfo_fixup (siginfo_t *native, void *inf, int direction) /* Is the inferior 32-bit? If so, then fixup the siginfo object. */ if (!is_64bit_tdesc ()) - { - gdb_assert (sizeof (siginfo_t) == sizeof (compat_siginfo_t)); - - 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 (native, inf, direction, + FIXUP_32); /* No fixup for native x32 GDB. */ else if (!is_elf64 && sizeof (void *) == 8) - { - gdb_assert (sizeof (siginfo_t) == sizeof (compat_x32_siginfo_t)); - - 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 (native, inf, direction, + FIXUP_X32); #endif return 0; @@ -1300,9 +685,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 * @@ -1517,29 +899,35 @@ x86_linux_update_xmltarget (void) 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 && startswith (query, "xmlRegisters=")) + 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 *p; + + for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ",")) { - use_xml = 1; - break; + if (strcmp (p, "i386") == 0) + { + use_xml = 1; + break; + } } - } - free (copy); + free (copy); + } } - x86_linux_update_xmltarget (); } @@ -1593,6 +981,31 @@ 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 *sysret) +{ + int use_64bit = register_size (regcache->tdesc, 0) == 8; + + if (use_64bit) + { + long l_sysno; + long l_sysret; + + collect_register_by_name (regcache, "orig_rax", &l_sysno); + collect_register_by_name (regcache, "rax", &l_sysret); + *sysno = (int) l_sysno; + *sysret = (int) l_sysret; + } + else + { + collect_register_by_name (regcache, "orig_eax", sysno); + collect_register_by_name (regcache, "eax", sysret); + } +} + static int x86_supports_tracepoints (void) { @@ -3404,12 +2817,62 @@ 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__ + if (tdesc == tdesc_amd64_linux || tdesc == tdesc_amd64_linux_no_xml + || tdesc == tdesc_x32_linux) + return X86_TDESC_SSE; + if (tdesc == tdesc_amd64_avx_linux || tdesc == tdesc_x32_avx_linux) + return X86_TDESC_AVX; + if (tdesc == tdesc_amd64_mpx_linux) + return X86_TDESC_MPX; + if (tdesc == tdesc_amd64_avx512_linux || tdesc == tdesc_x32_avx512_linux) + return X86_TDESC_AVX512; +#endif + + if (tdesc == tdesc_i386_mmx_linux) + return X86_TDESC_MMX; + if (tdesc == tdesc_i386_linux || tdesc == tdesc_i386_linux_no_xml) + return X86_TDESC_SSE; + if (tdesc == tdesc_i386_avx_linux) + return X86_TDESC_AVX; + if (tdesc == tdesc_i386_mpx_linux) + return X86_TDESC_MPX; + if (tdesc == tdesc_i386_avx512_linux) + return X86_TDESC_AVX512; + + return 0; +} + /* This is initialized assuming an amd64 target. x86_arch_setup will correct it for i386 or amd64 targets. */ @@ -3422,8 +2885,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, @@ -3441,6 +2904,7 @@ struct linux_target_ops the_low_target = x86_siginfo_fixup, x86_linux_new_process, x86_linux_new_thread, + x86_linux_new_fork, x86_linux_prepare_to_resume, x86_linux_process_qsupported, x86_supports_tracepoints, @@ -3449,6 +2913,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 @@ -3465,7 +2933,7 @@ initialize_low_arch (void) init_registers_x32_avx_linux (); init_registers_x32_avx512_linux (); - tdesc_amd64_linux_no_xml = xmalloc (sizeof (struct target_desc)); + tdesc_amd64_linux_no_xml = XNEW (struct target_desc); copy_target_description (tdesc_amd64_linux_no_xml, tdesc_amd64_linux); tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml; #endif @@ -3475,7 +2943,7 @@ initialize_low_arch (void) init_registers_i386_avx512_linux (); init_registers_i386_mpx_linux (); - tdesc_i386_linux_no_xml = xmalloc (sizeof (struct target_desc)); + tdesc_i386_linux_no_xml = XNEW (struct target_desc); copy_target_description (tdesc_i386_linux_no_xml, tdesc_i386_linux); tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml;