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.
+2020-04-27 Simon Marchi <simon.marchi@efficios.com>
+
+ * 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.
+
2020-04-27 Luis Machado <luis.machado@linaro.org>
* dwarf2/frame-tailcall.c (dwarf2_tailcall_sniffer_first): Handle
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 (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 (AMD64_GSBASE_REGNUM, &base);
- }
- }
-#endif
}
}
if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
perror_with_name (_("Couldn't write floating point 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 (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 (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
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,
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;
return PS_OK;
}
}
-#endif
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
- return PS_OK;
break;
+
default: /* Should not happen. */
return PS_BADADDR;
}
if (regnum >= num_regs)
return -1;
- /* Kernels that predate Linux 2.6.25 don't provide access to
- these segment registers in user_regs_struct. */
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- if (regnum == AMD64_FSBASE_REGNUM || regnum == AMD64_GSBASE_REGNUM)
- return -1;
-#endif
-
return reg_offset[regnum];
}
/* Define to 1 if `td_pcb' is a member of `struct thread'. */
#undef HAVE_STRUCT_THREAD_TD_PCB
-/* Define to 1 if `fs_base' is a member of `struct user_regs_struct'. */
-#undef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
-
-/* Define to 1 if `gs_base' is a member of `struct user_regs_struct'. */
-#undef HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE
-
/* Define to 1 if you have the <sys/debugreg.h> header file. */
#undef HAVE_SYS_DEBUGREG_H
fi
-# See if <sys/user.h> supports the %fs_base and %gs_bas amd64 segment registers.
-# Older amd64 Linux's don't have the fs_base and gs_base members of
-# `struct user_regs_struct'.
-ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "fs_base" "ac_cv_member_struct_user_regs_struct_fs_base" "#include <sys/types.h>
-#include <sys/user.h>
-"
-if test "x$ac_cv_member_struct_user_regs_struct_fs_base" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE 1
-_ACEOF
-
-
-fi
-ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "gs_base" "ac_cv_member_struct_user_regs_struct_gs_base" "#include <sys/types.h>
-#include <sys/user.h>
-"
-if test "x$ac_cv_member_struct_user_regs_struct_gs_base" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE 1
-_ACEOF
-
-
-fi
-
-
# See if <sys/ptrace.h> provides the PTRACE_GETREGS request.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTRACE_GETREGS" >&5
$as_echo_n "checking for PTRACE_GETREGS... " >&6; }
[#include <sys/types.h>
#include <machine/reg.h>])
-# See if <sys/user.h> supports the %fs_base and %gs_bas amd64 segment registers.
-# Older amd64 Linux's don't have the fs_base and gs_base members of
-# `struct user_regs_struct'.
-AC_CHECK_MEMBERS([struct user_regs_struct.fs_base, struct user_regs_struct.gs_base],
- [], [], [#include <sys/types.h>
-#include <sys/user.h>])
-
# See if <sys/ptrace.h> provides the PTRACE_GETREGS request.
AC_MSG_CHECKING(for PTRACE_GETREGS)
AC_CACHE_VAL(gdb_cv_have_ptrace_getregs,
+2020-04-27 Simon Marchi <simon.marchi@efficios.com>
+
+ * 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.
+
2020-04-22 Hannes Domani <ssbssa@yahoo.de>
* server.cc (handle_search_memory_1): Fix gdb_read_memory return value
/* Define to 1 if `st_blocks' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BLOCKS
-/* Define to 1 if `fs_base' is a member of `struct user_regs_struct'. */
-#undef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
-
-/* Define to 1 if `gs_base' is a member of `struct user_regs_struct'. */
-#undef HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE
-
/* Define to 1 if the target supports __sync_*_compare_and_swap */
#undef HAVE_SYNC_BUILTINS
_ACEOF
-# See if <sys/user.h> supports the %fs_base and %gs_bas amd64 segment registers.
-# Older amd64 Linux's don't have the fs_base and gs_base members of
-# `struct user_regs_struct'.
-ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "fs_base" "ac_cv_member_struct_user_regs_struct_fs_base" "#include <sys/types.h>
-#include <sys/user.h>
-"
-if test "x$ac_cv_member_struct_user_regs_struct_fs_base" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE 1
-_ACEOF
-
-
-fi
-ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "gs_base" "ac_cv_member_struct_user_regs_struct_gs_base" "#include <sys/types.h>
-#include <sys/user.h>
-"
-if test "x$ac_cv_member_struct_user_regs_struct_gs_base" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE 1
-_ACEOF
-
-
-fi
-
-
-
ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include <sys/types.h>
#include <sys/socket.h>
AC_CHECK_DECLS([perror, vasprintf, vsnprintf])
-# See if <sys/user.h> supports the %fs_base and %gs_bas amd64 segment registers.
-# Older amd64 Linux's don't have the fs_base and gs_base members of
-# `struct user_regs_struct'.
-AC_CHECK_MEMBERS([struct user_regs_struct.fs_base, struct user_regs_struct.gs_base],
- [], [], [#include <sys/types.h>
-#include <sys/user.h>])
-
-
AC_CHECK_TYPES(socklen_t, [], [],
[#include <sys/types.h>
#include <sys/socket.h>
-1,
-1, -1, -1, -1, -1, -1, -1, -1,
ORIG_RAX * 8,
-#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
21 * 8, 22 * 8,
-#else
- -1, -1,
-#endif
-1, -1, -1, -1, /* MPX registers BND0 ... BND3. */
-1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
-1, -1, -1, -1, -1, -1, -1, -1, /* xmm16 ... xmm31 (AVX512) */
if (x86_64_regmap[i] != -1)
collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- {
- unsigned long base;
- int lwpid = lwpid_of (current_thread);
-
- collect_register_by_name (regcache, "fs_base", &base);
- ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_FS);
-
- collect_register_by_name (regcache, "gs_base", &base);
- ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_SET_GS);
- }
-#endif
-
return;
}
if (x86_64_regmap[i] != -1)
supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
- {
- unsigned long base;
- int lwpid = lwpid_of (current_thread);
-
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
- supply_register_by_name (regcache, "fs_base", &base);
-
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_GS) == 0)
- supply_register_by_name (regcache, "gs_base", &base);
- }
-#endif
return;
}
#endif