[PATCH V5] x86: Add tls check in gas
Jan Beulich
jbeulich@suse.com
Wed Sep 25 07:51:02 GMT 2024
On 25.09.2024 05:32, Cui, Lili wrote:
>> I've grabbed the newest tls.pdf I could find (still over 10 years old, from Ulrich
>> Drepper's site as referenced by the Thread-Local Storage section in gcc14 doc),
>> and that raises questions:
>
> Jan, you can find the latest TLS in ABI chapter 10 (https://gitlab.com/x86-psABIs/x86-64-ABI)
So far all my questions were on the i386 side of things, which isn't covered
there.
> and some TLS doc linkers there (e.g. http://www.akkadia.org/drepper/tls.pdf)
This is the doc I was referring to.
As an aside - of course it shouldn't really take two docs to get a complete
picture.
>> On 19.09.2024 08:38, Cui, Lili wrote:
>>> @@ -6357,6 +6387,356 @@ static INLINE bool may_need_pass2 (const
>> insn_template *t)
>>> && (t->base_opcode | 8) == 0x2c); }
>>>
>>> +static enum x86_tls_error_type
>>> +x86_check_tls_relocation (enum bfd_reloc_code_real r_type) {
>>> + switch (r_type)
>>> + {
>>> + case BFD_RELOC_386_TLS_GOTDESC:
>>> + /* Check GDesc access model:
>>
>> What's "GDesc access model"? I can't find anything named like this, nor mention
>> of ...
>>> + leal x@tlsdesc(%ebx), %reg32 --> Memory reg must be %ebx and
>>> + SIB is not supported.
>>> + */
>>
>> ... @tlsdesc.
>
> It is defined in ABI doc.
As per above - where? I can see the 64-bit counterpart of this being
described in the x86-64 psABI, but that's not covering the case here.
>>> + if (i.tm.mnem_off != MN_lea)
>>> + return x86_tls_error_insn;
>>> + if (i.index_reg)
>>> + return x86_tls_error_sib;
>>> + if (!i.base_reg)
>>> + return x86_tls_error_no_base_reg;
>>> + if (i.base_reg->reg_type.bitfield.instance != RegB)
>>> + return x86_tls_error_ebx;
>>> + if (!i.op[1].regs->reg_type.bitfield.dword)
>>> + return x86_tls_error_dest_32bit_reg_size;
>>> + break;
>>> +
>>> + case BFD_RELOC_386_TLS_GD:
>>> + /* Check GD access model:
>>> +
>>> + leal foo@tlsgd(,%ebx,1), %eax --> Only this fixed format is supported.
>>> + leal foo@tlsgd(%reg32), %eax --> Dest reg must be '%eax'
>>> + Memory reg can't be %eax.
>>> + */
>>
>> Where's this 2nd form coming from? The doc says the SIB form must be used,
>> and the converted-to code sequence would require one more byte than the non-
>> SIB form provides space for.
>>
>> The doc doesn't say that the index reg has to be %ebx, it merely mentions that in
>> the example, for being the commonly used GOT reg.
>> This is the same ...
>
> It is mentioned in 4.1 of tls.pdf.
Where? Please quote the sentence. I can't spot anything saying that it has to
be %ebx. And similarly I can't spot anything indicating the non-SIB form would
be okay to use; to the contrary it says "Note the form of the first operand of
leal which forces the use of the SIB form ...". While that leaves open the use
of a NOP (or address size override on the associated CALL, as per the checking
the linker does) together with the non-SIB form,
- constraints on register use then still ought to be the same between both
variants,
- the presence of the NOP then would also need checking (as the linker does,
or else it might corrupt code).
>>> + if (i.tm.mnem_off != MN_lea)
>>> + return x86_tls_error_insn;
>>> + if (i.op[1].regs->reg_type.bitfield.instance != Accum)
>>> + return x86_tls_error_dest_eax;
>>> + if (!i.op[1].regs->reg_type.bitfield.dword)
>>> + return x86_tls_error_dest_32bit_reg_size;
>>> + if (i.index_reg)
>>> + {
>>> + if (i.base_reg)
>>> + return x86_tls_error_base_reg;
>>> + if (i.index_reg->reg_type.bitfield.instance != RegB)
>>> + return x86_tls_error_index_ebx;
>>> + if (i.log2_scale_factor)
>>> + return x86_tls_error_scale_factor;
>>> + }
>>> + else
>>> + {
>>> + if (!i.base_reg)
>>> + return x86_tls_error_no_base_reg;
>>> + if (i.base_reg->reg_type.bitfield.instance == Accum)
>>> + return x86_tls_error_eax;
>>> + }
>>> + break;
>>> +
>>> + case BFD_RELOC_386_TLS_LDM:
>>> + /* Check LDM access model:
>>> +
>>> + leal foo@tlsldm(%reg32), %eax --> Dest reg must be '%eax'
>>> + Memory reg can't be %eax and SIB
>>> + is not supported.
>>> + */
>>
>> ... for this case, and surprisingly any register is permitted here.
>
> There are two types of sequence listings in linker comments:
>
> /* Check transition from LD access model. Only
> leal foo@tlsldm(%ebx), %eax
> call ___tls_get_addr@PLT
> or
> leal foo@tlsldm(%reg), %eax
> call *___tls_get_addr@GOT(%reg)
> which may be converted to
> addr32 call ___tls_get_addr
> can transit to different access model. */
>
> They may be related to 4.2 of tls.pdf.
They are corresponding to that section, yes, but they don't match it.
Also note how all uses of addr32 in those comments are fishy: We're talking
about 32-bit mode code there, so the 0x67 prefix is addr16.
The recurring "%eax can't be used as the GOT base register since it
is used to pass parameter to ___tls_get_addr" in linker comments are also
unclear to me: The transitions eliminate the calls, and hence no arguments
are passed anyway. Locally and transiently establishing the GOT pointer in
%eax therefore looks entirely acceptable to me. It is clear from both the
original and the transitioned-to code sequences that %eax would no longer
hold the GOT pointer after these sequences. What instead is the case is
that in e.g.
leal foo@tlsldm(%reg), %eax
call *___tls_get_addr@GOT(%reg)
%reg is used again after the LEA, and hence its clobbering would be a
problem. Yet that's nowhere near what those comments say.
Jan
More information about the Binutils
mailing list