/* Native-dependent code for GNU/Linux x86-64.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 2001-2017 Free Software Foundation, Inc.
Contributed by Jiri Smid, SuSE Labs.
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,
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 <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "inferior.h"
-#include "gdbcore.h"
#include "regcache.h"
-#include "linux-nat.h"
-#include "amd64-linux-tdep.h"
-
-#include "gdb_assert.h"
-#include "gdb_string.h"
-#include <sys/ptrace.h>
-#include <sys/debugreg.h>
-#include <sys/syscall.h>
-#include <sys/procfs.h>
+#include "elf/common.h"
+#include <sys/uio.h>
+#include "nat/gdb_ptrace.h"
#include <asm/prctl.h>
-/* FIXME ezannoni-2003-07-09: we need <sys/reg.h> to be included after
- <asm/ptrace.h> because the latter redefines FS and GS for no apparent
- reason, and those definitions don't match the ones that libpthread_db
- uses, which come from <sys/reg.h>. */
-/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have
- been removed from ptrace.h in the kernel. However, better safe than
- sorry. */
-#include <asm/ptrace.h>
#include <sys/reg.h>
-#include "gdb_proc_service.h"
-
-/* Prototypes for supply_gregset etc. */
#include "gregset.h"
+#include "gdb_proc_service.h"
+#include "amd64-nat.h"
+#include "linux-nat.h"
#include "amd64-tdep.h"
+#include "amd64-linux-tdep.h"
#include "i386-linux-tdep.h"
-#include "amd64-nat.h"
+#include "x86-xstate.h"
-/* Mapping between the general-purpose registers in GNU/Linux x86-64
- `struct user' format and GDB's register cache layout. */
+#include "x86-linux-nat.h"
+#include "nat/linux-ptrace.h"
+#include "nat/amd64-linux-siginfo.h"
-static int amd64_linux_gregset64_reg_offset[] =
-{
- RAX * 8, RBX * 8, /* %rax, %rbx */
- RCX * 8, RDX * 8, /* %rcx, %rdx */
- RSI * 8, RDI * 8, /* %rsi, %rdi */
- RBP * 8, RSP * 8, /* %rbp, %rsp */
- R8 * 8, R9 * 8, /* %r8 ... */
- R10 * 8, R11 * 8,
- R12 * 8, R13 * 8,
- R14 * 8, R15 * 8, /* ... %r15 */
- RIP * 8, EFLAGS * 8, /* %rip, %eflags */
- CS * 8, SS * 8, /* %cs, %ss */
- DS * 8, ES * 8, /* %ds, %es */
- FS * 8, GS * 8, /* %fs, %gs */
- -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
-};
-\f
+/* This definition comes from prctl.h. Kernels older than 2.5.64
+ do not have it. */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL 30
+#endif
/* Mapping between the general-purpose registers in GNU/Linux x86-64
`struct user' format and GDB's register cache layout for GNU/Linux
-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, -1, -1, -1, -1,
+ -1, -1, -1, -1, /* MPX registers BND0 ... BND3. */
+ -1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512) */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm7 (AVX512) */
+ -1, /* PKEYS register PKRU */
+ ORIG_RAX * 8 /* "orig_eax" */
};
\f
in *GREGSETP. */
void
-supply_gregset (elf_gregset_t *gregsetp)
+supply_gregset (struct regcache *regcache, const elf_gregset_t *gregsetp)
{
- amd64_supply_native_gregset (current_regcache, gregsetp, -1);
+ amd64_supply_native_gregset (regcache, gregsetp, -1);
}
/* Fill register REGNUM (if it is a general-purpose register) in
do this for all registers. */
void
-fill_gregset (elf_gregset_t *gregsetp, int regnum)
+fill_gregset (const struct regcache *regcache,
+ elf_gregset_t *gregsetp, int regnum)
{
- amd64_collect_native_gregset (current_regcache, gregsetp, regnum);
+ amd64_collect_native_gregset (regcache, gregsetp, regnum);
}
/* Transfering floating-point registers between GDB, inferiors and cores. */
values in *FPREGSETP. */
void
-supply_fpregset (elf_fpregset_t *fpregsetp)
+supply_fpregset (struct regcache *regcache, const elf_fpregset_t *fpregsetp)
{
- amd64_supply_fxsave (current_regcache, -1, fpregsetp);
+ amd64_supply_fxsave (regcache, -1, fpregsetp);
}
/* Fill register REGNUM (if it is a floating-point or SSE register) in
-1, do this for all registers. */
void
-fill_fpregset (elf_fpregset_t *fpregsetp, int regnum)
+fill_fpregset (const struct regcache *regcache,
+ elf_fpregset_t *fpregsetp, int regnum)
{
- amd64_collect_fxsave (current_regcache, regnum, fpregsetp);
+ amd64_collect_fxsave (regcache, regnum, fpregsetp);
}
\f
registers). */
static void
-amd64_linux_fetch_inferior_registers (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;
/* GNU/Linux LWP ID's are process ID's. */
- tid = TIDGET (inferior_ptid);
+ tid = ptid_get_lwp (regcache_get_ptid (regcache));
if (tid == 0)
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+ tid = ptid_get_pid (regcache_get_ptid (regcache)); /* Not a threaded program. */
- if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+ if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
{
elf_gregset_t regs;
if (ptrace (PTRACE_GETREGS, tid, 0, (long) ®s) < 0)
perror_with_name (_("Couldn't get registers"));
- amd64_supply_native_gregset (current_regcache, ®s, -1);
+ amd64_supply_native_gregset (regcache, ®s, -1);
if (regnum != -1)
return;
}
- if (regnum == -1 || !amd64_native_gregset_supplies_p (regnum))
+ if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, 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 == TRIBOOL_TRUE)
+ {
+ char xstateregs[X86_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_fxsave (current_regcache, -1, &fpregs);
+ 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);
+ }
+#ifndef 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 base;
+
+ if (regnum == -1 || regnum == AMD64_FSBASE_REGNUM)
+ {
+ if (ptrace (PTRACE_ARCH_PRCTL, tid, &base, ARCH_GET_FS) < 0)
+ perror_with_name (_("Couldn't get segment register fs_base"));
+
+ regcache_raw_supply (regcache, AMD64_FSBASE_REGNUM, &base);
+ }
+
+ if (regnum == -1 || regnum == AMD64_GSBASE_REGNUM)
+ {
+ if (ptrace (PTRACE_ARCH_PRCTL, tid, &base, ARCH_GET_GS) < 0)
+ perror_with_name (_("Couldn't get segment register gs_base"));
+
+ regcache_raw_supply (regcache, AMD64_GSBASE_REGNUM, &base);
+ }
+ }
+#endif
}
}
registers). */
static void
-amd64_linux_store_inferior_registers (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;
/* GNU/Linux LWP ID's are process ID's. */
- tid = TIDGET (inferior_ptid);
+ tid = ptid_get_lwp (regcache_get_ptid (regcache));
if (tid == 0)
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+ tid = ptid_get_pid (regcache_get_ptid (regcache)); /* Not a threaded program. */
- if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+ if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
{
elf_gregset_t regs;
if (ptrace (PTRACE_GETREGS, tid, 0, (long) ®s) < 0)
perror_with_name (_("Couldn't get registers"));
- amd64_collect_native_gregset (current_regcache, ®s, regnum);
+ amd64_collect_native_gregset (regcache, ®s, regnum);
if (ptrace (PTRACE_SETREGS, tid, 0, (long) ®s) < 0)
perror_with_name (_("Couldn't write registers"));
return;
}
- if (regnum == -1 || !amd64_native_gregset_supplies_p (regnum))
+ if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
{
elf_fpregset_t fpregs;
- if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
- perror_with_name (_("Couldn't get floating point status"));
-
- amd64_collect_fxsave (current_regcache, regnum, &fpregs);
-
- if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
- perror_with_name (_("Couldn't write floating point status"));
-
- return;
- }
-}
-\f
-
-static unsigned long
-amd64_linux_dr_get (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);
-
- /* 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 vectore. For now, just return zero if the
- ptrace call fails. */
- errno = 0;
- value = ptrace (PT_READ_U, 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;
-}
-
-static void
-amd64_linux_dr_set (int regnum, unsigned long value)
-{
- int tid;
+ if (have_ptrace_getregset == TRIBOOL_TRUE)
+ {
+ char xstateregs[X86_XSTATE_MAX_SIZE];
+ struct iovec iov;
- /* 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);
+ 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"));
- errno = 0;
- ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value);
- if (errno != 0)
- perror_with_name (_("Couldn't write debug register"));
-}
+ amd64_collect_xsave (regcache, regnum, xstateregs, 0);
-void
-amd64_linux_dr_set_control (unsigned long control)
-{
- amd64_linux_dr_set (DR_CONTROL, control);
-}
-
-void
-amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
-{
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
- amd64_linux_dr_set (DR_FIRSTADDR + regnum, addr);
-}
+ 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"));
-void
-amd64_linux_dr_reset_addr (int regnum)
-{
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+ amd64_collect_fxsave (regcache, regnum, &fpregs);
- amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
-}
+ if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
+ perror_with_name (_("Couldn't write floating point status"));
+ }
-unsigned long
-amd64_linux_dr_get_status (void)
-{
- return amd64_linux_dr_get (DR_STATUS);
+#ifndef 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. */
+ void *base;
+
+ if (regnum == -1 || regnum == AMD64_FSBASE_REGNUM)
+ {
+ regcache_raw_collect (regcache, AMD64_FSBASE_REGNUM, &base);
+
+ if (ptrace (PTRACE_ARCH_PRCTL, tid, base, ARCH_SET_FS) < 0)
+ perror_with_name (_("Couldn't write segment register fs_base"));
+ }
+ if (regnum == -1 || regnum == AMD64_GSBASE_REGNUM)
+ {
+
+ regcache_raw_collect (regcache, AMD64_GSBASE_REGNUM, &base);
+ if (ptrace (PTRACE_ARCH_PRCTL, tid, base, ARCH_SET_GS) < 0)
+ perror_with_name (_("Couldn't write segment register gs_base"));
+ }
+ }
+#endif
+ }
}
\f
a request for a thread's local storage address. */
ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
+ps_get_thread_area (struct ps_prochandle *ph,
lwpid_t lwpid, int idx, void **base)
{
- if (gdbarch_ptr_bit (current_gdbarch) == 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
- the thread's local storage. See i386-linux-nat.c more
- info. */
- unsigned int desc[4];
-
- /* This code assumes that "int" is 32 bits and that
- GET_THREAD_AREA returns no more than 4 int values. */
- gdb_assert (sizeof (int) == 4);
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
- if (ptrace (PTRACE_GET_THREAD_AREA,
- lwpid, (void *) (long) idx, (unsigned long) &desc) < 0)
- return PS_ERR;
-
- /* Extend the value to 64 bits. Here it's assumed that a "long"
- and a "void *" are the same. */
- (*base) = (void *) (long) desc[1];
- return PS_OK;
+ unsigned int base_addr;
+ ps_err_e result;
+
+ result = x86_linux_get_thread_area (lwpid, (void *) (long) idx,
+ &base_addr);
+ if (result == PS_OK)
+ {
+ /* Extend the value to 64 bits. Here it's assumed that
+ a "long" and a "void *" are the same. */
+ (*base) = (void *) (long) base_addr;
+ }
+ return result;
}
else
{
- /* This definition comes from prctl.h, but some kernels may not
- have it. */
-#ifndef PTRACE_ARCH_PRCTL
-#define PTRACE_ARCH_PRCTL 30
-#endif
+
/* FIXME: ezannoni-2003-07-09 see comment above about include
file order. We could be getting bogus values for these two. */
gdb_assert (FS < ELF_NGREG);
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;
}
\f
-static void (*super_post_startup_inferior) (ptid_t ptid);
+/* Convert a ptrace/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. Returns true if any
+ conversion was done; false otherwise. If DIRECTION is 1, then copy
+ from INF to PTRACE. If DIRECTION is 0, copy from PTRACE to
+ INF. */
-static void
-amd64_linux_child_post_startup_inferior (ptid_t ptid)
+static int
+amd64_linux_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
{
- i386_cleanup_dregs ();
- super_post_startup_inferior (ptid);
+ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+
+ /* Is the inferior 32-bit? If so, then do fixup the siginfo
+ object. */
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+ FIXUP_32);
+ /* No fixup for native x32 GDB. */
+ else if (gdbarch_addr_bit (gdbarch) == 32 && sizeof (void *) == 8)
+ return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+ FIXUP_X32);
+ else
+ return 0;
}
-\f
/* Provide a prototype to silence -Wmissing-prototypes. */
void _initialize_amd64_linux_nat (void);
amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset;
amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS;
- amd64_native_gregset64_reg_offset = amd64_linux_gregset64_reg_offset;
+ amd64_native_gregset64_reg_offset = amd64_linux_gregset_reg_offset;
amd64_native_gregset64_num_regs = AMD64_LINUX_NUM_REGS;
gdb_assert (ARRAY_SIZE (amd64_linux_gregset32_reg_offset)
== amd64_native_gregset32_num_regs);
- gdb_assert (ARRAY_SIZE (amd64_linux_gregset64_reg_offset)
- == amd64_native_gregset64_num_regs);
- /* Fill in the generic GNU/Linux methods. */
- t = linux_target ();
-
- /* 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;
+ /* Create a generic x86 GNU/Linux target. */
+ t = x86_linux_create_target ();
/* Add our register access methods. */
t->to_fetch_registers = amd64_linux_fetch_inferior_registers;
t->to_store_registers = amd64_linux_store_inferior_registers;
- /* Register the target. */
- linux_nat_add_target (t);
+ /* Add the target. */
+ x86_linux_add_target (t);
+
+ /* Add our siginfo layout converter. */
+ linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
}