X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Famd64-linux-nat.c;h=b7b889bcd2bc3adf2c8319aa2140b96bcd8262cd;hb=2117c711ae07700adb57ea5b5ca61e4c32d7e3d2;hp=23eadbd79c64b3e9336377e21daa2d49dda6ea72;hpb=ba224c70dd8ba8ab0aabe0c1ac9f98c7d832b024;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index 23eadbd79c..b7b889bcd2 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -1,6 +1,6 @@ /* Native-dependent code for GNU/Linux x86-64. - Copyright (C) 2001-2012 Free Software Foundation, Inc. + Copyright (C) 2001-2014 Free Software Foundation, Inc. Contributed by Jiri Smid, SuSE Labs. This file is part of GDB. @@ -25,15 +25,18 @@ #include "regset.h" #include "linux-nat.h" #include "amd64-linux-tdep.h" +#include "linux-btrace.h" +#include "btrace.h" #include "gdb_assert.h" -#include "gdb_string.h" +#include #include "elf/common.h" #include #include #include #include #include +#include #include /* FIXME ezannoni-2003-07-09: we need to be included after because the latter redefines FS and GS for no apparent @@ -97,7 +100,9 @@ static int amd64_linux_gregset32_reg_offset[] = -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, - ORIG_RAX * 8 /* "orig_eax" */ + -1, -1, -1, -1, /* MPX registers BND0 ... BND3. */ + -1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */ + ORIG_RAX * 8, /* "orig_eax" */ }; @@ -161,9 +166,9 @@ amd64_linux_fetch_inferior_registers (struct target_ops *ops, int tid; /* GNU/Linux LWP ID's are process ID's. */ - tid = TIDGET (inferior_ptid); + tid = ptid_get_lwp (inferior_ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum)) { @@ -216,9 +221,9 @@ amd64_linux_store_inferior_registers (struct target_ops *ops, int tid; /* GNU/Linux LWP ID's are process ID's. */ - tid = TIDGET (inferior_ptid); + tid = ptid_get_lwp (inferior_ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum)) { @@ -278,9 +283,9 @@ amd64_linux_dr_get (ptid_t ptid, int regnum) int tid; unsigned long value; - tid = TIDGET (ptid); + tid = ptid_get_lwp (ptid); if (tid == 0) - tid = PIDGET (ptid); + tid = ptid_get_pid (ptid); errno = 0; value = ptrace (PTRACE_PEEKUSER, tid, @@ -298,9 +303,9 @@ amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { int tid; - tid = TIDGET (ptid); + tid = ptid_get_lwp (ptid); if (tid == 0) - tid = PIDGET (ptid); + tid = ptid_get_pid (ptid); errno = 0; ptrace (PTRACE_POKEUSER, tid, @@ -336,8 +341,8 @@ amd64_linux_dr_get_status (void) return amd64_linux_dr_get (inferior_ptid, DR_STATUS); } -/* Callback for linux_nat_iterate_watchpoint_lwps. Update the debug registers - of LWP. */ +/* Callback for iterate_over_lwps. Update the debug registers of + LWP. */ static int update_debug_registers_callback (struct lwp_info *lwp, void *arg) @@ -363,7 +368,9 @@ update_debug_registers_callback (struct lwp_info *lwp, void *arg) static void amd64_linux_dr_set_control (unsigned long control) { - linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); } /* Set address REGNUM (zero based) to ADDR in all LWPs of the current @@ -372,9 +379,11 @@ amd64_linux_dr_set_control (unsigned long control) static void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) { + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); - linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); } /* Called when resuming a thread. @@ -393,7 +402,8 @@ amd64_linux_prepare_to_resume (struct lwp_info *lwp) if (lwp->arch_private->debug_registers_changed) { - struct i386_debug_reg_state *state = i386_debug_reg_state (); + struct i386_debug_reg_state *state + = i386_debug_reg_state (ptid_get_pid (lwp->ptid)); int i; /* On Linux kernel before 2.6.33 commit @@ -433,6 +443,41 @@ amd64_linux_new_thread (struct lwp_info *lp) lp->arch_private = info; } + +/* linux_nat_new_fork hook. */ + +static void +amd64_linux_new_fork (struct lwp_info *parent, pid_t child_pid) +{ + pid_t parent_pid; + struct i386_debug_reg_state *parent_state; + struct i386_debug_reg_state *child_state; + + /* NULL means no watchpoint has ever been set in the parent. In + that case, there's nothing to do. */ + if (parent->arch_private == NULL) + return; + + /* 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. */ + + parent_pid = ptid_get_pid (parent->ptid); + parent_state = i386_debug_reg_state (parent_pid); + child_state = i386_debug_reg_state (child_pid); + *child_state = *parent_state; +} + /* This function is called by libthread_db as part of its handling of @@ -442,7 +487,7 @@ ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { - if (gdbarch_bfd_arch_info (target_gdbarch)->bits_per_word == 32) + if (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 32) { /* The full structure is found in . The second integer is the LDT's base_address and that is used to locate @@ -479,10 +524,39 @@ ps_get_thread_area (const struct ps_prochandle *ph, switch (idx) { case FS: +#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE + { + /* PTRACE_ARCH_PRCTL is obsolete since 2.6.25, where the + fs_base and gs_base fields of user_regs_struct can be + used directly. */ + unsigned long fs; + errno = 0; + fs = ptrace (PTRACE_PEEKUSER, lwpid, + offsetof (struct user_regs_struct, fs_base), 0); + if (errno == 0) + { + *base = (void *) fs; + return PS_OK; + } + } +#endif if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0) return PS_OK; break; case GS: +#ifdef HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE + { + unsigned long gs; + errno = 0; + gs = ptrace (PTRACE_PEEKUSER, lwpid, + offsetof (struct user_regs_struct, gs_base), 0); + if (errno == 0) + { + *base = (void *) gs; + return PS_OK; + } + } +#endif if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0) return PS_OK; break; @@ -494,13 +568,14 @@ ps_get_thread_area (const struct ps_prochandle *ph, } -static void (*super_post_startup_inferior) (ptid_t ptid); +static void (*super_post_startup_inferior) (struct target_ops *self, + ptid_t ptid); static void -amd64_linux_child_post_startup_inferior (ptid_t ptid) +amd64_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid) { i386_cleanup_dregs (); - super_post_startup_inferior (ptid); + super_post_startup_inferior (self, ptid); } @@ -974,9 +1049,9 @@ amd64_linux_read_description (struct target_ops *ops) static uint64_t xcr0; /* GNU/Linux LWP ID's are process ID's. */ - tid = TIDGET (inferior_ptid); + tid = ptid_get_lwp (inferior_ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ /* Get CS register. */ errno = 0; @@ -1022,18 +1097,41 @@ amd64_linux_read_description (struct target_ops *ops) } /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ - if (have_ptrace_getregset - && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) + if (have_ptrace_getregset && (xcr0 & I386_XSTATE_ALL_MASK)) { - if (is_64bit) + switch (xcr0 & I386_XSTATE_ALL_MASK) { - if (is_x32) - return tdesc_x32_avx_linux; + case I386_XSTATE_MPX_MASK: + if (is_64bit) + { + if (is_x32) + return tdesc_x32_avx_linux; /* No MPX on x32 using AVX. */ + else + return tdesc_amd64_mpx_linux; + } + else + return tdesc_i386_mpx_linux; + case I386_XSTATE_AVX_MASK: + if (is_64bit) + { + if (is_x32) + return tdesc_x32_avx_linux; + else + return tdesc_amd64_avx_linux; + } else - return tdesc_amd64_avx_linux; + return tdesc_i386_avx_linux; + default: + if (is_64bit) + { + if (is_x32) + return tdesc_x32_linux; + else + return tdesc_amd64_linux; + } + else + return tdesc_i386_linux; } - else - return tdesc_i386_avx_linux; } else { @@ -1049,6 +1147,59 @@ amd64_linux_read_description (struct target_ops *ops) } } +/* Enable branch tracing. */ + +static struct btrace_target_info * +amd64_linux_enable_btrace (struct target_ops *self, ptid_t ptid) +{ + struct btrace_target_info *tinfo; + struct gdbarch *gdbarch; + + errno = 0; + tinfo = linux_enable_btrace (ptid); + + 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. */ + gdbarch = target_thread_architecture (ptid); + tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch); + + return tinfo; +} + +/* Disable branch tracing. */ + +static void +amd64_linux_disable_btrace (struct target_ops *self, + struct btrace_target_info *tinfo) +{ + enum btrace_error errcode = linux_disable_btrace (tinfo); + + if (errcode != BTRACE_ERR_NONE) + error (_("Could not disable branch tracing.")); +} + +/* Teardown branch tracing. */ + +static void +amd64_linux_teardown_btrace (struct target_ops *self, + struct btrace_target_info *tinfo) +{ + /* Ignore errors. */ + linux_disable_btrace (tinfo); +} + +static enum btrace_error +amd64_linux_read_btrace (struct target_ops *self, + VEC (btrace_block_s) **data, + struct btrace_target_info *btinfo, + enum btrace_read_type type) +{ + return linux_read_btrace (data, btinfo, type); +} + /* Provide a prototype to silence -Wmissing-prototypes. */ void _initialize_amd64_linux_nat (void); @@ -1087,9 +1238,18 @@ _initialize_amd64_linux_nat (void) t->to_read_description = amd64_linux_read_description; + /* Add btrace methods. */ + t->to_supports_btrace = linux_supports_btrace; + t->to_enable_btrace = amd64_linux_enable_btrace; + t->to_disable_btrace = amd64_linux_disable_btrace; + t->to_teardown_btrace = amd64_linux_teardown_btrace; + t->to_read_btrace = amd64_linux_read_btrace; + /* Register the target. */ linux_nat_add_target (t); linux_nat_set_new_thread (t, amd64_linux_new_thread); + linux_nat_set_new_fork (t, amd64_linux_new_fork); + linux_nat_set_forget_process (t, i386_forget_process); linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup); linux_nat_set_prepare_to_resume (t, amd64_linux_prepare_to_resume); }