Skip to content

[ELF][AArch64][PAC] Weak undefined symbols treated as symbol with value 0 and signed #173296

Description

@jrtc27

The PAuthABI spec says the following:

For a relocation that involves signing a pointer, if the target symbol is an undefined weak reference, the result of the relocation is 0 (nullptr) regardless of the signing schema.

However, this is not what's implemented. isStaticLinkTimeConstant has an early false return for RE_AARCH64_AUTH that ignores this case, and so will treat it like a defined non-preemptible symbol, emitting R_AARCH64_AUTH_RELATIVE with an addend of the symbol's VA (+ original addend), 0. See this failing test (FileCheck assertions derived from the output when dropping the @AUTH(da,42), which is what the output should be the same as):

# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld --static %t.o -o %t
# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
# RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA

## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
## to NULL (plus addend).

# RELA-LABEL: Relocations [
# RELA-NEXT:  ]

# DATA-LABEL: Hex dump of section '.data':
# DATA-NEXT:  0x00220158 00000000 00000000 10000000 00000000

.weak undef

.data
foo:
.quad undef@AUTH(da,42)
.quad (undef + 16)@AUTH(da,42)

We have a similar case in CHERI LLVM for capabilities, as those need run-time initialisation like PAuth signing, and use the following instead:

  if (e == R_ABS_CAP)
    return !sym.isPreemptible && sym.isUndefWeak();

However, we're currently based on an older version of LLD, and there's been a little refactoring here (we predate moving !config->isPic && sym.isUndefWeak() from processAux into isStaticLinkTimeConstant), so that might not be quite the right change, and AArch64::relocate will need to know to write the full 64 bits to zero out the signing schema from the input (or replace it with the upper bits of the addend, especially if negative), so that's insufficient.

Activity

  1. jrtc27 commented on Dec 22, 2025

    @jrtc27
    ContributorAuthor
  2. llvmbot commented on Dec 22, 2025

    @llvmbot
    Member

    @llvm/issue-subscribers-lld-elf

    Author: Jessica Clarke (jrtc27)

    Details The PAuthABI spec says the following:

    > For a relocation that involves signing a pointer, if the target symbol is an undefined weak reference, the result of the relocation is 0 (nullptr) regardless of the signing schema.

    However, this is not what's implemented. isStaticLinkTimeConstant has an early false return for RE_AARCH64_AUTH that ignores this case, and so will treat it like a defined non-preemptible symbol, emitting R_AARCH64_AUTH_RELATIVE with an addend of the symbol's VA (+ original addend), 0. See this failing test (FileCheck assertions derived from the output when dropping the @<!-- -->AUTH(da,42), which is what the output should be the same as):

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld --static %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
    
    ## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
    ## to NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # DATA-LABEL: Hex dump of section '.data':
    # DATA-NEXT:  0x00220158 00000000 00000000 10000000 00000000
    
    .weak undef
    
    .data
    foo:
    .quad undef@<!-- -->AUTH(da,42)
    .quad (undef + 16)@<!-- -->AUTH(da,42)

    We have a similar case in CHERI LLVM for capabilities, as those need run-time initialisation like PAuth signing, and use the following instead:

      if (e == R_ABS_CAP)
        return !sym.isPreemptible &amp;&amp; sym.isUndefWeak();

    However, we're currently based on an older version of LLD, and there's been a little refactoring here (we predate moving !config-&gt;isPic &amp;&amp; sym.isUndefWeak() from processAux into isStaticLinkTimeConstant), so that might not be quite the right change, and AArch64::relocate will need to know to write the full 64 bits to zero out the signing schema from the input (or replace it with the upper bits of the addend, especially if negative), so that's insufficient.

  3. asl commented on Dec 22, 2025

    @asl
    Contributor

    Also tagging @atrosinenko

  4. added theissue type on Dec 22, 2025
  5. kovdan01 commented on Dec 23, 2025

    @kovdan01
    Contributor

    @jrtc27 Thanks for your issue report and interest to PAuth! I see that the reproducer you've provided tests static linking. While the documentation has a section regarding static linking, it only provides a high-level view of how this could be implemented: https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#static-linking. And I'm not aware of anyone using that - it requires additional runtime support, which does not seem to be implemented by anyone.

    We've never actually tested that static linking works as expected for AUTH relocs at all (and I'm kinda surprised if this was working as expected for other cases with non-undef-weak symbols).

    @asl Would it make sense to just explicitly unsupport (e.g. emit an error message) static linking (at least as for now) if we have AUTH relocs? It would at least be less misleading because right now I suppose that we just get invalid binaries which make no sense (and unless runtime support for AUTH relocs in static binaries is present, there is no reason to use static linking with PAuth).

    Also, I wonder if we have any test cases for dynamic linking which violate the rule mentioned in the issue description:

    For a relocation that involves signing a pointer, if the target symbol is an undefined weak reference, the result of the relocation is 0 (nullptr) regardless of the signing schema.

    As far as I remember, when we have an undefined weak symbol and an AUTH reloc against that, we end up with a dynamic relocation against this symbol. This seems to work properly given that we have our own runtime pauth-testsuite involving tests with undef weak symbols and we also run regular llvm-test-suite which I believe should also include smth like that.

    @jrtc27 Were you facing similar issues with dynamic linking? If yes, could you please provide a reproducer for that as well? If no, could you please tell if this is "no, we were testing smth with dynamic linking and everything was OK" or "no, we were not testing dynamic linking at all"? Thanks in advance for your collaboration!

  6. asl commented on Dec 23, 2025

    @asl
    Contributor

    I think currently it is indeed reasonable to explicitly error out on static linking when pauth is involved. If any downstream platform would rely on it, then likely some private changes wrt emission of pauth relocs would be needed in any case

  7. jrtc27 commented on Dec 23, 2025

    @jrtc27
    ContributorAuthor

    No, it's not specific to static linking, I just wrote the test using that because it's the simplest to think about. Here's the same test tweaked to use a PIE:

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld -pie %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
    
    ## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
    ## to NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # DATA-LABEL: Hex dump of section '.data':
    # DATA-NEXT:  0x000302d8 00000000 00000000 10000000 00000000
    
    .weak undef
    
    .data
    foo:
    .quad undef@AUTH(da,42)
    .quad (undef + 16)@AUTH(da,42)

    Or for a DSO (need to try a bit harder to make symbols non-preemptible):

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld -shared %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
    
    ## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
    ## to NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # DATA-LABEL: Hex dump of section '.data':
    # DATA-NEXT:  0x000302b8 00000000 00000000 10000000 00000000
    
    .weak undef
    .hidden undef
    
    .data
    foo:
    .quad undef@AUTH(da,42)
    .quad (undef + 16)@AUTH(da,42)
  8. jrtc27 commented on Dec 23, 2025

    @jrtc27
    ContributorAuthor

    @asl Would it make sense to just explicitly unsupport (e.g. emit an error message) static linking (at least as for now) if we have AUTH relocs? It would at least be less misleading because right now I suppose that we just get invalid binaries which make no sense (and unless runtime support for AUTH relocs in static binaries is present, there is no reason to use static linking with PAuth).

    As far as I can tell static linking is generally fine, the dynamic relocs end up in .rela.dyn and encapsulated by __rel[a]_iplt_start/end just like IRELATIVE relocations (and how static PIE works for platforms that support that). Your runtime may not support that, but I don't see any reason why a runtime that did wouldn't work. That is, I don't see anything making the binaries "invalid", they look how I'd expect them to.

  9. jrtc27 commented on Dec 23, 2025

    @jrtc27
    ContributorAuthor

    Were you facing similar issues with dynamic linking? If yes, could you please provide a reproducer for that as well? If no, could you please tell if this is "no, we were testing smth with dynamic linking and everything was OK" or "no, we were not testing dynamic linking at all"? Thanks in advance for your collaboration!

    Just to be clear, I'm not actually using PAuth in any way. I have just been working to clean up upstream LLD in various ways, in particular around relative relocation handling, as I've spent a lot of time thinking about and refining our implementation downstream for CHERI/Morello, and there's a lot of overlap conceptually, so I've been aligning the two. My experience also means that, as I've been reading and refactoring the upstream code, ways to break it have popped into my head (sometimes aligned with bugs we've had in the past that I've fixed), and I've either submitted fixes for them or filed issues. So I don't actually care about PAuth specifically, but it's convenient that what it needs LLD to do overlaps with CHERI in some respects.

  10. kovdan01 commented on Dec 24, 2025

    @kovdan01
    Contributor

    @jrtc27 Thanks for additional test cases and for investing your time into this (especially given that pauth is not your main field of interest)! I'll dive into this deeper and report the results.

  11. jrtc27 commented on Feb 1, 2026

    @jrtc27
    ContributorAuthor

    NB this also applies to GOT entries, which follow a separate path (and would be easier to fix since there isn't a signing schema to replace).

    PDE test:

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld --static %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.got %t | FileCheck %s --check-prefix=GOT
    # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
    
    ## Verify that an auth GOT entry for a weak undefined symbol is resolved to
    ## NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # GOT-LABEL: Hex dump of section '.got':
    # GOT-NEXT:  0x00220198 00000000 00000000
    
    # DIS-LABEL: <_start>:
    # DIS-NEXT:    adrp x0, 0x220000
    # DIS-NEXT:    ldr  x0, [x0, #0x198]
    
    .weak undef
    
    .globl _start
    _start:
      adrp x0, :got_auth:undef
      ldr x0, [x0, :got_auth_lo12:undef]

    PIE test:

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld -pie %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.got %t | FileCheck %s --check-prefix=GOT
    # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
    
    ## Verify that an auth GOT entry for a weak undefined symbol is resolved to
    ## NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # GOT-LABEL: Hex dump of section '.got':
    # GOT-NEXT:  0x000202a8 00000000 00000000
    
    # DIS-LABEL: <_start>:
    # DIS-NEXT:    adrp x0, 0x20000
    # DIS-NEXT:    ldr  x0, [x0, #0x2a8]
    
    .weak undef
    
    .globl _start
    _start:
      adrp x0, :got_auth:undef
      ldr x0, [x0, :got_auth_lo12:undef]

    DSO test:

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
    # RUN: ld.lld -shared %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.got %t | FileCheck %s --check-prefix=GOT
    # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
    
    ## Verify that an auth GOT entry for a weak undefined symbol is resolved to
    ## NULL (plus addend).
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:  ]
    
    # GOT-LABEL: Hex dump of section '.got':
    # GOT-NEXT:  0x000202b0 00000000 00000000
    
    # DIS-LABEL: <_start>:
    # DIS-NEXT:    adrp x0, 0x20000
    # DIS-NEXT:    ldr  x0, [x0, #0x2b0]
    
    .weak undef
    .hidden undef
    
    .globl _start
    _start:
      adrp x0, :got_auth:undef
      ldr x0, [x0, :got_auth_lo12:undef]
  12. kovdan01 commented on Feb 5, 2026

    @kovdan01
    Contributor

    NB this also applies to GOT entries, which follow a separate path (and would be easier to fix since there isn't a signing schema to replace).

    Thanks @jrtc27 for providing additional context! I'll try to allocate some time to look into this during the next week

  13. 16 remaining items

  14. kovdan01 commented on May 11, 2026

    @kovdan01
    Contributor

    @jrtc27 Sorry for a delay, @smithp35 thanks for updating the docs!

    I've prepared a draft PR which partially resolves the issue #194636, but we still have an unresolved case with AUTH TLSDESC for undefined weak non-preemptible symbols.

    As discussed on the previous PAuth meeting, it would be nice to allow link-time relaxation for such cases. See the following ticket for details: ARM-software/abi-aa#393. I would be happy to discuss any details regarding that.

    I suppose that it's worth making such a relaxation mandatory in lld, since similar non-auth relaxations are already mandatory.

    So, as soon as we have a clear understanding of how things would look like in the docs, I'll submit a PR against AArch64 AsmPrinter (LLVM) and update the PR #194636 correspondingly.

  15. added 2 commits that reference this issue on May 12, 2026
    0bb1eb1
    31cc042
  16. kovdan01 commented on May 18, 2026

    @kovdan01
    Contributor

    JFYI: I've updated the lld PR #194636 to use the R_AARCH64_AUTH_TLSDESC_CALL introduced in ARM-software/abi-aa#395.

    I've also submitted #198327 implementing backend support for that. The lld PR it dependent on it.

  17. added 2 commits that reference this issue on May 25, 2026
    4bd8774
    f56b506
  18. jrtc27 commented on Jun 3, 2026

    @jrtc27
    ContributorAuthor

    For the slightly more general case, absolute symbols are also mis-handled (something we accidentally missed for CHERI until last week1...); for both data and GOT auth relocations they will end up with relative relocations, which is rather unhelpful when you want an absolute address. In CHERI land we made them behave the same as undef weak, i.e. link-time constants that look like you cast a plain integer to a pointer, which would translate to an unsigned integer value for PAuth. That may not be what makes most sense for you, though, in which case I guess you'd have to translate them to AUTH_ABS64 relocations against a null symbol instead as a special PAuth-specific class of handling? Test below that demonstrates the wrong behaviour, adapted from the PIE test in #194636 (notably, drops the TLSDESC case; absolute+TLS is just so utterly nonsensical I don't even want to think about how you might interpret it in some twisted manner that makes any semblance of being valid input):

    # REQUIRES: aarch64
    # RUN: llvm-mc -filetype=obj -triple=aarch64 -mattr=+pauth %s -o %t.o
    # RUN: ld.lld -pie --defsym abs=0x12345678 %t.o -o %t
    # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
    # RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
    # RUN: llvm-readelf -x.got  %t | FileCheck %s --check-prefix=GOT
    # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
    
    ## XXX: R_AARCH64_AUTH_ABS64 and GOT-generating relocations against an absolute
    ## symbol are resolved to a relative relocation.
    
    # RELA-LABEL: Relocations [
    # RELA-NEXT:    Section ({{.+}}) .rela.dyn {
    # RELA-NEXT:      0x20358 R_AARCH64_AUTH_RELATIVE - 0x12345678
    # RELA-NEXT:      0x30360 R_AARCH64_AUTH_RELATIVE - 0x12345678
    # RELA-NEXT:      0x30368 R_AARCH64_AUTH_RELATIVE - 0x1234569D
    # RELA-NEXT:    }
    # RELA-NEXT:  ]
    
    # DATA-LABEL: Hex dump of section '.data':
    # DATA-NEXT:  0x00030360 00000000 2a000020 00000000 2a000020
    # DATA-NEXT:  0x00030370 78563412 00000000 9d563412 00000000
    
    # GOT-LABEL:  Hex dump of section '.got':
    # GOT-NEXT:   0x00020358 00000000 000000a0
    
    # DIS-LABEL:  <_start>:
    # DIS-NEXT:     adrp x0, 0x20000
    # DIS-NEXT:     ldr  x0, [x0, #0x358]
    
    .globl _start
    _start:
      adrp  x0,  :got_auth:abs
      ldr   x0,  [x0, :got_auth_lo12:abs]
    
    .data
    foo:
    .quad abs@AUTH(da,42)
    .quad (abs + 37)@AUTH(da,42)
    .quad abs
    .quad abs + 37

    Footnotes

    1. https://github.com/CTSRD-CHERI/llvm-project/commit/71fefccdd45ed34b1f010809c0ed383add556fae#diff-7bda54aace526c7e883a5e1c34446b3ffca171b50a4340a8795ef0ecc0d1f291 if interested, though the actual code change is not very exciting ↩

  19. added 2 commits that reference this issue on Aug 3, 2026
    582a92d
    b48bca9
  20. added a commit that references this issue on Aug 21, 2026
    a6dd7f0
  21. added 2 commits that reference this issue on Sep 16, 2026
    d902a56
    d73dcde
  22. added a commit that references this issue on Sep 22, 2026
    368c7f0
  23. added a commit that references this issue on Sep 22, 2026
    d3cce17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions