X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fi386-linux-nat.c;h=be2b6c9f9c95c0798b00a2e9f772da6aba5f4fb7;hb=df71cb5cbfa26ad79e5cf674056205f0505e229d;hp=973cba78b6286c03feb5f9cce5fa6d1124ca5c9c;hpb=2e024c20ebf22ba56d2b25498076c60cf957132e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c index 973cba78b6..be2b6c9f9c 100644 --- a/gdb/i386-linux-nat.c +++ b/gdb/i386-linux-nat.c @@ -1,13 +1,12 @@ /* Native-dependent code for GNU/Linux i386. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1999-2013 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,19 +15,23 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ #include "defs.h" +#include "i386-nat.h" #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "regset.h" #include "target.h" #include "linux-nat.h" +#include "linux-btrace.h" +#include "btrace.h" #include "gdb_assert.h" #include "gdb_string.h" +#include "elf/common.h" +#include #include #include #include @@ -45,22 +48,6 @@ #include #endif -#ifndef DR_FIRSTADDR -#define DR_FIRSTADDR 0 -#endif - -#ifndef DR_LASTADDR -#define DR_LASTADDR 3 -#endif - -#ifndef DR_STATUS -#define DR_STATUS 6 -#endif - -#ifndef DR_CONTROL -#define DR_CONTROL 7 -#endif - /* Prototypes for supply_gregset etc. */ #include "gregset.h" @@ -70,6 +57,27 @@ /* Defines ps_err_e, struct ps_prochandle. */ #include "gdb_proc_service.h" + +#include "i386-xstate.h" + +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif + +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif + +/* Per-thread arch-specific data we want to keep. */ + +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? */ +static int have_ptrace_getregset = -1; /* The register sets used in GNU/Linux ELF core-dumps are identical to @@ -84,24 +92,6 @@ those names are now used for the register sets used in the `mcontext_t' type, and have a different size and layout. */ -/* Mapping between the general-purpose registers in `struct user' - format and GDB's register array layout. */ -static int regmap[] = -{ - EAX, ECX, EDX, EBX, - UESP, EBP, ESI, EDI, - EIP, EFL, CS, SS, - DS, ES, FS, GS, - -1, -1, -1, -1, /* st0, st1, st2, st3 */ - -1, -1, -1, -1, /* st4, st5, st6, st7 */ - -1, -1, -1, -1, /* fctrl, fstat, ftag, fiseg */ - -1, -1, -1, -1, /* fioff, foseg, fooff, fop */ - -1, -1, -1, -1, /* xmm0, xmm1, xmm2, xmm3 */ - -1, -1, -1, -1, /* xmm4, xmm5, xmm6, xmm6 */ - -1, /* mxcsr */ - ORIG_EAX -}; - /* Which ptrace request retrieves which registers? These apply to the corresponding SET requests as well. */ @@ -111,6 +101,9 @@ static int regmap[] = #define GETFPXREGS_SUPPLIES(regno) \ (I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS) +#define GETXSTATEREGS_SUPPLIES(regno) \ + (I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX_NUM_REGS) + /* Does the current host support the GETREGS request? */ int have_ptrace_getregs = #ifdef HAVE_PTRACE_GETREGS @@ -130,7 +123,7 @@ int have_ptrace_getregs = for this to be a simple variable. */ int have_ptrace_getfpxregs = #ifdef HAVE_PTRACE_GETFPXREGS - 1 + -1 #else 0 #endif @@ -148,7 +141,7 @@ fetch_register (struct regcache *regcache, int regno) int val; gdb_assert (!have_ptrace_getregs); - if (regmap[regno] == -1) + if (i386_linux_gregset_reg_offset[regno] == -1) { regcache_raw_supply (regcache, regno, NULL); return; @@ -160,16 +153,17 @@ fetch_register (struct regcache *regcache, int regno) tid = PIDGET (inferior_ptid); /* Not a threaded program. */ errno = 0; - val = ptrace (PTRACE_PEEKUSER, tid, 4 * regmap[regno], 0); + val = ptrace (PTRACE_PEEKUSER, tid, + i386_linux_gregset_reg_offset[regno], 0); if (errno != 0) error (_("Couldn't read register %s (#%d): %s."), - gdbarch_register_name (current_gdbarch, regno), + gdbarch_register_name (get_regcache_arch (regcache), regno), regno, safe_strerror (errno)); regcache_raw_supply (regcache, regno, &val); } -/* Store one register. */ +/* Store one register. */ static void store_register (const struct regcache *regcache, int regno) @@ -178,7 +172,7 @@ store_register (const struct regcache *regcache, int regno) int val; gdb_assert (!have_ptrace_getregs); - if (regmap[regno] == -1) + if (i386_linux_gregset_reg_offset[regno] == -1) return; /* GNU/Linux LWP ID's are process ID's. */ @@ -188,10 +182,11 @@ store_register (const struct regcache *regcache, int regno) errno = 0; regcache_raw_collect (regcache, regno, &val); - ptrace (PTRACE_POKEUSER, tid, 4 * regmap[regno], val); + ptrace (PTRACE_POKEUSER, tid, + i386_linux_gregset_reg_offset[regno], val); if (errno != 0) error (_("Couldn't write register %s (#%d): %s."), - gdbarch_register_name (current_gdbarch, regno), + gdbarch_register_name (get_regcache_arch (regcache), regno), regno, safe_strerror (errno)); } @@ -205,15 +200,17 @@ store_register (const struct regcache *regcache, int regno) void supply_gregset (struct regcache *regcache, const elf_gregset_t *gregsetp) { - const elf_greg_t *regp = (const elf_greg_t *) gregsetp; + const gdb_byte *regp = (const gdb_byte *) gregsetp; int i; for (i = 0; i < I386_NUM_GREGS; i++) - regcache_raw_supply (regcache, i, regp + regmap[i]); + regcache_raw_supply (regcache, i, + regp + i386_linux_gregset_reg_offset[i]); - if (I386_LINUX_ORIG_EAX_REGNUM < gdbarch_num_regs (current_gdbarch)) - regcache_raw_supply (regcache, I386_LINUX_ORIG_EAX_REGNUM, - regp + ORIG_EAX); + if (I386_LINUX_ORIG_EAX_REGNUM + < gdbarch_num_regs (get_regcache_arch (regcache))) + regcache_raw_supply (regcache, I386_LINUX_ORIG_EAX_REGNUM, regp + + i386_linux_gregset_reg_offset[I386_LINUX_ORIG_EAX_REGNUM]); } /* Fill register REGNO (if it is a general-purpose register) in @@ -224,17 +221,19 @@ void fill_gregset (const struct regcache *regcache, elf_gregset_t *gregsetp, int regno) { - elf_greg_t *regp = (elf_greg_t *) gregsetp; + gdb_byte *regp = (gdb_byte *) gregsetp; int i; for (i = 0; i < I386_NUM_GREGS; i++) if (regno == -1 || regno == i) - regcache_raw_collect (regcache, i, regp + regmap[i]); + regcache_raw_collect (regcache, i, + regp + i386_linux_gregset_reg_offset[i]); if ((regno == -1 || regno == I386_LINUX_ORIG_EAX_REGNUM) - && I386_LINUX_ORIG_EAX_REGNUM < gdbarch_num_regs (current_gdbarch)) - regcache_raw_collect (regcache, I386_LINUX_ORIG_EAX_REGNUM, - regp + ORIG_EAX); + && I386_LINUX_ORIG_EAX_REGNUM + < gdbarch_num_regs (get_regcache_arch (regcache))) + regcache_raw_collect (regcache, I386_LINUX_ORIG_EAX_REGNUM, regp + + i386_linux_gregset_reg_offset[I386_LINUX_ORIG_EAX_REGNUM]); } #ifdef HAVE_PTRACE_GETREGS @@ -346,37 +345,74 @@ store_fpregs (const struct regcache *regcache, int tid, int regno) #else -static void fetch_fpregs (struct regcache *regcache, int tid) {} -static void store_fpregs (const struct regcache *regcache, int tid, int regno) {} +static void +fetch_fpregs (struct regcache *regcache, int tid) +{ +} + +static void +store_fpregs (const struct regcache *regcache, int tid, int regno) +{ +} #endif /* Transfering floating-point and SSE registers to and from GDB. */ -#ifdef HAVE_PTRACE_GETFPXREGS - -/* Fill GDB's register array with the floating-point and SSE register - values in *FPXREGSETP. */ +/* Fetch all registers covered by the PTRACE_GETREGSET request from + process/thread TID and store their values in GDB's register array. + Return non-zero if successful, zero otherwise. */ -void -supply_fpxregset (struct regcache *regcache, - const elf_fpxregset_t *fpxregsetp) +static int +fetch_xstateregs (struct regcache *regcache, int tid) { - i387_supply_fxsave (regcache, -1, fpxregsetp); + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; + + if (!have_ptrace_getregset) + return 0; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof(xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, + &iov) < 0) + perror_with_name (_("Couldn't read extended state status")); + + i387_supply_xsave (regcache, -1, xstateregs); + return 1; } -/* Fill register REGNO (if it is a floating-point or SSE register) in - *FPXREGSETP with the value in GDB's register array. If REGNO is - -1, do this for all registers. */ +/* Store all valid registers in GDB's register array covered by the + PTRACE_SETREGSET request into the process/thread specified by TID. + Return non-zero if successful, zero otherwise. */ -void -fill_fpxregset (const struct regcache *regcache, - elf_fpxregset_t *fpxregsetp, int regno) +static int +store_xstateregs (const struct regcache *regcache, int tid, int regno) { - i387_collect_fxsave (regcache, regno, fpxregsetp); + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; + + if (!have_ptrace_getregset) + return 0; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof(xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, + &iov) < 0) + perror_with_name (_("Couldn't read extended state status")); + + i387_collect_xsave (regcache, regno, xstateregs, 0); + + if (ptrace (PTRACE_SETREGSET, tid, (unsigned int) NT_X86_XSTATE, + (int) &iov) < 0) + perror_with_name (_("Couldn't write extended state status")); + + return 1; } +#ifdef HAVE_PTRACE_GETFPXREGS + /* Fetch all registers covered by the PTRACE_GETFPXREGS request from process/thread TID and store their values in GDB's register array. Return non-zero if successful, zero otherwise. */ @@ -400,7 +436,7 @@ fetch_fpxregs (struct regcache *regcache, int tid) perror_with_name (_("Couldn't read floating-point and SSE registers")); } - supply_fpxregset (regcache, (const elf_fpxregset_t *) &fpxregs); + i387_supply_fxsave (regcache, -1, (const elf_fpxregset_t *) &fpxregs); return 1; } @@ -427,7 +463,7 @@ store_fpxregs (const struct regcache *regcache, int tid, int regno) perror_with_name (_("Couldn't read floating-point and SSE registers")); } - fill_fpxregset (regcache, &fpxregs, regno); + i387_collect_fxsave (regcache, regno, &fpxregs); if (ptrace (PTRACE_SETFPXREGS, tid, 0, &fpxregs) == -1) perror_with_name (_("Couldn't write floating-point and SSE registers")); @@ -437,8 +473,17 @@ store_fpxregs (const struct regcache *regcache, int tid, int regno) #else -static int fetch_fpxregs (struct regcache *regcache, int tid) { return 0; } -static int store_fpxregs (const struct regcache *regcache, int tid, int regno) { return 0; } +static int +fetch_fpxregs (struct regcache *regcache, int tid) +{ + return 0; +} + +static int +store_fpxregs (const struct regcache *regcache, int tid, int regno) +{ + return 0; +} #endif /* HAVE_PTRACE_GETFPXREGS */ @@ -450,7 +495,8 @@ static int store_fpxregs (const struct regcache *regcache, int tid, int regno) { registers). */ static void -i386_linux_fetch_inferior_registers (struct regcache *regcache, int regno) +i386_linux_fetch_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regno) { int tid; @@ -460,7 +506,7 @@ i386_linux_fetch_inferior_registers (struct regcache *regcache, int regno) { int i; - for (i = 0; i < gdbarch_num_regs (current_gdbarch); i++) + for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) if (regno == -1 || regno == i) fetch_register (regcache, i); @@ -483,10 +529,12 @@ i386_linux_fetch_inferior_registers (struct regcache *regcache, int regno) /* The call above might reset `have_ptrace_getregs'. */ if (!have_ptrace_getregs) { - i386_linux_fetch_inferior_registers (regcache, regno); + i386_linux_fetch_inferior_registers (ops, regcache, regno); return; } + if (fetch_xstateregs (regcache, tid)) + return; if (fetch_fpxregs (regcache, tid)) return; fetch_fpregs (regcache, tid); @@ -499,6 +547,12 @@ i386_linux_fetch_inferior_registers (struct regcache *regcache, int regno) return; } + if (GETXSTATEREGS_SUPPLIES (regno)) + { + if (fetch_xstateregs (regcache, tid)) + return; + } + if (GETFPXREGS_SUPPLIES (regno)) { if (fetch_fpxregs (regcache, tid)) @@ -522,7 +576,8 @@ i386_linux_fetch_inferior_registers (struct regcache *regcache, int regno) do this for all registers (including the floating point and SSE registers). */ static void -i386_linux_store_inferior_registers (struct regcache *regcache, int regno) +i386_linux_store_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regno) { int tid; @@ -532,7 +587,7 @@ i386_linux_store_inferior_registers (struct regcache *regcache, int regno) { int i; - for (i = 0; i < gdbarch_num_regs (current_gdbarch); i++) + for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) if (regno == -1 || regno == i) store_register (regcache, i); @@ -550,6 +605,8 @@ i386_linux_store_inferior_registers (struct regcache *regcache, int regno) if (regno == -1) { store_regs (regcache, tid, regno); + if (store_xstateregs (regcache, tid, regno)) + return; if (store_fpxregs (regcache, tid, regno)) return; store_fpregs (regcache, tid, regno); @@ -562,6 +619,12 @@ i386_linux_store_inferior_registers (struct regcache *regcache, int regno) return; } + if (GETXSTATEREGS_SUPPLIES (regno)) + { + if (store_xstateregs (regcache, tid, regno)) + return; + } + if (GETFPXREGS_SUPPLIES (regno)) { if (store_fpxregs (regcache, tid, regno)) @@ -581,44 +644,37 @@ i386_linux_store_inferior_registers (struct regcache *regcache, int regno) /* Support for debug registers. */ +/* Get debug register REGNUM value from only the one LWP of PTID. */ + static unsigned long -i386_linux_dr_get (int regnum) +i386_linux_dr_get (ptid_t ptid, int regnum) { int tid; unsigned long value; - /* FIXME: kettenis/2001-01-29: It's not clear what we should do with - multi-threaded processes here. For now, pretend there is just - one thread. */ - tid = PIDGET (inferior_ptid); + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); - /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the - ptrace call fails breaks debugging remote targets. The correct - way to fix this is to add the hardware breakpoint and watchpoint - stuff to the target vector. For now, just return zero if the - ptrace call fails. */ errno = 0; value = ptrace (PTRACE_PEEKUSER, tid, offsetof (struct user, u_debugreg[regnum]), 0); if (errno != 0) -#if 0 perror_with_name (_("Couldn't read debug register")); -#else - return 0; -#endif return value; } +/* Set debug register REGNUM to VALUE in only the one LWP of PTID. */ + static void -i386_linux_dr_set (int regnum, unsigned long value) +i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { int tid; - /* FIXME: kettenis/2001-01-29: It's not clear what we should do with - multi-threaded processes here. For now, pretend there is just - one thread. */ - tid = PIDGET (inferior_ptid); + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); errno = 0; ptrace (PTRACE_POKEUSER, tid, @@ -627,33 +683,166 @@ i386_linux_dr_set (int regnum, unsigned long value) perror_with_name (_("Couldn't write debug register")); } -void +/* Return the inferior's debug register REGNUM. */ + +static CORE_ADDR +i386_linux_dr_get_addr (int regnum) +{ + /* DR6 and DR7 are retrieved with some other way. */ + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + return i386_linux_dr_get (inferior_ptid, regnum); +} + +/* Return the inferior's DR7 debug control register. */ + +static unsigned long +i386_linux_dr_get_control (void) +{ + return i386_linux_dr_get (inferior_ptid, DR_CONTROL); +} + +/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */ + +static unsigned long +i386_linux_dr_get_status (void) +{ + return i386_linux_dr_get (inferior_ptid, DR_STATUS); +} + +/* Callback for iterate_over_lwps. Update the debug registers of + LWP. */ + +static int +update_debug_registers_callback (struct lwp_info *lwp, void *arg) +{ + if (lwp->arch_private == NULL) + lwp->arch_private = XCNEW (struct arch_lwp_info); + + /* The actual update is done later just before resuming the lwp, we + just mark that the registers need updating. */ + lwp->arch_private->debug_registers_changed = 1; + + /* If the lwp isn't stopped, force it to momentarily pause, so we + can update its debug registers. */ + if (!lwp->stopped) + linux_stop_lwp (lwp); + + /* Continue the iteration. */ + return 0; +} + +/* Set DR_CONTROL to ADDR in all LWPs of the current inferior. */ + +static void i386_linux_dr_set_control (unsigned long control) { - i386_linux_dr_set (DR_CONTROL, control); + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); } -void +/* Set address REGNUM (zero based) to ADDR in all LWPs of the current + inferior. */ + +static void i386_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); - i386_linux_dr_set (DR_FIRSTADDR + regnum, addr); + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); } -void -i386_linux_dr_reset_addr (int regnum) +/* Called when resuming a thread. + If the debug regs have changed, update the thread's copies. */ + +static void +i386_linux_prepare_to_resume (struct lwp_info *lwp) { - gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); + int clear_status = 0; + + /* NULL means this is the main thread still going through the shell, + or, no watchpoint has been set yet. In that case, there's + nothing to do. */ + if (lwp->arch_private == NULL) + return; + + if (lwp->arch_private->debug_registers_changed) + { + struct i386_debug_reg_state *state + = i386_debug_reg_state (ptid_get_pid (lwp->ptid)); + int i; - i386_linux_dr_set (DR_FIRSTADDR + regnum, 0L); + /* See amd64_linux_prepare_to_resume for Linux kernel note on + i386_linux_dr_set calls ordering. */ + + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + if (state->dr_ref_count[i] > 0) + { + i386_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]); + + /* If we're setting a watchpoint, any change the inferior + had done itself to the debug registers needs to be + discarded, otherwise, i386_stopped_data_address can get + confused. */ + clear_status = 1; + } + + i386_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror); + + lwp->arch_private->debug_registers_changed = 0; + } + + if (clear_status || lwp->stopped_by_watchpoint) + i386_linux_dr_set (lwp->ptid, DR_STATUS, 0); } -unsigned long -i386_linux_dr_get_status (void) +static void +i386_linux_new_thread (struct lwp_info *lp) +{ + struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); + + info->debug_registers_changed = 1; + + lp->arch_private = info; +} + +/* linux_nat_new_fork hook. */ + +static void +i386_linux_new_fork (struct lwp_info *parent, pid_t child_pid) { - return i386_linux_dr_get (DR_STATUS); + 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; } + /* Called by libthread_db. Returns a pointer to the thread local @@ -675,7 +864,7 @@ ps_get_thread_area (const struct ps_prochandle *ph, call. Is this function needed? I'm guessing that the `base' is the - address of a a descriptor that libthread_db uses to find the + address of a descriptor that libthread_db uses to find the thread local address base that GDB needs. Perhaps that descriptor is defined by the ABI. Anyway, given that libthread_db calls this function without prompting (gdb @@ -725,28 +914,30 @@ static const unsigned char linux_syscall[] = { 0xcd, 0x80 }; If SIGNAL is nonzero, give it that signal. */ static void -i386_linux_resume (ptid_t ptid, int step, enum target_signal signal) +i386_linux_resume (struct target_ops *ops, + ptid_t ptid, int step, enum gdb_signal signal) { int pid = PIDGET (ptid); - int request = PTRACE_CONT; + int request; - if (pid == -1) - /* Resume all threads. */ - /* I think this only gets used in the non-threaded case, where "resume - all threads" and "resume inferior_ptid" are the same. */ - pid = PIDGET (inferior_ptid); + if (catch_syscall_enabled () > 0) + request = PTRACE_SYSCALL; + else + request = PTRACE_CONT; if (step) { struct regcache *regcache = get_thread_regcache (pid_to_ptid (pid)); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); ULONGEST pc; gdb_byte buf[LINUX_SYSCALL_LEN]; request = PTRACE_SINGLESTEP; regcache_cooked_read_unsigned (regcache, - gdbarch_pc_regnum (current_gdbarch), &pc); + gdbarch_pc_regnum (gdbarch), &pc); /* Returning from a signal trampoline is done by calling a special system call (sigreturn or rt_sigreturn, see @@ -757,7 +948,7 @@ i386_linux_resume (ptid_t ptid, int step, enum target_signal signal) that's about to be restored, and set the trace flag there. */ /* First check if PC is at a system call. */ - if (read_memory_nobpt (pc, buf, LINUX_SYSCALL_LEN) == 0 + if (target_read_memory (pc, buf, LINUX_SYSCALL_LEN) == 0 && memcmp (buf, linux_syscall, LINUX_SYSCALL_LEN) == 0) { ULONGEST syscall; @@ -772,7 +963,8 @@ i386_linux_resume (ptid_t ptid, int step, enum target_signal signal) regcache_cooked_read_unsigned (regcache, I386_ESP_REGNUM, &sp); if (syscall == SYS_rt_sigreturn) - addr = read_memory_integer (sp + 8, 4) + 20; + addr = read_memory_unsigned_integer (sp + 8, 4, byte_order) + + 20; else addr = sp; @@ -786,7 +978,7 @@ i386_linux_resume (ptid_t ptid, int step, enum target_signal signal) } } - if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1) + if (ptrace (request, pid, 0, gdb_signal_to_host (signal)) == -1) perror_with_name (("ptrace")); } @@ -799,6 +991,108 @@ i386_linux_child_post_startup_inferior (ptid_t ptid) super_post_startup_inferior (ptid); } +/* Get Linux/x86 target description from running target. */ + +static const struct target_desc * +i386_linux_read_description (struct target_ops *ops) +{ + int tid; + 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. */ + +#ifdef HAVE_PTRACE_GETFPXREGS + if (have_ptrace_getfpxregs == -1) + { + elf_fpxregset_t fpxregs; + + if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0) + { + have_ptrace_getfpxregs = 0; + have_ptrace_getregset = 0; + return tdesc_i386_mmx_linux; + } + } +#endif + + 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, + &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 (long long))]; + } + } + + /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ + if (have_ptrace_getregset + && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) + return tdesc_i386_avx_linux; + else + return tdesc_i386_linux; +} + +/* Enable branch tracing. */ + +static struct btrace_target_info * +i386_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 +i386_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 +i386_linux_teardown_btrace (struct btrace_target_info *tinfo) +{ + /* Ignore errors. */ + linux_disable_btrace (tinfo); +} + +/* -Wmissing-prototypes */ +extern initialize_file_ftype _initialize_i386_linux_nat; + void _initialize_i386_linux_nat (void) { @@ -807,6 +1101,15 @@ _initialize_i386_linux_nat (void) /* Fill in the generic GNU/Linux methods. */ t = linux_target (); + i386_use_watchpoints (t); + + i386_dr_low.set_control = i386_linux_dr_set_control; + i386_dr_low.set_addr = i386_linux_dr_set_addr; + i386_dr_low.get_addr = i386_linux_dr_get_addr; + i386_dr_low.get_status = i386_linux_dr_get_status; + i386_dr_low.get_control = i386_linux_dr_get_control; + i386_set_debug_register_length (4); + /* Override the default ptrace resume method. */ t->to_resume = i386_linux_resume; @@ -818,6 +1121,19 @@ _initialize_i386_linux_nat (void) t->to_fetch_registers = i386_linux_fetch_inferior_registers; t->to_store_registers = i386_linux_store_inferior_registers; + t->to_read_description = i386_linux_read_description; + + /* Add btrace methods. */ + t->to_supports_btrace = linux_supports_btrace; + t->to_enable_btrace = i386_linux_enable_btrace; + t->to_disable_btrace = i386_linux_disable_btrace; + t->to_teardown_btrace = i386_linux_teardown_btrace; + t->to_read_btrace = linux_read_btrace; + /* Register the target. */ linux_nat_add_target (t); + linux_nat_set_new_thread (t, i386_linux_new_thread); + linux_nat_set_new_fork (t, i386_linux_new_fork); + linux_nat_set_forget_process (t, i386_forget_process); + linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume); }