Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

static linkers (lld and GNU ld) out of sync with aaelf64 for GOT relocations with addends. #217

Closed
smithp35 opened this issue Aug 22, 2023 · 2 comments · Fixed by #272
Closed

Comments

@smithp35
Copy link
Contributor

Raised due to LLVM issue llvm/llvm-project#63418

Raising this as an ABI issue rather than a two separate toolchain issues as it may be simpler to amend the ABI than try and fix the tools.

When doing a :got: and :got_lo12: expression to a local symbol, the GNU and LLVM assemblers convert this to a GOT generating relocation to the section symbol + addend.

For example:

        .global main
        .type   main, %function
main:
        adrp    x0, :got:x
        ldr     x0, [x0, #:got_lo12:x]
        adrp    x1, :got:y
        ldr     x1, [x1, #:got_lo12:y]
        adrp    x2, :got:z
        ldr     x2, [x2, #:got_lo12:z]
        add     x0, x1, x1
        add     x0, x0, x2
        ret
        .size   main, .-main
        .bss
        .align  3
        .size   x, 4
        .size   y, 8
        .size   z, 8
x:
        .zero   4
y:
        .zero 8
z:
        .zero 8

has the following relocations.

Relocation section '.rela.text' at offset 0x128 contains 6 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  0000000500000137 R_AARCH64_ADR_GOT_PAGE 0000000000000000 .bss + 0
0000000000000004  0000000500000138 R_AARCH64_LD64_GOT_LO12_NC 0000000000000000 .bss + 0
0000000000000008  0000000500000137 R_AARCH64_ADR_GOT_PAGE 0000000000000000 .bss + 4
000000000000000c  0000000500000138 R_AARCH64_LD64_GOT_LO12_NC 0000000000000000 .bss + 4
0000000000000010  0000000500000137 R_AARCH64_ADR_GOT_PAGE 0000000000000000 .bss + c
0000000000000014  0000000500000138 R_AARCH64_LD64_GOT_LO12_NC 0000000000000000 .bss + c

The ABI https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#576static-aarch64-relocations search for GOT-relative instruction relocations

Defines these to be:

  • Page(G(GDAT(S+A)))-Page(P)
  • G(GDAT(S+A))

With

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).
G(expr) is the address of the GOT entry for the expression expr.

GNU ld and lld are mishandling this with LLD using something like:

  • Page(G((GDAT(S)) + A) - Page(P))
  • G(GDAT(S)) + A
    With GNU ld.bfd appearing to ignore the addend A completely

We could say that this is a pair of toolchain bugs, however for LLD in particular some work would need to be done to generate separate GOT slots for S+A rather than just S. This might proove difficult to get accepted for a corner case just for AArch64. While I don't know for certain I expect GNU ld will have a similar problem.

It may be worth altering the ABI to not permit addends, or have them implemented in a way that GNU ld and LLD can both implement. We could then alter the assembler to not use symbol + addend forms for a local symbol.

If we determine that the ABI is correct we can raise toolchain issues and close this as won't fix.

jrtc27 added a commit to llvm/llvm-project that referenced this issue Aug 22, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes #63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
llvmbot pushed a commit to llvm/llvm-project-release-prs that referenced this issue Aug 22, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm/llvm-project#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552

(cherry picked from commit 7e1afab1b1821550c5f8d0d6a50636236fa02e2c)
MaskRay added a commit to MaskRay/llvm-project that referenced this issue Aug 23, 2023
…relocations

Assemblers change certain relocations referencing a local symbol to
reference the section symbol instead. This conversion is disabled for
many conditions (`shouldRelocateWithSymbol`), e.g. TLS symbol, for most
targets (including AArch32, x86, PowerPC, and RISC-V) GOT-generating
relocations.

However, AArch64 encodes the GOT-generating intent in MCValue::RefKind
instead of MCSymbolRef::Kind (see commit
0999cbd (2014)), therefore not affected
by the code `case MCSymbolRefExpr::VK_GOT:`. Therefore, GOT relocations
referencing two local symbols may share the same GOT entry after linking
(GNU ld, ld.lld), which is not expected:
```
ldr     x1, [x1, :got_lo12:x]  // converted to .data+0
ldr     x1, [x1, :got_lo12:y]  // converted to .data+4

.data
// .globl x, y  would suppress STT_SECTION conversion
x:
.zero 4
y:
.long 42
```

This patch changes AArch64 to suppress local symbol to STT_SECTION
conversion for GOT relocations, matching most other targets. x and y
will use different GOT entries, which IMO is the most sensable behavior.

With this change, the ABI decision on ARM-software/abi-aa#217
will only affect relocations explicitly referencing STT_SECTION symbols, e.g.
```
ldr     x1, [x1, :got_lo12:(.data+0)]
ldr     x1, [x1, :got_lo12:(.data+4)]
// I consider this unreasonable uses
```

IMO all reasonable use cases are unaffected.

Link: llvm#63418
GNU assembler PR: https://sourceware.org/bugzilla/show_bug.cgi?id=30788

Differential Revision: https://reviews.llvm.org/D158577
@MaskRay
Copy link
Contributor

MaskRay commented Aug 23, 2023

When doing a :got: and :got_lo12: expression to a local symbol, the GNU and LLVM assemblers convert this to a GOT generating relocation to the section symbol + addend.

I consider this assembler issues specifically for AArch64. GNU assembler and LLVM integrated assembler for most other targets suppress local symbol to STT_SECTION conversion for GOT relocations.

This is neglected likely because compilers don't emit such constructs. llvm/llvm-project#63418 is found due to inline assembly uses.

GNU ld and lld are mishandling this with LLD using something like:

Agreed that GNU ld and lld don't comply to the ABI when generating GOT entries.
They create GOT entries based on the symbol, ignoring addend.

If we fix GNU assembler and LLVM integrated assembler to suppress STT_SECTION conversion (https://reviews.llvm.org/D158577 and https://sourceware.org/bugzilla/show_bug.cgi?id=30788), I believe this ABI change (GDAT(S+A) vs GDAT(S)) is moot, as all reasonable use cases will be unaffected.

ldr     x1, [x1, :got_lo12:x]
ldr     x1, [x1, :got_lo12:y]

// affected by this change but I do not recommend this use case.
// x86-64 maintainer considers a similar case reasonable: https://sourceware.org/bugzilla/show_bug.cgi?id=26939
ldr     x1, [x1, :got_lo12:(x+8)]

// affected by this change but I do not recommend this use case.
ldr     x1, [x1, :got_lo12:(.data+0)]
ldr     x1, [x1, :got_lo12:(.data+8)]

.data
x:
.zero 4
y:
.zero 8

It may be worth altering the ABI to not permit addends, or have them implemented in a way that GNU ld and LLD can both implement. We could then alter the assembler to not use symbol + addend forms for a local symbol.

If we determine that the ABI is correct we can raise toolchain issues and close this as won't fix.

Switching from GDAT(S) to GDAT(S+A) would indeed be disruptive for lld so I hope that we use GDAT(S).

If we change assemblers as mentioned, whether or not the ABI requires GDAT(S) or GDAT(S+A) is largely moot for lld.

tru pushed a commit to llvm/llvm-project-release-prs that referenced this issue Aug 25, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm/llvm-project#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552

(cherry picked from commit 7e1afab1b1821550c5f8d0d6a50636236fa02e2c)
MaskRay added a commit to llvm/llvm-project that referenced this issue Aug 29, 2023
…relocations

Assemblers change certain relocations referencing a local symbol to
reference the section symbol instead. This conversion is disabled for
many conditions (`shouldRelocateWithSymbol`), e.g. TLS symbol, for most
targets (including AArch32, x86, PowerPC, and RISC-V) GOT-generating
relocations.

However, AArch64 encodes the GOT-generating intent in MCValue::RefKind
instead of MCSymbolRef::Kind (see commit
0999cbd (2014)), therefore not affected
by the code `case MCSymbolRefExpr::VK_GOT:`. As GNU ld and ld.lld
create GOT entries based on the symbol, ignoring addend, the two ldr
instructions will share the same GOT entry, which is not expected:
```
ldr     x1, [x1, :got_lo12:x]  // converted to .data+0
ldr     x1, [x1, :got_lo12:y]  // converted to .data+4

.data
// .globl x, y  would suppress STT_SECTION conversion
x:
.zero 4
y:
.long 42
```

This patch changes AArch64 to suppress local symbol to STT_SECTION
conversion for GOT relocations, matching most other targets. x and y
will use different GOT entries, which IMO is the most sensable behavior.

With this change, the ABI decision on ARM-software/abi-aa#217
will only affect relocations explicitly referencing STT_SECTION symbols, e.g.
```
ldr     x1, [x1, :got_lo12:(.data+0)]
ldr     x1, [x1, :got_lo12:(.data+4)]
// I consider this unreasonable uses
```

IMO all reasonable use cases are unaffected.

Link: #63418
GNU assembler PR: https://sourceware.org/bugzilla/show_bug.cgi?id=30788

Reviewed By: peter.smith

Differential Revision: https://reviews.llvm.org/D158577
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 2, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 2, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 2, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 3, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 3, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 6, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
razmser pushed a commit to SuduIDE/llvm-project that referenced this issue Oct 11, 2023
On FreeBSD and NetBSD we don't use .weak due to differing semantics.
Currently we end up using no directive, which gives a local symbol,
whereas the closer thing to a weak symbol would be a global one. In
particular, both GNU and LLVM toolchains cannot handle a GOT-indirect
reference to a local symbol at a non-zero offset within a section on
AArch64 (see ARM-software/abi-aa#217), and so
interceptors do not work on FreeBSD/arm64, failing to link with LLD.
Switching to .globl both works around this bug and more closely aligns
such non-weak platforms with weak ones.

Fixes llvm#63418

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D158552
smithp35 added a commit to smithp35/abi-aa that referenced this issue Jul 2, 2024
The GDAT(S + A) relocation operation requires a static linker to
create a GOT entry for (S + A). Requiring at least one GOT entry
for each unique tuple (S, A). Unfortunately no known static linker
has implemented this correctly, with one of two forms being
implemented instead:
* GDAT(S) with the addend ignored.
* GDAT(S) + A with a single GOT entry per S, and A added to the
  value of GDAT(S).
These implementations are correct and consistent only for an
addend (A) of zero.

No known compiler uses non-zero addends in relocations that use
the GDAT(S+A) operation, although it is possible to generate
them using assembly language.

This change synchronizes the ABI with the behavior of existing
static linker implementations. The benefit of permitting code
generators [*] to use a non zero addend in GDAT(S + A) is judged
to be lower than implementing GDAT(S + A) correctly in existing
static linkers, many of which assume that there is a single
GOT entry per unique symbol S.

It is QoI whether a static linker gives an error if a non zero
addend is used for a relocation that uses the GDAT(S) operation.

Fixes ARM-software#217
Also resolves ARM-software#247

[*] The most common use case for a non-zero addend is in
constructing a C++ object with a vtable. The first two entries
in the vtable are the offset to top and a pointer to RTTI, the
vtable pointer in the object starts at offset 0x10. This offset
can be encoded in the relocation addend. We would save an add
instruction for each construction of a C++ object with a vtable
if addends were permitted.
@smithp35
Copy link
Contributor Author

smithp35 commented Jul 2, 2024

I've submitted #272 which removes A from the GDAT(S + A) relocation operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants