Trying to understand some relocation handling on Mach-O X86-64

Nick Kledzik kledzik at apple.com
Thu Feb 6 15:12:12 PST 2014


On Feb 6, 2014, at 1:52 PM, Rafael Espíndola <rafael.espindola at gmail.com> wrote:

> On 6 February 2014 13:37, Nick Kledzik <kledzik at apple.com> wrote:
>> Rafael,
>> 
>> 99.9% of the time, you don’t need symbols on literals. But it is that last 0.1%
>> that drives this. On i386 and armv7, mach-o uses “scattered relocations”
>> to handle these rare cases, but x86_64 (and arm64) do not have scattered
>> relocations.
>> 
>> An example is:
>> 
>>        .section __TEXT,__cstring
>> L1:
>>        .asciz "f"
>> L2:
>>        .asciz "b"
>> 
>>        .data
>>        .quad L1+2
>>        .quad L2-2
>> 
>>        .text
>>        leaq      %rax, L1+2
>>        leaq      %rax, L2-2
>> 
>> If you use local relocations, the values for the .quad and the leaq are evaluated
>> by the assembler, and the relocation just tells the linker that there is something
>> at that address that might need fixing up.  But as you can see, the linker will
>> think each points to the wrong string.
> 
> What is the expected meaning with Mach-O? That the relocation always
> points 2 byte after the start of a given string (for L1+2)?
Yes. The user wants the end of “f” - not the start of “b”. 

> 
>> I think this is not a problem for ELF for two reasons:
>> 1) the expression L1+2 could be encoded with a RELA relocation where
>> the quad/lea points to L1 and the RELA addend contains the +2.
>> 2) By default the ELF linker does not merge cstrings, so the “f” and “b” will
>> always be next to each other in the final output.  The darwin linker always
>> coalesces the __cstring section, so the “f” in this .o file might move to
>> replace an “f” from another .o file.
> 
> Not quiet. For ELF merging happens or not depending on the section
> flags. The sections we use for non unnamed_addr strings are always
> mergeable, so from clang's point of view that is the same.
> 
> REL or RELA is not the issue. On 32 bits x86 we use REL. on X86-64 we use RELA.
> 
> The logic that is used by MC when handling this is that a symbol that
> is used in a relocation is kept in the symbol table. It looks like gas
> does the same. This means that we do get the above semantic (points X
> bytes after the start of a given string).
> 
> For example, in the attached test.s I get
> 
> $ llvm-mc -filetype=obj  test.s -o test.o
> $ llvm-nm test.o
> 00000008 r .L3
> 00000000 D D
> 
> Now, this is in no way specific to C strings. In the attached test2.s
> the pointer in D always ends up pointing 4 bytes past the number 42.
> 
> Why then the special case for C strings on Mach-O? Couldn't we use the
> same logic as ELF?
Are all local labels kept if they are referenced?  Or just ones used with an addend?
If all are kept, then every function would have tons of local labels in the .o file
for the target of every branch internal to the function.

The constraint is that we need a symbol based relocation for anything with
an addend.  Most sections already have symbols that can be used.  But
some sections are made up of unnamed entities, so naturally have no
symbol name, so the compiler invents some local name.

The cctools ‘as’ tool hack was to just preserve all ‘L’ labels in c-string sections.
If MC already has a way to determine which ‘L’ labels are referenced using
an addend and just those can be preserved into the .o file, that would work too.


> The advantages would be
> 
> * The X bytes past a known mergeable data relocations would also work
> for utf-16 string, integers, floats, etc
> * doesSectionRequireSymbols goes away.
> * isSectionAtomizable would go away, except it can then be modified a
> bit. It should say if a section is symbol-atomizable instead of
> know-datatype-atomizable. With that it would be used to avoid putting
> a L symbol in an symbol-atomizable section like __TEXT,__const.



> BTW, in PR18743, is there a section we could put the constant and use
> an L prefix or do all sections with an unknown datatype get atomized
> using symbols?
There are a few sections that the linker implicitly knows how to atomize
(e.g. __literal8 (all 8-bytes), __cstring (zero terminated), __eh_frame (CFI chunks), etc).
All other sections are atomized at (non-L) symbols.

So the trick here is knowing that when an unnamed value gets put into a
section atomized by symbols, that the unnamed value needs an ‘l’ symbol.

-Nick





More information about the llvm-commits mailing list