Index: gdb/amd64-linux-nat.c |
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c |
index 01982acf17c26fd840eeafcc281db0ec9a429c5c..b1676ac32ea676bd0d046ce35c05decf6687968c 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-2013 Free Software Foundation, Inc. |
Contributed by Jiri Smid, SuSE Labs. |
This file is part of GDB. |
@@ -25,9 +25,11 @@ |
#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 <string.h> |
#include "elf/common.h" |
#include <sys/uio.h> |
#include <sys/ptrace.h> |
@@ -98,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" */ |
}; |
@@ -162,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)) |
{ |
@@ -217,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)) |
{ |
@@ -279,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, |
@@ -299,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, |
@@ -337,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) |
@@ -364,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 |
@@ -373,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. |
@@ -394,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 |
@@ -434,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 |
@@ -443,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 <asm-i386/ldt.h>. The second |
integer is the LDT's base_address and that is used to locate |
@@ -1004,9 +1048,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; |
@@ -1052,18 +1096,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_i386_avx_linux; |
+ default: |
+ if (is_64bit) |
+ { |
+ if (is_x32) |
+ return tdesc_x32_linux; |
+ else |
+ return tdesc_amd64_linux; |
+ } |
else |
- return tdesc_amd64_avx_linux; |
+ return tdesc_i386_linux; |
} |
- else |
- return tdesc_i386_avx_linux; |
} |
else |
{ |
@@ -1079,6 +1146,48 @@ amd64_linux_read_description (struct target_ops *ops) |
} |
} |
+/* Enable branch tracing. */ |
+ |
+static struct btrace_target_info * |
+amd64_linux_enable_btrace (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 btrace_target_info *tinfo) |
+{ |
+ int errcode = linux_disable_btrace (tinfo); |
+ |
+ if (errcode != 0) |
+ error (_("Could not disable branch tracing: %s."), safe_strerror (errcode)); |
+} |
+ |
+/* Teardown branch tracing. */ |
+ |
+static void |
+amd64_linux_teardown_btrace (struct btrace_target_info *tinfo) |
+{ |
+ /* Ignore errors. */ |
+ linux_disable_btrace (tinfo); |
+} |
+ |
/* Provide a prototype to silence -Wmissing-prototypes. */ |
void _initialize_amd64_linux_nat (void); |
@@ -1117,9 +1226,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 = 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); |
} |