mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
1eb3991427
I recently stumbled on this code mentioning Linux kernel 2.6.25, and thought it could be time for some spring cleaning (newer GDBs probably don't need to supports 12-year old kernels). I then found that the "legacy" case is probably broken anyway, which gives an even better motivation for its removal. In short, this patch removes the configure checks that check if user_regs_struct contains the fs_base/gs_base fields and adjusts all uses of the HAVE_STRUCT_USER_REGS_STRUCT_{FS,GS}_BASE macros. The longer explanation/rationale follows. Apparently, Linux kernels since 2.6.25 (that's from 2008) have been reliably providing fs_base and gs_base as part of user_regs_struct. Commit df5d438e33d7 in the Linux kernel [1] seems related. This means that we can get these values by reading registers with PTRACE_GETREGS. Previously, these values were obtained using a separate PTRACE_ARCH_PRCTL ptrace call. First, I'm not even sure the configure check was really right in the first place. The user_regs_struct used by GDB comes from /usr/include/x86_64-linux-gnu/sys/user.h (or equivalent on other distros) and is provided by glibc. glibc has had the fs_base/gs_base fields in there for a very long time, at least since this commit from 2001 [2]. The Linux kernel also has its version of user_regs_struct, which I think was exported to user-space at some point. It included the fs_base/gs_base fields since at least this 2002 commit [3]. In any case, my conclusion is that the fields were there long before the aforementioned Linux kernel commit. The kernel commit didn't add these fields, it only made sure that they have reliable values when obtained with PTRACE_GETREGS. So, checking for the presence of the fs_base/gs_base fields in struct user_regs_struct doesn't sound like a good way of knowing if we can reliably get the fs_base/gs_base values from PTRACE_GETREGS. My guess is that if we were using that strategy on a < 2.6.25 kernel, things would not work correctly: - configure would find that the user_regs_struct has the fs_base/gs_base fields (which are probided by glibc anyway) - we would be reading the fs_base/gs_base values using PTRACE_GETREGS, for which the kernel would provide unreliable values Second, I have tried to see how things worked by forcing GDB to not use fs_base/gs_base from PTRACE_GETREGS (forcing it to use the "legacy" code, by configuring with ac_cv_member_struct_user_regs_struct_gs_base=no ac_cv_member_struct_user_regs_struct_fs_base=no Doing so breaks writing registers back to the inferior. For example, calling an inferior functions gives an internal error: (gdb) p malloc(10) /home/smarchi/src/binutils-gdb/gdb/i387-tdep.c:1408: internal-error: invalid i387 regnum 152 The relevant last frames where this error happens are: #8 0x0000563123d262fc in internal_error (file=0x563123e93fd8 "/home/smarchi/src/binutils-gdb/gdb/i387-tdep.c", line=1408, fmt=0x563123e94482 "invalid i387 regnum %d") at /home/smarchi/src/binutils-gdb/gdbsupport/errors.cc:55 #9 0x0000563123047d0d in i387_collect_xsave (regcache=0x5631269453f0, regnum=152, xsave=0x7ffd38402a20, gcore=0) at /home/smarchi/src/binutils-gdb/gdb/i387-tdep.c:1408 #10 0x0000563122c69e8a in amd64_collect_xsave (regcache=0x5631269453f0, regnum=152, xsave=0x7ffd38402a20, gcore=0) at /home/smarchi/src/binutils-gdb/gdb/amd64-tdep.c:3448 #11 0x0000563122c5e94c in amd64_linux_nat_target::store_registers (this=0x56312515fd10 <the_amd64_linux_nat_target>, regcache=0x5631269453f0, regnum=152) at /home/smarchi/src/binutils-gdb/gdb/amd64-linux-nat.c:335 #12 0x00005631234c8c80 in target_store_registers (regcache=0x5631269453f0, regno=152) at /home/smarchi/src/binutils-gdb/gdb/target.c:3485 #13 0x00005631232e8df7 in regcache::raw_write (this=0x5631269453f0, regnum=152, buf=0x56312759e468 "@\225\372\367\377\177") at /home/smarchi/src/binutils-gdb/gdb/regcache.c:765 #14 0x00005631232e8f0c in regcache::cooked_write (this=0x5631269453f0, regnum=152, buf=0x56312759e468 "@\225\372\367\377\177") at /home/smarchi/src/binutils-gdb/gdb/regcache.c:778 #15 0x00005631232e75ec in regcache::restore (this=0x5631269453f0, src=0x5631275eb130) at /home/smarchi/src/binutils-gdb/gdb/regcache.c:283 #16 0x0000563123083fc4 in infcall_suspend_state::restore (this=0x5631273ed930, gdbarch=0x56312718cf20, tp=0x5631270bca90, regcache=0x5631269453f0) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:9103 #17 0x0000563123081eed in restore_infcall_suspend_state (inf_state=0x5631273ed930) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:9151 The problem seems to be that amd64_linux_nat_target::store_registers calls amd64_native_gregset_supplies_p to know whether gregset provides fs_base. When !HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE, amd64_native_gregset_supplies_p returns false. store_registers therefore assumes that it must be an "xstate" register. This is of course wrong, and that leads to the failed assertion when i387_collect_xsave doesn't recognize the register. amd64_linux_nat_target::store_registers could probably be fixed to handle this case, but I don't think it's worth it, given that it would only be to support very old kernels. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=df5d438e33d7fc914ba9b6e0d6b019a8966c5fcc [2] https://sourceware.org/git/?p=glibc.git;a=commit;h=c9cf6ddeebb7bb [3] https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/?id=88e4bc32686ebd0b1111a94f93eba2d334241f68 gdb/ChangeLog: * configure.ac: Remove check for fs_base/gs_base in user_regs_struct. * configure: Re-generate. * config.in: Re-generate. * amd64-nat.c (amd64_native_gregset_reg_offset): Adjust. * amd64-linux-nat.c (amd64_linux_nat_target::fetch_registers, amd64_linux_nat_target::store_registers, ps_get_thread_area, ): Adjust. gdbserver/ChangeLog: * configure.ac: Remove check for fs_base/gs_base in user_regs_struct. * configure: Re-generate. * config.in: Re-generate. * linux-x86-low.cc (x86_64_regmap, x86_fill_gregset, x86_store_gregset): Adjust.
160 lines
4.9 KiB
C
160 lines
4.9 KiB
C
/* Native-dependent code for AMD64.
|
|
|
|
Copyright (C) 2003-2020 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "gdbarch.h"
|
|
#include "regcache.h"
|
|
|
|
#include "i386-tdep.h"
|
|
#include "amd64-tdep.h"
|
|
#include "amd64-nat.h"
|
|
|
|
/* The following bits of code help with implementing debugging 32-bit
|
|
code natively on AMD64. The idea is to define two mappings between
|
|
the register number as used by GDB and the register set used by the
|
|
host to represent the general-purpose registers; one for 32-bit
|
|
code and one for 64-bit code. The mappings are specified by the
|
|
following variables and consist of an array of offsets within the
|
|
register set indexed by register number, and the number of
|
|
registers supported by the mapping. We don't need mappings for the
|
|
floating-point and SSE registers, since the difference between
|
|
64-bit and 32-bit variants are negligible. The difference in the
|
|
number of SSE registers is already handled by the target code. */
|
|
|
|
/* General-purpose register mapping for native 32-bit code. */
|
|
int *amd64_native_gregset32_reg_offset;
|
|
int amd64_native_gregset32_num_regs = I386_NUM_GREGS;
|
|
|
|
/* General-purpose register mapping for native 64-bit code. */
|
|
int *amd64_native_gregset64_reg_offset;
|
|
int amd64_native_gregset64_num_regs = AMD64_NUM_GREGS;
|
|
|
|
/* Return the offset of REGNUM within the appropriate native
|
|
general-purpose register set. */
|
|
|
|
static int
|
|
amd64_native_gregset_reg_offset (struct gdbarch *gdbarch, int regnum)
|
|
{
|
|
int *reg_offset = amd64_native_gregset64_reg_offset;
|
|
int num_regs = amd64_native_gregset64_num_regs;
|
|
|
|
gdb_assert (regnum >= 0);
|
|
|
|
if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
|
|
{
|
|
reg_offset = amd64_native_gregset32_reg_offset;
|
|
num_regs = amd64_native_gregset32_num_regs;
|
|
}
|
|
|
|
if (num_regs > gdbarch_num_regs (gdbarch))
|
|
num_regs = gdbarch_num_regs (gdbarch);
|
|
|
|
if (regnum >= num_regs)
|
|
return -1;
|
|
|
|
return reg_offset[regnum];
|
|
}
|
|
|
|
/* Return whether the native general-purpose register set supplies
|
|
register REGNUM. */
|
|
|
|
int
|
|
amd64_native_gregset_supplies_p (struct gdbarch *gdbarch, int regnum)
|
|
{
|
|
return (amd64_native_gregset_reg_offset (gdbarch, regnum) != -1);
|
|
}
|
|
|
|
|
|
/* Supply register REGNUM, whose contents are stored in GREGS, to
|
|
REGCACHE. If REGNUM is -1, supply all appropriate registers. */
|
|
|
|
void
|
|
amd64_supply_native_gregset (struct regcache *regcache,
|
|
const void *gregs, int regnum)
|
|
{
|
|
const char *regs = (const char *) gregs;
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
int num_regs = amd64_native_gregset64_num_regs;
|
|
int i;
|
|
|
|
if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
|
|
num_regs = amd64_native_gregset32_num_regs;
|
|
|
|
if (num_regs > gdbarch_num_regs (gdbarch))
|
|
num_regs = gdbarch_num_regs (gdbarch);
|
|
|
|
for (i = 0; i < num_regs; i++)
|
|
{
|
|
if (regnum == -1 || regnum == i)
|
|
{
|
|
int offset = amd64_native_gregset_reg_offset (gdbarch, i);
|
|
|
|
if (offset != -1)
|
|
regcache->raw_supply (i, regs + offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Collect register REGNUM from REGCACHE and store its contents in
|
|
GREGS. If REGNUM is -1, collect and store all appropriate
|
|
registers. */
|
|
|
|
void
|
|
amd64_collect_native_gregset (const struct regcache *regcache,
|
|
void *gregs, int regnum)
|
|
{
|
|
char *regs = (char *) gregs;
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
int num_regs = amd64_native_gregset64_num_regs;
|
|
int i;
|
|
|
|
if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
|
|
{
|
|
num_regs = amd64_native_gregset32_num_regs;
|
|
|
|
/* Make sure %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp and
|
|
%eip get zero-extended to 64 bits. */
|
|
for (i = 0; i <= I386_EIP_REGNUM; i++)
|
|
{
|
|
if (regnum == -1 || regnum == i)
|
|
memset (regs + amd64_native_gregset_reg_offset (gdbarch, i), 0, 8);
|
|
}
|
|
/* Ditto for %cs, %ss, %ds, %es, %fs, and %gs. */
|
|
for (i = I386_CS_REGNUM; i <= I386_GS_REGNUM; i++)
|
|
{
|
|
if (regnum == -1 || regnum == i)
|
|
memset (regs + amd64_native_gregset_reg_offset (gdbarch, i), 0, 8);
|
|
}
|
|
}
|
|
|
|
if (num_regs > gdbarch_num_regs (gdbarch))
|
|
num_regs = gdbarch_num_regs (gdbarch);
|
|
|
|
for (i = 0; i < num_regs; i++)
|
|
{
|
|
if (regnum == -1 || regnum == i)
|
|
{
|
|
int offset = amd64_native_gregset_reg_offset (gdbarch, i);
|
|
|
|
if (offset != -1)
|
|
regcache->raw_collect (i, regs + offset);
|
|
}
|
|
}
|
|
}
|