diff mbox

[1/9] Support the fs_base and gs_base registers on i386.

Message ID afb3f5e132cc8844216f7da3084b2db9ddf73d14.1548180889.git.jhb@FreeBSD.org
State New
Headers show

Commit Message

John Baldwin Jan. 22, 2019, 6:42 p.m. UTC
As on amd64, these registers hold the base address of the fs and gs
segments, respectively.  For i386 these two registers are 32 bits.

gdb/ChangeLog:

	* amd64-fbsd-nat.c (amd64_fbsd_nat_target::read_description):
	Update calls to i386_target_description to add 'segments'
	parameter.
	* amd64-tdep.c (amd64_init_abi): Set tdep->fsbase_regnum.  Don't
	add segment base registers.
	* arch/i386.c (i386_create_target_description): Add 'segments'
	parameter to enable segment base registers.
	* arch/i386.h (i386_create_target_description): Likewise.
	* features/i386/32bit-segments.xml: New file.
	* features/i386/32bit-segments.c: Generate.
	* i386-fbsd-nat.c (i386_fbsd_nat_target::read_description): Update
	call to i386_target_description to add 'segments' parameter.
	* i386-fbsd-tdep.c (i386fbsd_core_read_description): Likewise.
	* i386-go32-tdep.c (i386_go32_init_abi): Likewise.
	* i386-linux-tdep.c (i386_linux_read_description): Likewise.
	* i386-tdep.c (i386_validate_tdesc_p): Add segment base registers
	if feature is present.
	(i386_gdbarch_init): Pass I386_NUM_REGS to set_gdbarch_num_regs.
	Add 'segments' parameter to call to i386_target_description.
	(i386_target_description): Add 'segments' parameter to enable
	segment base registers.
	(_initialize_i386_tdep) [GDB_SELF_TEST]: Add 'segments' parameter
	to call to i386_target_description.
	* i386-tdep.h (struct gdbarch_tdep): Add 'fsbase_regnum'.
	(enum i386_regnum): Add I386_FSBASE_REGNUM and I386_GSBASE_REGNUM.
	Define I386_NUM_REGS.
	(i386_target_description): Add 'segments' parameter to enable
	segment base registers.

gdb/gdbserver/ChangeLog:

	* linux-x86-tdesc.c (i386_linux_read_description): Update call to
	i386_create_target_description for 'segments' parameter.
	* lynx-i386-low.c (lynx_i386_arch_setup): Likewise.
	* nto-x86-low.c (nto_x86_arch_setup): Likewise.
	* win32-i386-low.c (i386_arch_setup): Likewise.
---
 gdb/ChangeLog                        | 31 ++++++++++++++++++++++++++
 gdb/amd64-fbsd-nat.c                 |  4 ++--
 gdb/amd64-tdep.c                     | 10 +--------
 gdb/arch/i386.c                      |  6 ++++-
 gdb/arch/i386.h                      |  3 ++-
 gdb/features/i386/32bit-segments.c   | 15 +++++++++++++
 gdb/features/i386/32bit-segments.xml | 12 ++++++++++
 gdb/gdbserver/ChangeLog              |  8 +++++++
 gdb/gdbserver/linux-x86-tdesc.c      |  2 +-
 gdb/gdbserver/lynx-i386-low.c        |  2 +-
 gdb/gdbserver/nto-x86-low.c          |  2 +-
 gdb/gdbserver/win32-i386-low.c       |  2 +-
 gdb/i386-fbsd-nat.c                  |  2 +-
 gdb/i386-fbsd-tdep.c                 |  2 +-
 gdb/i386-go32-tdep.c                 |  2 +-
 gdb/i386-linux-tdep.c                |  2 +-
 gdb/i386-tdep.c                      | 33 +++++++++++++++++++++-------
 gdb/i386-tdep.h                      | 12 ++++++++--
 18 files changed, 119 insertions(+), 31 deletions(-)
 create mode 100644 gdb/features/i386/32bit-segments.c
 create mode 100644 gdb/features/i386/32bit-segments.xml

Comments

Simon Marchi Jan. 27, 2019, 3:47 a.m. UTC | #1
On 2019-01-22 1:42 p.m., John Baldwin wrote:
> As on amd64, these registers hold the base address of the fs and gs
> segments, respectively.  For i386 these two registers are 32 bits.

I just started to look at this, still trying to get my head around it and
connect all the dots.  Just a silly question for now:

> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index d4143ae155..1eb0bcdf17 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3107,15 +3107,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
>  
>    if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments") != NULL)
>      {
> -      const struct tdesc_feature *feature =
> -	  tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
> -      struct tdesc_arch_data *tdesc_data_segments =
> -	  (struct tdesc_arch_data *) info.tdep_info;
> -
> -      tdesc_numbered_register (feature, tdesc_data_segments,
> -		       AMD64_FSBASE_REGNUM, "fs_base");
> -      tdesc_numbered_register (feature, tdesc_data_segments,
> -		       AMD64_GSBASE_REGNUM, "gs_base");
> +      tdep->fsbase_regnum = AMD64_FSBASE_REGNUM;
>      }

Can you explain what this change does and why it is needed?  I am a bit lost.

I see you add something equivalent in i386-tdep.c, but as far as I know, they are completely
separate.

Simon
John Baldwin Jan. 28, 2019, 5:54 p.m. UTC | #2
On 1/26/19 7:47 PM, Simon Marchi wrote:
> On 2019-01-22 1:42 p.m., John Baldwin wrote:
>> As on amd64, these registers hold the base address of the fs and gs
>> segments, respectively.  For i386 these two registers are 32 bits.
> 
> I just started to look at this, still trying to get my head around it and
> connect all the dots.  Just a silly question for now:
> 
>> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
>> index d4143ae155..1eb0bcdf17 100644
>> --- a/gdb/amd64-tdep.c
>> +++ b/gdb/amd64-tdep.c
>> @@ -3107,15 +3107,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
>>  
>>    if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments") != NULL)
>>      {
>> -      const struct tdesc_feature *feature =
>> -	  tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
>> -      struct tdesc_arch_data *tdesc_data_segments =
>> -	  (struct tdesc_arch_data *) info.tdep_info;
>> -
>> -      tdesc_numbered_register (feature, tdesc_data_segments,
>> -		       AMD64_FSBASE_REGNUM, "fs_base");
>> -      tdesc_numbered_register (feature, tdesc_data_segments,
>> -		       AMD64_GSBASE_REGNUM, "gs_base");
>> +      tdep->fsbase_regnum = AMD64_FSBASE_REGNUM;
>>      }
> 
> Can you explain what this change does and why it is needed?  I am a bit lost.
> 
> I see you add something equivalent in i386-tdep.c, but as far as I know, they are completely
> separate.

It took a while for me to understand this as well. :-/

The 64-bit (amd64) and 32-bit (i386) gdbarch's share a fair bit of code.
In particular, the 64-bit gdbarch is "inherited" from the 32-bit one, but
overrides various behaviors.  Specifically, see the comment above
i386_gdbarch_init.  amd64_init_abi is called from OSABI specific handlers
part way through i386_gdbarch_init from gdbarch_init_osabi() to apply
64-bit overrides.

One of the things that gets overridden is that 32-bit and 64-bit gdbarch's
use different register numbers.  This is because 64-bit x86 has r8-15.
The first 8 registers are the same, but the remaining registers for FPU,
SSE, etc. are all shifted by 8 for amd64.  There's a comment that hints at
this in i386-tdep.h:

/* GDB's i386 target supports both the 32-bit Intel Architecture
   (IA-32) and the 64-bit AMD x86-64 architecture.  Internally it uses
   a similar register layout for both.

   - General purpose registers
   - FPU data registers
   - FPU control registers
   - SSE data registers
   - SSE control register

   The general purpose registers for the x86-64 architecture are quite
   different from IA-32.  Therefore, gdbarch_fp0_regnum
   determines the register number at which the FPU data registers
   start.  The number of FPU data and control registers is the same
   for both architectures.  The number of SSE registers however,
   differs and is determined by the num_xmm_regs member of `struct
   gdbarch_tdep'.  */

As the comment notes, the way this is handled for the FPU registers is that
tdep->fp0_regnum holds the value of the first FPU register (AMD64_ST0_REGNUM
vs I386_ST0_REGNUM).  This same approach is then used for all the other
shared register banks (tdep->mm0_regnum, ymm0_regnum, zmm0_regnum, etc.).
Helper variables are also used to handle the different numbers of SSE/AVX
registers on 32-bit vs 64-bit.

i386_validate_tdesc_p() is called after gdbarch_init_osabi() to add the
shared register sets.  It checks for each feature being present.  If it is,
it first checks to see if the corresponding tdep fields (e.g. ymm0_regnum)
have been set.  If they aren't, it sets them to the I386 values.  Then it
adds the appropriate registers for each feature.  In the case of a 64-bit
architecture, amd64_init_abi() is called from gdbarch_init_osabi() before
i386_validate_tdesc_p() is called.  amd64_init_abi() sets the various tdep
variables for any features that are present to the AMD64 values.  Then when
i386_validate_tdesc_p() runs, it still adds the registers, but it does so
using the 64-bit register numbers instead of 32-bit register numbers.

When fs_base and gs_base were first added, they were only added on amd64,
so they were not treated as a shared register set.  Instead, they were
just added directly in amd64_init_abi() using the AMD64 register numbers.
This particular part of the patch (along with the change to
i386_validate_tdesc_p in i386-tdep.c) is changing the fs_base/gs_base
register set to be a shared register set like FPU, AVX/SSE, etc.  It does
this by adding a new tdep->fsbase_regnum to hold the base number of the
two registers.  It then changes amd64_init_abi() to just set the tdep
field as it does for other register sets and changes i386_validate_tdesc_p
to add the registers at the appropriate register number, defaulting to the
I386 values if the tdep field wasn't set.

Hopefully that isn't too confusing.  It took me a while to figure it out
myself.
Simon Marchi Feb. 2, 2019, 3:11 p.m. UTC | #3
On 2019-01-28 12:54, John Baldwin wrote:
> On 1/26/19 7:47 PM, Simon Marchi wrote:
>> On 2019-01-22 1:42 p.m., John Baldwin wrote:
>>> As on amd64, these registers hold the base address of the fs and gs
>>> segments, respectively.  For i386 these two registers are 32 bits.
>> 
>> I just started to look at this, still trying to get my head around it 
>> and
>> connect all the dots.  Just a silly question for now:
>> 
>>> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
>>> index d4143ae155..1eb0bcdf17 100644
>>> --- a/gdb/amd64-tdep.c
>>> +++ b/gdb/amd64-tdep.c
>>> @@ -3107,15 +3107,7 @@ amd64_init_abi (struct gdbarch_info info, 
>>> struct gdbarch *gdbarch,
>>> 
>>>    if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments") != 
>>> NULL)
>>>      {
>>> -      const struct tdesc_feature *feature =
>>> -	  tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
>>> -      struct tdesc_arch_data *tdesc_data_segments =
>>> -	  (struct tdesc_arch_data *) info.tdep_info;
>>> -
>>> -      tdesc_numbered_register (feature, tdesc_data_segments,
>>> -		       AMD64_FSBASE_REGNUM, "fs_base");
>>> -      tdesc_numbered_register (feature, tdesc_data_segments,
>>> -		       AMD64_GSBASE_REGNUM, "gs_base");
>>> +      tdep->fsbase_regnum = AMD64_FSBASE_REGNUM;
>>>      }
>> 
>> Can you explain what this change does and why it is needed?  I am a 
>> bit lost.
>> 
>> I see you add something equivalent in i386-tdep.c, but as far as I 
>> know, they are completely
>> separate.
> 
> It took a while for me to understand this as well. :-/
> 
> The 64-bit (amd64) and 32-bit (i386) gdbarch's share a fair bit of 
> code.
> In particular, the 64-bit gdbarch is "inherited" from the 32-bit one, 
> but
> overrides various behaviors.  Specifically, see the comment above
> i386_gdbarch_init.  amd64_init_abi is called from OSABI specific 
> handlers
> part way through i386_gdbarch_init from gdbarch_init_osabi() to apply
> 64-bit overrides.
> 
> One of the things that gets overridden is that 32-bit and 64-bit 
> gdbarch's
> use different register numbers.  This is because 64-bit x86 has r8-15.
> The first 8 registers are the same, but the remaining registers for 
> FPU,
> SSE, etc. are all shifted by 8 for amd64.  There's a comment that hints 
> at
> this in i386-tdep.h:
> 
> /* GDB's i386 target supports both the 32-bit Intel Architecture
>    (IA-32) and the 64-bit AMD x86-64 architecture.  Internally it uses
>    a similar register layout for both.
> 
>    - General purpose registers
>    - FPU data registers
>    - FPU control registers
>    - SSE data registers
>    - SSE control register
> 
>    The general purpose registers for the x86-64 architecture are quite
>    different from IA-32.  Therefore, gdbarch_fp0_regnum
>    determines the register number at which the FPU data registers
>    start.  The number of FPU data and control registers is the same
>    for both architectures.  The number of SSE registers however,
>    differs and is determined by the num_xmm_regs member of `struct
>    gdbarch_tdep'.  */
> 
> As the comment notes, the way this is handled for the FPU registers is 
> that
> tdep->fp0_regnum holds the value of the first FPU register 
> (AMD64_ST0_REGNUM
> vs I386_ST0_REGNUM).  This same approach is then used for all the other
> shared register banks (tdep->mm0_regnum, ymm0_regnum, zmm0_regnum, 
> etc.).
> Helper variables are also used to handle the different numbers of 
> SSE/AVX
> registers on 32-bit vs 64-bit.
> 
> i386_validate_tdesc_p() is called after gdbarch_init_osabi() to add the
> shared register sets.  It checks for each feature being present.  If it 
> is,
> it first checks to see if the corresponding tdep fields (e.g. 
> ymm0_regnum)
> have been set.  If they aren't, it sets them to the I386 values.  Then 
> it
> adds the appropriate registers for each feature.  In the case of a 
> 64-bit
> architecture, amd64_init_abi() is called from gdbarch_init_osabi() 
> before
> i386_validate_tdesc_p() is called.  amd64_init_abi() sets the various 
> tdep
> variables for any features that are present to the AMD64 values.  Then 
> when
> i386_validate_tdesc_p() runs, it still adds the registers, but it does 
> so
> using the 64-bit register numbers instead of 32-bit register numbers.
> 
> When fs_base and gs_base were first added, they were only added on 
> amd64,
> so they were not treated as a shared register set.  Instead, they were
> just added directly in amd64_init_abi() using the AMD64 register 
> numbers.
> This particular part of the patch (along with the change to
> i386_validate_tdesc_p in i386-tdep.c) is changing the fs_base/gs_base
> register set to be a shared register set like FPU, AVX/SSE, etc.  It 
> does
> this by adding a new tdep->fsbase_regnum to hold the base number of the
> two registers.  It then changes amd64_init_abi() to just set the tdep
> field as it does for other register sets and changes 
> i386_validate_tdesc_p
> to add the registers at the appropriate register number, defaulting to 
> the
> I386 values if the tdep field wasn't set.
> 
> Hopefully that isn't too confusing.  It took me a while to figure it 
> out
> myself.

Thanks for the explanation, it does clear it up.

There was nothing else in this patch that stood out to me.

Simon
diff mbox

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8e03dbf883..4afd5b664e 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,34 @@ 
+2019-01-22  John Baldwin  <jhb@FreeBSD.org>
+
+	* amd64-fbsd-nat.c (amd64_fbsd_nat_target::read_description):
+	Update calls to i386_target_description to add 'segments'
+	parameter.
+	* amd64-tdep.c (amd64_init_abi): Set tdep->fsbase_regnum.  Don't
+	add segment base registers.
+	* arch/i386.c (i386_create_target_description): Add 'segments'
+	parameter to enable segment base registers.
+	* arch/i386.h (i386_create_target_description): Likewise.
+	* features/i386/32bit-segments.xml: New file.
+	* features/i386/32bit-segments.c: Generate.
+	* i386-fbsd-nat.c (i386_fbsd_nat_target::read_description): Update
+	call to i386_target_description to add 'segments' parameter.
+	* i386-fbsd-tdep.c (i386fbsd_core_read_description): Likewise.
+	* i386-go32-tdep.c (i386_go32_init_abi): Likewise.
+	* i386-linux-tdep.c (i386_linux_read_description): Likewise.
+	* i386-tdep.c (i386_validate_tdesc_p): Add segment base registers
+	if feature is present.
+	(i386_gdbarch_init): Pass I386_NUM_REGS to set_gdbarch_num_regs.
+	Add 'segments' parameter to call to i386_target_description.
+	(i386_target_description): Add 'segments' parameter to enable
+	segment base registers.
+	(_initialize_i386_tdep) [GDB_SELF_TEST]: Add 'segments' parameter
+	to call to i386_target_description.
+	* i386-tdep.h (struct gdbarch_tdep): Add 'fsbase_regnum'.
+	(enum i386_regnum): Add I386_FSBASE_REGNUM and I386_GSBASE_REGNUM.
+	Define I386_NUM_REGS.
+	(i386_target_description): Add 'segments' parameter to enable
+	segment base registers.
+
 2019-01-22  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
 	* event-top.c (handle_line_of_input): use unique_xmalloc_ptr for
diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 6d8e12b033..39ba69d9bf 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -190,13 +190,13 @@  amd64_fbsd_nat_target::read_description ()
       if (is64)
 	return amd64_target_description (xcr0, true);
       else
-	return i386_target_description (xcr0);
+	return i386_target_description (xcr0, false);
     }
 #endif
   if (is64)
     return amd64_target_description (X86_XSTATE_SSE_MASK, true);
   else
-    return i386_target_description (X86_XSTATE_SSE_MASK);
+    return i386_target_description (X86_XSTATE_SSE_MASK, false);
 }
 
 #if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index d4143ae155..1eb0bcdf17 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3107,15 +3107,7 @@  amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
 
   if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments") != NULL)
     {
-      const struct tdesc_feature *feature =
-	  tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
-      struct tdesc_arch_data *tdesc_data_segments =
-	  (struct tdesc_arch_data *) info.tdep_info;
-
-      tdesc_numbered_register (feature, tdesc_data_segments,
-		       AMD64_FSBASE_REGNUM, "fs_base");
-      tdesc_numbered_register (feature, tdesc_data_segments,
-		       AMD64_GSBASE_REGNUM, "gs_base");
+      tdep->fsbase_regnum = AMD64_FSBASE_REGNUM;
     }
 
   if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys") != NULL)
diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
index 73987da97b..311b5fd8c8 100644
--- a/gdb/arch/i386.c
+++ b/gdb/arch/i386.c
@@ -28,11 +28,12 @@ 
 #include "../features/i386/32bit-avx512.c"
 #include "../features/i386/32bit-mpx.c"
 #include "../features/i386/32bit-pkeys.c"
+#include "../features/i386/32bit-segments.c"
 
 /* Create i386 target descriptions according to XCR0.  */
 
 target_desc *
-i386_create_target_description (uint64_t xcr0, bool is_linux)
+i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
 {
   target_desc *tdesc = allocate_target_description ();
 
@@ -53,6 +54,9 @@  i386_create_target_description (uint64_t xcr0, bool is_linux)
   if (is_linux)
     regnum = create_feature_i386_32bit_linux (tdesc, regnum);
 
+  if (segments)
+    regnum = create_feature_i386_32bit_segments (tdesc, regnum);
+
   if (xcr0 & X86_XSTATE_AVX)
     regnum = create_feature_i386_32bit_avx (tdesc, regnum);
 
diff --git a/gdb/arch/i386.h b/gdb/arch/i386.h
index a6a801b818..af8c6bd73f 100644
--- a/gdb/arch/i386.h
+++ b/gdb/arch/i386.h
@@ -18,4 +18,5 @@ 
 #include "common/tdesc.h"
 #include <stdint.h>
 
-target_desc *i386_create_target_description (uint64_t xcr0, bool is_linux);
+target_desc *i386_create_target_description (uint64_t xcr0, bool is_linux,
+					     bool segments);
diff --git a/gdb/features/i386/32bit-segments.c b/gdb/features/i386/32bit-segments.c
new file mode 100644
index 0000000000..c22c3dfbc3
--- /dev/null
+++ b/gdb/features/i386/32bit-segments.c
@@ -0,0 +1,15 @@ 
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: 32bit-segments.xml */
+
+#include "common/tdesc.h"
+
+static int
+create_feature_i386_32bit_segments (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.segments");
+  tdesc_create_reg (feature, "fs_base", regnum++, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "gs_base", regnum++, 1, NULL, 32, "int");
+  return regnum;
+}
diff --git a/gdb/features/i386/32bit-segments.xml b/gdb/features/i386/32bit-segments.xml
new file mode 100644
index 0000000000..098948e5ec
--- /dev/null
+++ b/gdb/features/i386/32bit-segments.xml
@@ -0,0 +1,12 @@ 
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016-2018 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.segments">
+  <reg name="fs_base" bitsize="32" type="int"/>
+  <reg name="gs_base" bitsize="32" type="int"/>
+</feature>
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 5312ca9919..fc8b37976a 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,11 @@ 
+2019-01-22  John Baldwin  <jhb@FreeBSD.org>
+
+	* linux-x86-tdesc.c (i386_linux_read_description): Update call to
+	i386_create_target_description for 'segments' parameter.
+	* lynx-i386-low.c (lynx_i386_arch_setup): Likewise.
+	* nto-x86-low.c (nto_x86_arch_setup): Likewise.
+	* win32-i386-low.c (i386_arch_setup): Likewise.
+
 2019-01-21  Tom Tromey  <tom@tromey.com>
 
 	* tracepoint.c: Fix includes.
diff --git a/gdb/gdbserver/linux-x86-tdesc.c b/gdb/gdbserver/linux-x86-tdesc.c
index 04bccc84ed..8f24a3d72d 100644
--- a/gdb/gdbserver/linux-x86-tdesc.c
+++ b/gdb/gdbserver/linux-x86-tdesc.c
@@ -87,7 +87,7 @@  i386_linux_read_description (uint64_t xcr0)
 
   if (*tdesc == NULL)
     {
-      *tdesc = i386_create_target_description (xcr0, true);
+      *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
diff --git a/gdb/gdbserver/lynx-i386-low.c b/gdb/gdbserver/lynx-i386-low.c
index 0731d2a73d..77ec8235e0 100644
--- a/gdb/gdbserver/lynx-i386-low.c
+++ b/gdb/gdbserver/lynx-i386-low.c
@@ -331,7 +331,7 @@  static void
 lynx_i386_arch_setup (void)
 {
   struct target_desc *tdesc
-    = i386_create_target_description (X86_XSTATE_SSE_MASK, false);
+    = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
 
   init_target_desc (tdesc, i386_expedite_regs);
 
diff --git a/gdb/gdbserver/nto-x86-low.c b/gdb/gdbserver/nto-x86-low.c
index 19c9acf42b..6930664e00 100644
--- a/gdb/gdbserver/nto-x86-low.c
+++ b/gdb/gdbserver/nto-x86-low.c
@@ -89,7 +89,7 @@  nto_x86_arch_setup (void)
 {
   the_low_target.num_regs = 16;
   struct target_desc *tdesc
-    = i386_create_target_description (X86_XSTATE_SSE_MASK, false);
+    = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
 
   init_target_desc (tdesc, i386_expedite_regs);
 
diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c
index 30f7af7553..9b7bc40225 100644
--- a/gdb/gdbserver/win32-i386-low.c
+++ b/gdb/gdbserver/win32-i386-low.c
@@ -439,7 +439,7 @@  i386_arch_setup (void)
 					   false, false);
   const char **expedite_regs = amd64_expedite_regs;
 #else
-  tdesc = i386_create_target_description (X86_XSTATE_SSE_MASK, false);
+  tdesc = i386_create_target_description (X86_XSTATE_SSE_MASK, false, false);
   const char **expedite_regs = i386_expedite_regs;
 #endif
 
diff --git a/gdb/i386-fbsd-nat.c b/gdb/i386-fbsd-nat.c
index 855572143b..8ff104cf72 100644
--- a/gdb/i386-fbsd-nat.c
+++ b/gdb/i386-fbsd-nat.c
@@ -160,7 +160,7 @@  i386_fbsd_nat_target::read_description ()
   if (x86bsd_xsave_len == 0)
     xcr0 = X86_XSTATE_SSE_MASK;
 
-  return i386_target_description (xcr0);
+  return i386_target_description (xcr0, false);
 }
 #endif
 
diff --git a/gdb/i386-fbsd-tdep.c b/gdb/i386-fbsd-tdep.c
index 82dfbd5c35..43b15167ae 100644
--- a/gdb/i386-fbsd-tdep.c
+++ b/gdb/i386-fbsd-tdep.c
@@ -267,7 +267,7 @@  i386fbsd_core_read_description (struct gdbarch *gdbarch,
 				struct target_ops *target,
 				bfd *abfd)
 {
-  return i386_target_description (i386fbsd_core_read_xcr0 (abfd));
+  return i386_target_description (i386fbsd_core_read_xcr0 (abfd), false);
 }
 
 /* Similar to i386_supply_fpregset, but use XSAVE extended state.  */
diff --git a/gdb/i386-go32-tdep.c b/gdb/i386-go32-tdep.c
index 5a18f35f99..200de53cd8 100644
--- a/gdb/i386-go32-tdep.c
+++ b/gdb/i386-go32-tdep.c
@@ -35,7 +35,7 @@  i386_go32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   /* DJGPP does not support the SSE registers.  */
   if (!tdesc_has_registers (info.target_desc))
-    tdep->tdesc = i386_target_description (X86_XSTATE_X87_MASK);
+    tdep->tdesc = i386_target_description (X86_XSTATE_X87_MASK, false);
 
   /* Native compiler is GCC, which uses the SVR4 register numbering
      even in COFF and STABS.  See the comment in i386_gdbarch_init,
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 0f344b0710..b2461836ad 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -694,7 +694,7 @@  i386_linux_read_description (uint64_t xcr0)
     [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
 
   if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true);
+    *tdesc = i386_create_target_description (xcr0, true, false);
 
   return *tdesc;
 }
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 8b2c72ac7b..65a5d6ab95 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8175,7 +8175,7 @@  i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
   const struct tdesc_feature *feature_core;
 
   const struct tdesc_feature *feature_sse, *feature_avx, *feature_mpx,
-			     *feature_avx512, *feature_pkeys;
+			     *feature_avx512, *feature_pkeys, *feature_segments;
   int i, num_regs, valid_p;
 
   if (! tdesc_has_registers (tdesc))
@@ -8198,6 +8198,9 @@  i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
   /* Try AVX512 registers.  */
   feature_avx512 = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx512");
 
+  /* Try segment base registers.  */
+  feature_segments = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.segments");
+
   /* Try PKEYS  */
   feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
 
@@ -8307,6 +8310,16 @@  i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
 	    tdep->mpx_register_names[i]);
     }
 
+  if (feature_segments)
+    {
+      if (tdep->fsbase_regnum < 0)
+	tdep->fsbase_regnum = I386_FSBASE_REGNUM;
+      valid_p &= tdesc_numbered_register (feature_segments, tdesc_data,
+					  tdep->fsbase_regnum, "fs_base");
+      valid_p &= tdesc_numbered_register (feature_segments, tdesc_data,
+					  tdep->fsbase_regnum + 1, "gs_base");
+    }
+
   if (feature_pkeys)
     {
       tdep->xcr0 |= X86_XSTATE_PKRU;
@@ -8543,14 +8556,14 @@  i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* Even though the default ABI only includes general-purpose registers,
      floating-point registers and the SSE registers, we have to leave a
      gap for the upper AVX, MPX and AVX512 registers.  */
-  set_gdbarch_num_regs (gdbarch, I386_PKEYS_NUM_REGS);
+  set_gdbarch_num_regs (gdbarch, I386_NUM_REGS);
 
   set_gdbarch_gnu_triplet_regexp (gdbarch, i386_gnu_triplet_regexp);
 
   /* Get the x86 target description from INFO.  */
   tdesc = info.target_desc;
   if (! tdesc_has_registers (tdesc))
-    tdesc = i386_target_description (X86_XSTATE_SSE_MASK);
+    tdesc = i386_target_description (X86_XSTATE_SSE_MASK, false);
   tdep->tdesc = tdesc;
 
   tdep->num_core_regs = I386_NUM_GREGS + I387_NUM_REGS;
@@ -8592,6 +8605,9 @@  i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->pkru_regnum = -1;
   tdep->num_pkeys_regs = 0;
 
+  /* No segment base registers.  */
+  tdep->fsbase_regnum = -1;
+
   tdesc_data = tdesc_data_alloc ();
 
   set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
@@ -8717,20 +8733,21 @@  i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 /* Return the target description for a specified XSAVE feature mask.  */
 
 const struct target_desc *
-i386_target_description (uint64_t xcr0)
+i386_target_description (uint64_t xcr0, bool segments)
 {
   static target_desc *i386_tdescs \
-    [2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
+    [2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
   target_desc **tdesc;
 
   tdesc = &i386_tdescs[(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
     [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
     [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
     [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
+    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
+    [segments ? 1 : 0];
 
   if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, false);
+    *tdesc = i386_create_target_description (xcr0, false, segments);
 
   return *tdesc;
 }
@@ -9072,7 +9089,7 @@  Show Intel Memory Protection Extensions specific variables."),
 
   for (auto &a : xml_masks)
     {
-      auto tdesc = i386_target_description (a.mask);
+      auto tdesc = i386_target_description (a.mask, false);
 
       selftests::record_xml_tdesc (a.xml, tdesc);
     }
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 2532306e5c..c0d494824c 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -200,6 +200,10 @@  struct gdbarch_tdep
   /* PKEYS register names.  */
   const char **pkeys_register_names;
 
+  /* Register number for %fsbase.  Set this to -1 to indicate the
+     absence of segment base registers.  */
+  int fsbase_regnum;
+
   /* Target description.  */
   const struct target_desc *tdesc;
 
@@ -296,7 +300,9 @@  enum i386_regnum
   I386_K7_REGNUM = I386_K0_REGNUM + 7,
   I386_ZMM0H_REGNUM,		/* %zmm0h */
   I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
-  I386_PKRU_REGNUM
+  I386_PKRU_REGNUM,
+  I386_FSBASE_REGNUM,
+  I386_GSBASE_REGNUM
 };
 
 /* Register numbers of RECORD_REGMAP.  */
@@ -337,6 +343,7 @@  enum record_i386_regnum
 #define I386_MPX_NUM_REGS	(I386_BNDSTATUS_REGNUM + 1)
 #define I386_AVX512_NUM_REGS	(I386_ZMM7H_REGNUM + 1)
 #define I386_PKEYS_NUM_REGS	(I386_PKRU_REGNUM + 1)
+#define I386_NUM_REGS		(I386_GSBASE_REGNUM + 1)
 
 /* Size of the largest register.  */
 #define I386_MAX_REGISTER_SIZE	64
@@ -440,7 +447,8 @@  extern int i386_svr4_reg_to_regnum (struct gdbarch *gdbarch, int reg);
 
 extern int i386_process_record (struct gdbarch *gdbarch,
                                 struct regcache *regcache, CORE_ADDR addr);
-extern const struct target_desc *i386_target_description (uint64_t xcr0);
+extern const struct target_desc *i386_target_description (uint64_t xcr0,
+							  bool segments);
 
 /* Return true iff the current target is MPX enabled.  */
 extern int i386_mpx_enabled (void);