[RFC 4/4] ld: bfd: sframe: fix incorrect r_addend in RELA entries
Jan Beulich
jbeulich@suse.com
Fri Mar 14 09:02:42 GMT 2025
On 14.03.2025 05:48, Indu Bhagat wrote:
> 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.
Hmm, this is pretty odd imo. There should have been two section kinds (or
maybe even three, seeing that the SFrame headers end up fully folded in
the diagram above), which would be combined only when linking final
binaries. I now regret that I didn't look more closely at all of this when
it was first introduced.
Jan
More information about the Binutils
mailing list