X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Famd64-linux-nat.c;h=98126107a590998e8f6863b534ed8a34dd308c28;hb=219f2f2398a678322264121a25214b3046180dec;hp=30f313795ff4a9b88d7b0b1afaf5d993642a1a4f;hpb=5b009018d2759269b0fd192077a391c795ebfa97;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index 30f313795f..98126107a5 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, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. Contributed by Jiri Smid, SuSE Labs. @@ -23,11 +23,14 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "regset.h" #include "linux-nat.h" #include "amd64-linux-tdep.h" #include "gdb_assert.h" #include "gdb_string.h" +#include "elf/common.h" +#include #include #include #include @@ -50,6 +53,19 @@ #include "amd64-tdep.h" #include "i386-linux-tdep.h" #include "amd64-nat.h" +#include "i386-nat.h" +#include "i386-xstate.h" + +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif + +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif + +/* Does the current host support PTRACE_GETREGSET? */ +static int have_ptrace_getregset = -1; /* Mapping between the general-purpose registers in GNU/Linux x86-64 `struct user' format and GDB's register cache layout. */ @@ -72,6 +88,8 @@ static int amd64_linux_gregset64_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, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, ORIG_RAX * 8 }; @@ -98,6 +116,7 @@ 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, + -1, -1, -1, -1, -1, -1, -1, -1, ORIG_RAX * 8 /* "orig_eax" */ }; @@ -155,7 +174,8 @@ fill_fpregset (const struct regcache *regcache, registers). */ static void -amd64_linux_fetch_inferior_registers (struct regcache *regcache, int regnum) +amd64_linux_fetch_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regnum) { struct gdbarch *gdbarch = get_regcache_arch (regcache); int tid; @@ -181,10 +201,26 @@ amd64_linux_fetch_inferior_registers (struct regcache *regcache, int regnum) { elf_fpregset_t fpregs; - if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) - perror_with_name (_("Couldn't get floating point status")); + if (have_ptrace_getregset) + { + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof (xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, + (unsigned int) NT_X86_XSTATE, (long) &iov) < 0) + perror_with_name (_("Couldn't get extended state status")); + + amd64_supply_xsave (regcache, -1, xstateregs); + } + else + { + if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name (_("Couldn't get floating point status")); - amd64_supply_fxsave (regcache, -1, &fpregs); + amd64_supply_fxsave (regcache, -1, &fpregs); + } } } @@ -193,7 +229,8 @@ amd64_linux_fetch_inferior_registers (struct regcache *regcache, int regnum) registers). */ static void -amd64_linux_store_inferior_registers (struct regcache *regcache, int regnum) +amd64_linux_store_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regnum) { struct gdbarch *gdbarch = get_regcache_arch (regcache); int tid; @@ -223,15 +260,33 @@ amd64_linux_store_inferior_registers (struct regcache *regcache, int regnum) { elf_fpregset_t fpregs; - if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) - perror_with_name (_("Couldn't get floating point status")); + if (have_ptrace_getregset) + { + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; - amd64_collect_fxsave (regcache, regnum, &fpregs); + iov.iov_base = xstateregs; + iov.iov_len = sizeof (xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, + (unsigned int) NT_X86_XSTATE, (long) &iov) < 0) + perror_with_name (_("Couldn't get extended state status")); - if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0) - perror_with_name (_("Couldn't write floating point status")); + amd64_collect_xsave (regcache, regnum, xstateregs, 0); - return; + if (ptrace (PTRACE_SETREGSET, tid, + (unsigned int) NT_X86_XSTATE, (long) &iov) < 0) + perror_with_name (_("Couldn't write extended state status")); + } + else + { + if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name (_("Couldn't get floating point status")); + + amd64_collect_fxsave (regcache, regnum, &fpregs); + + if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name (_("Couldn't write floating point status")); + } } } @@ -267,6 +322,8 @@ amd64_linux_dr_get (ptid_t ptid, int regnum) return value; } +/* Set debug register REGNUM to VALUE in only the one LWP of PTID. */ + static void amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { @@ -283,7 +340,9 @@ amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) perror_with_name (_("Couldn't write debug register")); } -void +/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST. */ + +static void amd64_linux_dr_set_control (unsigned long control) { struct lwp_info *lp; @@ -294,7 +353,9 @@ amd64_linux_dr_set_control (unsigned long control) amd64_linux_dr_set (ptid, DR_CONTROL, control); } -void +/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ + +static void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) { struct lwp_info *lp; @@ -307,18 +368,41 @@ amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); } -void +/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */ + +static void amd64_linux_dr_reset_addr (int regnum) { amd64_linux_dr_set_addr (regnum, 0); } -unsigned long +/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */ + +static unsigned long amd64_linux_dr_get_status (void) { return amd64_linux_dr_get (inferior_ptid, DR_STATUS); } +/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */ + +static void +amd64_linux_dr_unset_status (unsigned long mask) +{ + struct lwp_info *lp; + ptid_t ptid; + + ALL_LWPS (lp, ptid) + { + unsigned long value; + + value = amd64_linux_dr_get (ptid, DR_STATUS); + value &= ~mask; + amd64_linux_dr_set (ptid, DR_STATUS, value); + } +} + + static void amd64_linux_new_thread (ptid_t ptid) { @@ -338,7 +422,7 @@ ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { - if (gdbarch_ptr_bit (current_gdbarch) == 32) + if (gdbarch_ptr_bit (target_gdbarch) == 32) { /* The full structure is found in . The second integer is the LDT's base_address and that is used to locate @@ -499,6 +583,15 @@ typedef struct compat_siginfo #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) { @@ -633,6 +726,77 @@ amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf, int direction) return 0; } +/* Get Linux/x86 target description from running target. + + Value of CS segment register: + 1. 64bit process: 0x33. + 2. 32bit process: 0x23. + */ + +#define AMD64_LINUX_USER64_CS 0x33 + +static const struct target_desc * +amd64_linux_read_description (struct target_ops *ops) +{ + unsigned long cs; + int tid; + int is_64bit; + static uint64_t xcr0; + + /* GNU/Linux LWP ID's are process ID's. */ + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + + /* Get CS register. */ + errno = 0; + cs = ptrace (PTRACE_PEEKUSER, tid, + offsetof (struct user_regs_struct, cs), 0); + if (errno != 0) + perror_with_name (_("Couldn't get CS register")); + + is_64bit = cs == AMD64_LINUX_USER64_CS; + + if (have_ptrace_getregset == -1) + { + uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))]; + struct iovec iov; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof (xstateregs); + + /* Check if PTRACE_GETREGSET works. */ + if (ptrace (PTRACE_GETREGSET, tid, + (unsigned int) NT_X86_XSTATE, (long) &iov) < 0) + have_ptrace_getregset = 0; + else + { + have_ptrace_getregset = 1; + + /* Get XCR0 from XSAVE extended state. */ + xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET + / sizeof (uint64_t))]; + } + } + + /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ + if (have_ptrace_getregset + && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) + { + if (is_64bit) + return tdesc_amd64_avx_linux; + else + return tdesc_i386_avx_linux; + } + else + { + if (is_64bit) + return tdesc_amd64_linux; + else + return tdesc_i386_linux; + } +} + /* Provide a prototype to silence -Wmissing-prototypes. */ void _initialize_amd64_linux_nat (void); @@ -656,6 +820,13 @@ _initialize_amd64_linux_nat (void) i386_use_watchpoints (t); + i386_dr_low.set_control = amd64_linux_dr_set_control; + i386_dr_low.set_addr = amd64_linux_dr_set_addr; + i386_dr_low.reset_addr = amd64_linux_dr_reset_addr; + i386_dr_low.get_status = amd64_linux_dr_get_status; + i386_dr_low.unset_status = amd64_linux_dr_unset_status; + i386_set_debug_register_length (8); + /* Override the GNU/Linux inferior startup hook. */ super_post_startup_inferior = t->to_post_startup_inferior; t->to_post_startup_inferior = amd64_linux_child_post_startup_inferior; @@ -664,6 +835,8 @@ _initialize_amd64_linux_nat (void) t->to_fetch_registers = amd64_linux_fetch_inferior_registers; t->to_store_registers = amd64_linux_store_inferior_registers; + t->to_read_description = amd64_linux_read_description; + /* Register the target. */ linux_nat_add_target (t); linux_nat_set_new_thread (t, amd64_linux_new_thread);