From 1eb399142793d31d1b7a358baad5fded996e02eb Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Mon, 27 Apr 2020 10:46:51 -0400 Subject: [PATCH] gdb, gdbserver: remove configure check for fs_base/gs_base in user_regs_struct 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 , 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. --- gdb/ChangeLog | 10 ++++++ gdb/amd64-linux-nat.c | 62 ++------------------------------------ gdb/amd64-nat.c | 7 ----- gdb/config.in | 6 ---- gdb/configure | 27 ----------------- gdb/configure.ac | 7 ----- gdbserver/ChangeLog | 9 ++++++ gdbserver/config.in | 6 ---- gdbserver/configure | 28 ----------------- gdbserver/configure.ac | 8 ----- gdbserver/linux-x86-low.cc | 29 ------------------ 11 files changed, 22 insertions(+), 177 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b076a45a9a..78b3ed8024 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2020-04-27 Simon Marchi + + * 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 * dwarf2/frame-tailcall.c (dwarf2_tailcall_sniffer_first): Handle diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index 63fc84b0b1..d860571c37 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -259,30 +259,6 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum) 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 } } @@ -348,30 +324,6 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum) 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 } } @@ -408,11 +360,7 @@ ps_get_thread_area (struct ps_prochandle *ph, 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, @@ -423,12 +371,10 @@ ps_get_thread_area (struct ps_prochandle *ph, 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; @@ -440,10 +386,8 @@ ps_get_thread_area (struct ps_prochandle *ph, 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; } diff --git a/gdb/amd64-nat.c b/gdb/amd64-nat.c index 6d770a1d80..fb3f522348 100644 --- a/gdb/amd64-nat.c +++ b/gdb/amd64-nat.c @@ -68,13 +68,6 @@ amd64_native_gregset_reg_offset (struct gdbarch *gdbarch, int regnum) 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]; } diff --git a/gdb/config.in b/gdb/config.in index 118e424580..d950515e51 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -469,12 +469,6 @@ /* 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 header file. */ #undef HAVE_SYS_DEBUGREG_H diff --git a/gdb/configure b/gdb/configure index 7e1af589f7..b6233adccf 100755 --- a/gdb/configure +++ b/gdb/configure @@ -15395,33 +15395,6 @@ _ACEOF fi -# See if 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 -#include -" -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 -#include -" -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 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; } diff --git a/gdb/configure.ac b/gdb/configure.ac index f405a0351d..9dac11469f 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1372,13 +1372,6 @@ AC_CHECK_MEMBERS([struct reg.r_fs, struct reg.r_gs], [], [], [#include #include ]) -# See if 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 -#include ]) - # See if provides the PTRACE_GETREGS request. AC_MSG_CHECKING(for PTRACE_GETREGS) AC_CACHE_VAL(gdb_cv_have_ptrace_getregs, diff --git a/gdbserver/ChangeLog b/gdbserver/ChangeLog index f017922d9e..3b5fd99de1 100644 --- a/gdbserver/ChangeLog +++ b/gdbserver/ChangeLog @@ -1,3 +1,12 @@ +2020-04-27 Simon Marchi + + * 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 * server.cc (handle_search_memory_1): Fix gdb_read_memory return value diff --git a/gdbserver/config.in b/gdbserver/config.in index 8683ce6830..07213aa527 100644 --- a/gdbserver/config.in +++ b/gdbserver/config.in @@ -303,12 +303,6 @@ /* 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 diff --git a/gdbserver/configure b/gdbserver/configure index 06edb4514e..5479823705 100755 --- a/gdbserver/configure +++ b/gdbserver/configure @@ -10043,34 +10043,6 @@ cat >>confdefs.h <<_ACEOF _ACEOF -# See if 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 -#include -" -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 -#include -" -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 #include diff --git a/gdbserver/configure.ac b/gdbserver/configure.ac index 755cc28cee..090a6dcdb6 100644 --- a/gdbserver/configure.ac +++ b/gdbserver/configure.ac @@ -145,14 +145,6 @@ libiberty_INIT AC_CHECK_DECLS([perror, vasprintf, vsnprintf]) -# See if 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 -#include ]) - - AC_CHECK_TYPES(socklen_t, [], [], [#include #include diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc index f6a399e098..7a65c1d079 100644 --- a/gdbserver/linux-x86-low.cc +++ b/gdbserver/linux-x86-low.cc @@ -233,11 +233,7 @@ static const int x86_64_regmap[] = -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) */ @@ -413,19 +409,6 @@ x86_fill_gregset (struct regcache *regcache, void *buf) 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; } @@ -468,18 +451,6 @@ x86_store_gregset (struct regcache *regcache, const void *buf) 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