[RFC 4/4] ld: bfd: sframe: fix incorrect r_addend in RELA entries
Indu Bhagat
indu.bhagat@oracle.com
Fri Mar 14 04:48:40 GMT 2025
On 3/11/25 5:22 AM, Jan Beulich wrote:
> On 11.03.2025 10:26, Jose E. Marchesi wrote:
>>> On 10.03.2025 14:16, Jens Remus wrote:
>>>> On 08.03.2025 08:38, Indu Bhagat wrote:
>>>>> With the fix in GAS, we now use a different PC-relative RELA for
>>>>> updating the SFrame FDE function start address: The value is the offset
>>>>> of the start PC of the function from the start of the SFrame section.
>>>>>
>>>>> When RELAs are output (e.g. for relocatable links), there is need to
>>>>> adjust the r_addend. This is because the r_addend values still have the
>>>>> correct values for the _input_ SFrame section being linked / relocated.
>>>>> The values must now be (before outputing the RELAs) with respect to the
>>>>> _output_ SFrame section.
>>>>>
>>>>> PS: This patch should be merged with the previous commits before final
>>>>> commit (Otherwise the tests will fail). It is currently a separate
>>>>> patch as I would like to check if this is OK to do. If such a "addend
>>>>> fixup" is risky or wrong, it seems we will need a new type of RELOC for
>>>>> SFrame sections.
>>>>
>>>>> diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c
>>>>
>>>>> @@ -574,6 +574,37 @@ _bfd_elf_sframe_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
>>>>> + out_fde_idx * sizeof (sframe_func_desc_entry));
>>>>> }
>>>>>
>>>>> +/* Get the "canonicalized" addend for the symbol reference corresponding to the
>>>>> + relocation at RELOC_INDEX. E.g., for the following reloc for the SFrame
>>>>
>>>> Nit: s/reloc/relocs/
>>>>
>>>>> + FDE function start address:
>>>>> + Offset Type Sym. Name + Addend
>>>>> + 00000000001c R_X86_64_PC32 .text + 1c
>>>>> + 000000000030 R_X86_64_PC32 .text + 3b
>>>>> + The canonicalized addend are 0 and b respectively as the relocs are for
>>>>> + symbols (.text + 0) and (.text + b) respectively.
>>>>
>>>> Maybe it would help to use the function symbols foo and bar in the example:
>>>>
>>>> relocation at RELOC_INDEX. E.g., for the following annotated relocs for the SFrame
>>>> FDE function start address of FDE[0] and FDE[1] for functions foo and bar:
>>>> Offset Type Sym. Name + Addend
>>>> 00000000001c R_X86_64_PC32 .text + 1c // FDE[0] for foo at .text + 0
>>>> 000000000030 R_X86_64_PC32 .text + 3b // FDE[1] for bar at .text + b
>>>> With:
>>>> 1c = 0 + sizeof(sframe_header) + 0 * sizeof(sframe_fde)
>>>> 3b = b + sizeof(sframe_header) + 1 * sizeof(sframe_fde)
>>>> The canonicalized addend are 0 and b respectively as the relocs are for
>>>> symbols (.text + 0) and (.text + b) respectively.
>>>
>>> Hmm, wait - there's a mix between .text and .sframe then, isn't there? That
>>> would explain why custom handling is then necessary. Yet as said in the other
>>> mail, custom handling should not be necessary. Not the least because other
>>> ELF-consuming tools also need to work, not just what GNU binutils provides.
>>
>> [At a risk of being reiterative, but this also helps to my own
>> understanding, because this is all very confusing :)]
>>
>> I think there is nothing special about that "mix". As far as I can tell
>> this is just one rather peculiar particular case of a PCrel reloc.
>> Suppose you have the typical:
>>
>> .text
>> ...
>> ldinsn OFFSET
>>
>> .data
>> ...
>> somedata:
>>
>> And then you install a relocation for the instruction's offset, which is
>> PC-relative. You use an expression like:
>>
>> ldinsn somedata
>>
>> Then I would expect GAS to emit a PCrel with:
>>
>> .text.rela:
>> P: offset_of_ldins_within_text
>> S: .data
>> A: offset_of_somedata_within_data
>>
>> With resolving expression:
>>
>> S + A - P
>>
>> to which the linker also adds .text, resulting in the expected:
>>
>> .data + offset_of_somedata_within_data - .text + offset_of_ldins_within_text
>>
>> i.e. the distance between somedata and the instruction which field is to
>> be patched.
>>
>> Now, for SFrame, the expression used wants to denote the distance
>> between the beginning of some given function and the beginning of the
>> .sframe section containing the FDE to patch. GAS is given the
>> expression:
>>
>> .text + text_offset - .sframe
>>
>> And the assembler smartly uses a PCrel relocation (it has no other
>> choice I suppose) such as:
>>
>> .sframe.rela:
>> P: offset_of_fde_within_sframe
>> S: .text
>> A: offset_of_function_within_text + offset_of_fde_within_sframe
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> I don't think this can work: You can't have offsets within two different
> sections added together. When doing a relocatable link, _both_ offsets
> may change. S+A has to describe the target of the relocation.
>
>> [I have been trying to find the code in GAS that actually does this,
>> because it seems to me it must be some ad-hoc code, but couln't find
>> it.]
>>
>> Which is a little weird, because it has the additional
>> offset_of_fde_within_sframe, intended to basically annull the same value
>> in P in the resolving function:
>>
>> S + A - P
>>
>> which is, with the linker-added .sframe:
>>
>> .text + (offset_of_funtion_within_text + offset_of_fde_within_sframe)
>> - .sframe + offset_of_fde_within_sframe
>>
>> which becomes:
>>
>> .text + offset_of_funtion_within_text - .sframe
>>
>> which is the desired distance between the beginning of the function and
>> the beginning of the .sframe section.
>>
>> I don't think ELF consuming tools that just apply relocations need any
>> special code to solve these relocs.
>>
>> But due how the SFrame format works, as Jens explained, tools that merge
>> SFrame input sections into a relocatable output section need to
>> basically recreate the relocations for the FDEs in the output.
>
> But that's what a random ELF consuming tool wouldn't do. I.e. relocations
> here want expressing in a way that even e.g. GNU ld pre-dating the
> introduction of .sframe can properly handle them, whether or not it is
> passed the -r option.
>
> Of course there may be situations where this is entirely impossible. But
> I very much hope here it isn't one of those. (If it was, measures would
> imo need taking to make sure unaware tools would diagnose occurrences
> rather than silently doing the wrong thing.)
>
>> If this
>> merging consisted (like with ehframe) just in collating the input
>> sections together, then no special casing would be needed. But merging
>> SFrame sections imply that the offset of the FDEs wrt the containing
>> output .sframe will likely be different.
>
> Is this really different from .eh_frame?
>
Yes, SFrame merging is different from .eh_frame.
See some notes at the end of this email as the answer is related to your
question there...
>> This offset is embedded in the
>> input relocation's offset and addend, as shown above. So the merging
>> tool has no other choice that to "remove" the old FDE offset from the
>> input relocation (from both addend and offset) and "insert" the new FDE
>> offset (in the resulting merged .sframe section in the output) in both
>> addend and offset. Tools that merge SFrame sections must use
>> sframe-specific logic anyway, so I don't think this is a big deal...
>
> As said above - as long as .sframe are ordinary PROGBITS sections, unaware
> tools should be fine to use.
>
>> The other changes in generic code in Indu's patch, as far as I can see,
>> are necessary because the linker assumes that the input section contents
>> are merged by stocking them (or a subset of them) in the merged output
>> section. This is not so in SFrame, so thats why Indu's patch makes ld
>> to not increase that offset within the output section every iteration in
>> the loop, if the sections being merged are .sframe. It is simply that
>> until now no format required this sort of merging, where I11,I12,I13 and
>> I21,I22,I23 areas in two input sections will be merged into for example
>> I11,I21,I12,I22,I13,I23 in the output section.
>
> This part I fear I'm not following. Why would there be any re-ordering of
> input "fragments"? What specifically do I11 etc denominate? Are sections
> being taken apart and then - kind of randomly - re-assembled?
>
Reusing the figure used earlier in another thread, I have renamed FDE
entries to show what I11, I12 etc denominate. Basically I1X are FDEs
from input 1, I2x are FDEs from input 2...
--------------------- ---------------------
| SFrame Header | | SFrame Header |
--------------------- ---------------------
| SFrame FDE I11 | (Linking) | SFrame FDE I11 |
| SFrame FDE I12 | | SFrame FDE I12 |
| ... | |------>| ... |
| SFrame FDE I1n | | | SFrame FDE I1n |
--------------------- | | SFrame FDE I21 |
| ... | ------ | SFrame FDE I22 |
|SFrame FREs (Sec 1)| | | ... |
|(Frame Row Entries)| | | SFrame FDE I2n |
| ... | | |-------------------|
| ... | | | ... |
--------------------- | |SFrame FREs (Sec 1)|
| |SFrame FREs (Sec 2)|
| |(Frame Row Entries)|
| | ... |
--------------------- | ---------------------
| SFrame Header | |
--------------------- |
| SFrame FDE I21 | |
| SFrame FDE I22 | |
| ... | ------
| SFrame FDE I2n |
---------------------
| ... |
|SFrame FREs (Sec 2)|
|(Frame Row Entries)|
| ... |
| ... |
---------------------
Note how the SFrame FDEs in the output section are placed at the
beginning of the output section.
This is in contrast to EH_Frame where the CIE/FDE _as_a_single_unit_is
simply placed at an offset in the output section. In SFrame the stack
trace data is split, with there being ONE SFrame FDE + N SFrame FREs per
function.
The first member of the SFrame FDE is the function start address, which
has the relocation.
More information about the Binutils
mailing list