gdb, gdbserver: remove configure check for fs_base/gs_base in user_regs_struct
authorSimon Marchi <simon.marchi@efficios.com>
Mon, 27 Apr 2020 14:46:51 +0000 (10:46 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Mon, 27 Apr 2020 14:47:50 +0000 (10:47 -0400)
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.

gdb/ChangeLog
gdb/amd64-linux-nat.c
gdb/amd64-nat.c
gdb/config.in
gdb/configure
gdb/configure.ac
gdbserver/ChangeLog
gdbserver/config.in
gdbserver/configure
gdbserver/configure.ac
gdbserver/linux-x86-low.cc

index b076a45a9a1d28c64aefe17ffe961c4987d2fe94..78b3ed80244ccf8fa4e9a35415cac4ecabc07c63 100644 (file)
@@ -1,3 +1,13 @@
+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
index 63fc84b0b1431eab0fa6ed797c2cd2493b2d5472..d860571c37d809799ae750ca50d666895094ba1a 100644 (file)
@@ -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
     }
 }
 \f
@@ -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;
        }
index 6d770a1d80a59e74aad087e2bb9768364b3ca4c3..fb3f52234812a7c82c175c7ee7c8eb9fa93eb3f0 100644 (file)
@@ -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];
 }
 
index 118e424580e5b9b3dcec49ff18f6a20f98602b9d..d950515e513aaf9d57f44b91ed918f9d05cbf337 100644 (file)
 /* 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
 
index 7e1af589f77e94931d8c37b616f61db8c2a753fc..b6233adccf215cd84438b9504ec0e1cf1ed791ed 100755 (executable)
@@ -15395,33 +15395,6 @@ _ACEOF
 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; }
index f405a0351d7907d6bec511638b4ad1ce758c8756..9dac11469f5b4760dab2897b25fa39202de00ca0 100644 (file)
@@ -1372,13 +1372,6 @@ AC_CHECK_MEMBERS([struct reg.r_fs, struct reg.r_gs], [], [],
                  [#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,
index f017922d9e1f3175d9dcee59365c98d2379814fb..3b5fd99de146901a53914ad64e9cb8f06c2198d1 100644 (file)
@@ -1,3 +1,12 @@
+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
index 8683ce6830a776e3e157f9f17c5235338d73e9d6..07213aa52739ff0f8fdf1b66e70f815128329f82 100644 (file)
 /* 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
 
index 06edb4514e0e49fc063e67aedfbdeda346e92f59..5479823705e572316661ce4a2016f1fa151d4692 100755 (executable)
@@ -10043,34 +10043,6 @@ cat >>confdefs.h <<_ACEOF
 _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>
 
index 755cc28cee9c0ca2f51dfb378ac55c3b0f3d69c0..090a6dcdb6e92ae6d4a13fa6f68a6dc8ba0ab83a 100644 (file)
@@ -145,14 +145,6 @@ libiberty_INIT
 
 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>
index f6a399e0982379ae41ead2b62bac06b42b01642b..7a65c1d079f86e96d1b0ee3f816e358a0bbdb6f4 100644 (file)
@@ -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