X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fx86-linux-nat.c;h=fe85358fa263adfee386b63d7950d6cb77d46f93;hb=5ceb1f4d804b09cb8767ca704c2ba41e50a2bcdb;hp=9eb82c5cdee9220aaa51863699349890d654f1ee;hpb=2b95d4403883ba22687ecb81520855204348a7fb;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index 9eb82c5cde..fe85358fa2 100644 --- a/gdb/x86-linux-nat.c +++ b/gdb/x86-linux-nat.c @@ -1,6 +1,6 @@ /* Native-dependent code for GNU/Linux x86 (i386 and x86-64). - Copyright (C) 1999-2015 Free Software Foundation, Inc. + Copyright (C) 1999-2019 Free Software Foundation, Inc. This file is part of GDB. @@ -21,13 +21,12 @@ #include "inferior.h" #include "elf/common.h" #include "gdb_proc_service.h" -#include +#include "nat/gdb_ptrace.h" #include #include #include #include "x86-nat.h" -#include "linux-nat.h" #ifndef __x86_64__ #include "i386-linux-nat.h" #endif @@ -36,210 +35,17 @@ #ifdef __x86_64__ #include "amd64-linux-tdep.h" #endif -#include "x86-xstate.h" +#include "common/x86-xstate.h" #include "nat/linux-btrace.h" #include "nat/linux-nat.h" #include "nat/x86-linux.h" +#include "nat/x86-linux-dregs.h" +#include "nat/linux-ptrace.h" -/* Per-thread arch-specific data we want to keep. */ +/* linux_nat_target::low_new_fork implementation. */ -struct arch_lwp_info -{ - /* Non-zero if our copy differs from what's recorded in the thread. */ - int debug_registers_changed; -}; - -/* Does the current host support PTRACE_GETREGSET? */ -int have_ptrace_getregset = -1; - - -/* 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")); -} - -/* 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); -} - -/* 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); -} - -/* 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 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); -} - -/* 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); -} - -/* Update the thread's debug registers if the values in our local - mirror have been changed. */ - -static void -x86_linux_update_debug_registers (struct lwp_info *lwp) -{ - ptid_t ptid = ptid_of_lwp (lwp); - int clear_status = 0; - - gdb_assert (lwp_is_stopped (lwp)); - - 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); -} - -/* Called prior to resuming a thread. */ - -static void -x86_linux_prepare_to_resume (struct lwp_info *lwp) -{ - x86_linux_update_debug_registers (lwp); -} - -/* Called when a new thread is detected. */ - -static void -x86_linux_new_thread (struct lwp_info *lwp) -{ - lwp_set_debug_registers_changed (lwp, 1); -} - - -/* linux_nat_new_fork hook. */ - -static void -x86_linux_new_fork (struct lwp_info *parent, pid_t child_pid) +void +x86_linux_nat_target::low_new_fork (struct lwp_info *parent, pid_t child_pid) { pid_t parent_pid; struct x86_debug_reg_state *parent_state; @@ -264,21 +70,22 @@ x86_linux_new_fork (struct lwp_info *parent, pid_t child_pid) in the end before detaching the forked off process, thus making this compatible with older Linux kernels too. */ - parent_pid = ptid_get_pid (parent->ptid); + parent_pid = parent->ptid.pid (); parent_state = x86_debug_reg_state (parent_pid); child_state = x86_debug_reg_state (child_pid); *child_state = *parent_state; } -static void (*super_post_startup_inferior) (struct target_ops *self, - ptid_t ptid); +x86_linux_nat_target::~x86_linux_nat_target () +{ +} -static void -x86_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid) +void +x86_linux_nat_target::post_startup_inferior (ptid_t ptid) { x86_cleanup_dregs (); - super_post_startup_inferior (self, ptid); + linux_nat_target::post_startup_inferior (ptid); } #ifdef __x86_64__ @@ -295,8 +102,8 @@ x86_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid) /* Get Linux/x86 target description from running target. */ -static const struct target_desc * -x86_linux_read_description (struct target_ops *ops) +const struct target_desc * +x86_linux_nat_target::read_description () { int tid; int is_64bit = 0; @@ -307,9 +114,9 @@ x86_linux_read_description (struct target_ops *ops) uint64_t xcr0_features_bits; /* GNU/Linux LWP ID's are process ID's. */ - tid = ptid_get_lwp (inferior_ptid); + tid = inferior_ptid.lwp (); if (tid == 0) - tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ + tid = inferior_ptid.pid (); /* Not a threaded program. */ #ifdef __x86_64__ { @@ -345,13 +152,13 @@ x86_linux_read_description (struct target_ops *ops) if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0) { have_ptrace_getfpxregs = 0; - have_ptrace_getregset = 0; - return tdesc_i386_mmx_linux; + have_ptrace_getregset = TRIBOOL_FALSE; + return i386_linux_read_description (X86_XSTATE_X87_MASK); } } #endif - if (have_ptrace_getregset == -1) + if (have_ptrace_getregset == TRIBOOL_UNKNOWN) { uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))]; struct iovec iov; @@ -362,10 +169,10 @@ x86_linux_read_description (struct target_ops *ops) /* Check if PTRACE_GETREGSET works. */ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, &iov) < 0) - have_ptrace_getregset = 0; + have_ptrace_getregset = TRIBOOL_FALSE; else { - have_ptrace_getregset = 1; + have_ptrace_getregset = TRIBOOL_TRUE; /* Get XCR0 from XSAVE extended state. */ xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET @@ -377,7 +184,7 @@ x86_linux_read_description (struct target_ops *ops) PTRACE_GETREGSET is not available then set xcr0_features_bits to zero so that the "no-features" descriptions are returned by the switches below. */ - if (have_ptrace_getregset) + if (have_ptrace_getregset == TRIBOOL_TRUE) xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK; else xcr0_features_bits = 0; @@ -385,46 +192,18 @@ x86_linux_read_description (struct target_ops *ops) if (is_64bit) { #ifdef __x86_64__ - switch (xcr0_features_bits) - { - case X86_XSTATE_MPX_AVX512_MASK: - case X86_XSTATE_AVX512_MASK: - if (is_x32) - return tdesc_x32_avx512_linux; - else - return tdesc_amd64_avx512_linux; - case X86_XSTATE_MPX_MASK: - if (is_x32) - return tdesc_x32_avx_linux; /* No MPX on x32 using AVX. */ - else - return tdesc_amd64_mpx_linux; - case X86_XSTATE_AVX_MASK: - if (is_x32) - return tdesc_x32_avx_linux; - else - return tdesc_amd64_avx_linux; - default: - if (is_x32) - return tdesc_x32_linux; - else - return tdesc_amd64_linux; - } + return amd64_linux_read_description (xcr0_features_bits, is_x32); #endif } else { - switch (xcr0_features_bits) - { - case X86_XSTATE_MPX_AVX512_MASK: - case X86_XSTATE_AVX512_MASK: - return tdesc_i386_avx512_linux; - case X86_XSTATE_MPX_MASK: - return tdesc_i386_mpx_linux; - case X86_XSTATE_AVX_MASK: - return tdesc_i386_avx_linux; - default: - return tdesc_i386_linux; - } + const struct target_desc * tdesc + = i386_linux_read_description (xcr0_features_bits); + + if (tdesc == NULL) + tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK); + + return tdesc; } gdb_assert_not_reached ("failed to return tdesc"); @@ -433,34 +212,28 @@ x86_linux_read_description (struct target_ops *ops) /* Enable branch tracing. */ -static struct btrace_target_info * -x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid, - const struct btrace_config *conf) +struct btrace_target_info * +x86_linux_nat_target::enable_btrace (ptid_t ptid, + const struct btrace_config *conf) { - struct btrace_target_info *tinfo; - struct gdbarch *gdbarch; - - errno = 0; - tinfo = linux_enable_btrace (ptid, conf); - - if (tinfo == NULL) - error (_("Could not enable branch tracing for %s: %s."), - target_pid_to_str (ptid), safe_strerror (errno)); - - /* Fill in the size of a pointer in bits. */ - if (tinfo->ptr_bits == 0) + struct btrace_target_info *tinfo = nullptr; + try + { + tinfo = linux_enable_btrace (ptid, conf); + } + catch (const gdb_exception_error &exception) { - gdbarch = target_thread_architecture (ptid); - tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch); + error (_("Could not enable branch tracing for %s: %s"), + target_pid_to_str (ptid).c_str (), exception.what ()); } + return tinfo; } /* Disable branch tracing. */ -static void -x86_linux_disable_btrace (struct target_ops *self, - struct btrace_target_info *tinfo) +void +x86_linux_nat_target::disable_btrace (struct btrace_target_info *tinfo) { enum btrace_error errcode = linux_disable_btrace (tinfo); @@ -470,28 +243,25 @@ x86_linux_disable_btrace (struct target_ops *self, /* Teardown branch tracing. */ -static void -x86_linux_teardown_btrace (struct target_ops *self, - struct btrace_target_info *tinfo) +void +x86_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo) { /* Ignore errors. */ linux_disable_btrace (tinfo); } -static enum btrace_error -x86_linux_read_btrace (struct target_ops *self, - struct btrace_data *data, - struct btrace_target_info *btinfo, - enum btrace_read_type type) +enum btrace_error +x86_linux_nat_target::read_btrace (struct btrace_data *data, + struct btrace_target_info *btinfo, + enum btrace_read_type type) { return linux_read_btrace (data, btinfo, type); } /* See to_btrace_conf in target.h. */ -static const struct btrace_config * -x86_linux_btrace_conf (struct target_ops *self, - const struct btrace_target_info *btinfo) +const struct btrace_config * +x86_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo) { return linux_btrace_conf (btinfo); } @@ -540,49 +310,14 @@ x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr) } -/* Create an x86 GNU/Linux target. */ - -struct target_ops * -x86_linux_create_target (void) +void +_initialize_x86_linux_nat () { - /* Fill in the generic GNU/Linux methods. */ - struct target_ops *t = linux_target (); - /* Initialize the debug register function vectors. */ - x86_use_watchpoints (t); x86_dr_low.set_control = x86_linux_dr_set_control; x86_dr_low.set_addr = x86_linux_dr_set_addr; x86_dr_low.get_addr = x86_linux_dr_get_addr; x86_dr_low.get_status = x86_linux_dr_get_status; x86_dr_low.get_control = x86_linux_dr_get_control; x86_set_debug_register_length (sizeof (void *)); - - /* Override the GNU/Linux inferior startup hook. */ - super_post_startup_inferior = t->to_post_startup_inferior; - t->to_post_startup_inferior = x86_linux_child_post_startup_inferior; - - /* Add the description reader. */ - t->to_read_description = x86_linux_read_description; - - /* Add btrace methods. */ - t->to_supports_btrace = linux_supports_btrace; - t->to_enable_btrace = x86_linux_enable_btrace; - t->to_disable_btrace = x86_linux_disable_btrace; - t->to_teardown_btrace = x86_linux_teardown_btrace; - t->to_read_btrace = x86_linux_read_btrace; - t->to_btrace_conf = x86_linux_btrace_conf; - - return t; -} - -/* Add an x86 GNU/Linux target. */ - -void -x86_linux_add_target (struct target_ops *t) -{ - linux_nat_add_target (t); - linux_nat_set_new_thread (t, x86_linux_new_thread); - linux_nat_set_new_fork (t, x86_linux_new_fork); - linux_nat_set_forget_process (t, x86_forget_process); - linux_nat_set_prepare_to_resume (t, x86_linux_prepare_to_resume); }