ARM-software / abi-aa

Application Binary Interface for the Arm® Architecture
Other
878 stars 173 forks source link

[aaelf64] Define GOT-Relative data relocation #223

Closed PiJoules closed 9 months ago

PiJoules commented 9 months ago

This introduces a new relocation R\_<CLS>\_GOTPCREL32 which follows the existing wording to “R__GOTREL32”, but instead evaluates to the 32-bit offset between a GOT entry for a given symbol and the current location where the relocation is applied, so its equation would be “G(GDAT(S+A))- P”.

mmalcomson commented 9 months ago

LGTM as well -- merging as I'm the second to look at it.

MaskRay commented 7 months ago

Thanks for the addition. I think another advantage introducing the data relocation is that: -shared -fpic -fexperimental-relative-c++-abi-vtables builds will not create PLT entries for functions in the vtables, as long as they are not directly called.

smeenai commented 5 months ago

I don't understand the equation for this relocation G(GDAT(S+A)) - P. Section 5.7.33 Relocation operations says:

GDAT(S+A) represents a pointer-sized entry in the GOT for address S+A. The entry will be relocated at run time with relocation R_<CLS>_GLOB_DAT(S+A)

Based on the LLD implementation of this relocation though (https://github.com/llvm/llvm-project/pull/72584), it seems the addend is applied to the place of the relocation, not the GOT entry. See the added test case for what I mean. The relocations in the object file are:

$ bin/llvm-readelf -rW tools/lld/test/ELF/Output/aarch64-reloc-gotpcrel32.s.tmp.o

Relocation section '.rela.data' at offset 0xd0 contains 5 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar + 0
0000000000000004  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar + 4
0000000000000008  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar - 4
000000000000000c  000000040000013b R_AARCH64_GOTPCREL32   0000000000000000 baz + ffffffff
0000000000000010  000000040000013b R_AARCH64_GOTPCREL32   0000000000000000 baz - ffffffff

The relocations in the output SO are:

$ bin/llvm-readelf -rW tools/lld/test/ELF/Output/aarch64-reloc-gotpcrel32.s.tmp.so

Relocation section '.rela.dyn' at offset 0x2c0 contains 2 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000020398  0000000100000401 R_AARCH64_GLOB_DAT     0000000000000000 baz + 0
0000000000020390  0000000200000401 R_AARCH64_GLOB_DAT     00000000000303a0 bar + 0

So the addend isn't propagated to the dynamic relocations, which GDAT(S+A) would imply in my reading. Instead, I believe the equation should be:

G(GDAT(S)) + A - P

This also better matches R_X86_64_GOTPCREL, which is defined as G + GOT + A - P, where G is the GOT offset for the symbol and GOT is the GOT address. I belive R_ARM_GOT_PREL is the AArch32 equivalent, and that's also defined as GOT(S) + A - P, where GOT(S) is the address of the GOT entry for S. Am I missing something?

PiJoules commented 5 months ago

I don't understand the equation for this relocation G(GDAT(S+A)) - P. Section 5.7.33 Relocation operations says:

GDAT(S+A) represents a pointer-sized entry in the GOT for address S+A. The entry will be relocated at run time with relocation R_<CLS>_GLOB_DAT(S+A)

Based on the LLD implementation of this relocation though (llvm/llvm-project#72584), it seems the addend is applied to the place of the relocation, not the GOT entry. See the added test case for what I mean. The relocations in the object file are:

$ bin/llvm-readelf -rW tools/lld/test/ELF/Output/aarch64-reloc-gotpcrel32.s.tmp.o

Relocation section '.rela.data' at offset 0xd0 contains 5 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar + 0
0000000000000004  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar + 4
0000000000000008  000000020000013b R_AARCH64_GOTPCREL32   0000000000000000 bar - 4
000000000000000c  000000040000013b R_AARCH64_GOTPCREL32   0000000000000000 baz + ffffffff
0000000000000010  000000040000013b R_AARCH64_GOTPCREL32   0000000000000000 baz - ffffffff

The relocations in the output SO are:

$ bin/llvm-readelf -rW tools/lld/test/ELF/Output/aarch64-reloc-gotpcrel32.s.tmp.so

Relocation section '.rela.dyn' at offset 0x2c0 contains 2 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000020398  0000000100000401 R_AARCH64_GLOB_DAT     0000000000000000 baz + 0
0000000000020390  0000000200000401 R_AARCH64_GLOB_DAT     00000000000303a0 bar + 0

So the addend isn't propagated to the dynamic relocations, which GDAT(S+A) would imply in my reading. Instead, I believe the equation should be:

G(GDAT(S)) + A - P

This also better matches R_X86_64_GOTPCREL, which is defined as G + GOT + A - P, where G is the GOT offset for the symbol and GOT is the GOT address. I belive R_ARM_GOT_PREL is the AArch32 equivalent, and that's also defined as GOT(S) + A - P, where GOT(S) is the address of the GOT entry for S. Am I missing something?

No you're correct. The addend should be applied to where the relocation is located, not to symbol we're taking the GOT entry for. The relocation is intended to be similar to R_X86_64_GOTPCREL as you said. I'll send out a followup fix. Sorry for the confusion.

PiJoules commented 5 months ago

https://github.com/ARM-software/abi-aa/pull/247

smeenai commented 5 months ago

Thanks for the confirmation and follow-up!

smithp35 commented 4 months ago

Apologies for the delayed response, just come back from vacation.

It is worth mentioning that there is a generic ABI issue with how GOT relative addends are represented in the AArch64 ABI https://github.com/ARM-software/abi-aa/issues/217 it looks like this instance may fall into that category. To summarise it looks like neither LLD, Mold, Gold or GNU ld implement GDAT(S+A) as written in the spec. Moreover they don't implement it in a consistent way either, with lld doing GDAT(S) + A and GNU ld just ignoring the A.

So far there hasn't been a satisfactory resolution as it's not clear what all the linker's can support. Probably best leave any further comments on https://github.com/ARM-software/abi-aa/issues/217

I'll leave a comment in https://github.com/ARM-software/abi-aa/pull/247 too