[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