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

John Baldwin
2019-01-28T17:54:41Z

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.

--
John Baldwin

Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â