[llvm-branch-commits] [lld] [lld-macho] Match ld64 for arm64e TLV bootstrap binds and map files (PR #234004)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Oct 21 10:47:57 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld-macho

Author: Oskar Wirga (oskarwirga)

<details>
<summary>Changes</summary>

Two small arm64e changes:

1. Sign the arm64e TLV bootstrap pointer as ld64 does. The first word of a thread-local variable descriptor is bound to `__tlv_bootstrap`, which dyld replaces with a pointer to `tlv_get_addr` that TLV accesses call with `blraaz`. On arm64e, write that bind as an auth'd bind with the IA key and no diversity, even though the object asks for a plain pointer.
2. List arm64e `__auth_got` entries in the map file. The map file printed the entries of `__got` but not those of `__auth_got`, which holds the targets of the stubs and the personalities.

Assisted-by: Claude


---
Full diff: https://github.com/llvm/llvm-project/pull/234004.diff


6 Files Affected:

- (modified) lld/MachO/InputSection.cpp (+16-1) 
- (modified) lld/MachO/InputSection.h (+2) 
- (modified) lld/MachO/MapFile.cpp (+11-6) 
- (modified) lld/MachO/Writer.cpp (+2-1) 
- (added) lld/test/MachO/arm64e-map.s (+36) 
- (added) lld/test/MachO/arm64e-tlv-bootstrap.s (+30) 


``````````diff
diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index 24e9283d05db5..a1fbd48b9044a 100644
--- a/lld/MachO/InputSection.cpp
+++ b/lld/MachO/InputSection.cpp
@@ -271,7 +271,10 @@ void ConcatInputSection::writeTo(uint8_t *buf) {
           !target->hasAttr(r.type, RelocAttrBits::AUTH)) {
         referentVA -= firstTLVDataSection->addr;
       } else if (needsFixup) {
-        writeChainedFixup(loc, referentSym, r.getAddend(), r.getAuthInfo());
+        const AuthInfo *auth = r.getAuthInfo();
+        if (!auth)
+          auth = getImplicitAuthInfo(this, referentSym);
+        writeChainedFixup(loc, referentSym, r.getAddend(), auth);
         continue;
       }
     } else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
@@ -420,6 +423,18 @@ bool macho::usesAuthGot(const InputSection *isec) {
   return config->arch() == AK_arm64e && isEhFrameSection(isec);
 }
 
+const AuthInfo *macho::getImplicitAuthInfo(const InputSection *isec,
+                                           const Symbol *sym) {
+  // The only pointer bound from a TLV descriptor is its thunk, which TLV
+  // accesses call with blraaz.
+  static constexpr AuthInfo tlvThunk = {/*diversity=*/0, PtrAuthKeyIA,
+                                        /*addrDiv=*/false};
+  if (config->arch() == AK_arm64e && isThreadLocalVariables(isec->getFlags()) &&
+      needsBinding(sym))
+    return &tlvThunk;
+  return nullptr;
+}
+
 bool macho::isGccExceptTabSection(const InputSection *isec) {
   return isec->getName() == section_names::gccExceptTab &&
          isec->getSegName() == segment_names::text;
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index abb130b4c363e..47e32429ea49b 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -311,6 +311,8 @@ bool isSelRefsSection(const InputSection *);
 bool isEhFrameSection(const InputSection *);
 bool isGccExceptTabSection(const InputSection *);
 bool usesAuthGot(const InputSection *);
+const AuthInfo *getImplicitAuthInfo(const InputSection *isec,
+                                    const Symbol *sym);
 
 extern std::vector<ConcatInputSection *> inputSections;
 // This is used as a counter for specyfing input order for input sections
diff --git a/lld/MachO/MapFile.cpp b/lld/MachO/MapFile.cpp
index 93d0e87a402a1..d85e19fc09bae 100644
--- a/lld/MachO/MapFile.cpp
+++ b/lld/MachO/MapFile.cpp
@@ -149,15 +149,15 @@ static void printObjCStubsEntries(raw_fd_ostream &os,
        << sym->getName() << '\n';
 }
 
-static void printNonLazyPointerSection(raw_fd_ostream &os, GotSection *osec) {
+// For printing the entries of the __got and __auth_got sections.
+static void printNonLazyPointer(raw_fd_ostream &os, uint64_t va,
+                                const Symbol *sym) {
   // ld64 considers stubs to belong to particular files, but considers GOT
   // entries to be linker-synthesized. Not sure why they made that decision, but
   // I think we can follow suit unless there's demand for better symbol-to-file
   // associations.
-  for (const Symbol *sym : osec->getEntries())
-    os << format("0x%08llX\t0x%08zX\t[  0] non-lazy-pointer-to-local: %s\n",
-                 osec->addr + sym->gotIndex * target->wordSize,
-                 target->wordSize, sym->getName().str().data());
+  os << format("0x%08llX\t0x%08zX\t[  0] non-lazy-pointer-to-local: %s\n", va,
+               target->wordSize, sym->getName().str().data());
 }
 
 static uint64_t getSymSizeForMap(Defined *sym) {
@@ -273,7 +273,12 @@ void macho::writeMapFile() {
         os << format("0x%08llX\t0x%08llX\t[  0] helper helper\n", osec->addr,
                      osec->getSize());
       } else if (osec == in.got) {
-        printNonLazyPointerSection(os, in.got);
+        for (const Symbol *sym : in.got->getEntries())
+          printNonLazyPointer(
+              os, in.got->addr + sym->gotIndex * target->wordSize, sym);
+      } else if (in.authGot && osec == in.authGot) {
+        for (const Symbol *sym : in.authGot->getEntries())
+          printNonLazyPointer(os, in.authGot->getVA(sym), sym);
       } else if (osec == in.objcMethList) {
         printIsecArrSyms(in.objcMethList->getInputs());
       }
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 97db232fbfcbb..bb556c17c52be 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -704,7 +704,8 @@ static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec,
     if (isAuth ||
         !(isThreadLocalVariables(isec->getFlags()) && isa<Defined>(sym)))
       addNonLazyBindingEntries(sym, isec, r.offset, r.getAddend(),
-                               /*forceOutlineAuth=*/isAuth);
+                               /*forceOutlineAuth=*/isAuth ||
+                                   getImplicitAuthInfo(isec, sym));
   }
 }
 
diff --git a/lld/test/MachO/arm64e-map.s b/lld/test/MachO/arm64e-map.s
new file mode 100644
index 0000000000000..c751368c06d04
--- /dev/null
+++ b/lld/test/MachO/arm64e-map.s
@@ -0,0 +1,36 @@
+# REQUIRES: aarch64
+## The map file lists the entries of arm64e's __auth_got like those of __got.
+
+# RUN: rm -rf %t; split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=arm64e-apple-macos -o %t/foo.o %t/foo.s
+# RUN: llvm-mc -filetype=obj -triple=arm64e-apple-macos -o %t/test.o %t/test.s
+# RUN: %lld -arch arm64e -platform_version macos 13.0 13.0 -dylib \
+# RUN:   -install_name @executable_path/libfoo.dylib %t/foo.o -o %t/libfoo.dylib
+# RUN: %lld -arch arm64e -platform_version macos 13.0 13.0 %t/test.o \
+# RUN:   %t/libfoo.dylib -map %t/map -o %t/test
+# RUN: llvm-objdump --macho --section-headers %t/test > %t/out
+# RUN: cat %t/map >> %t/out
+# RUN: FileCheck %s < %t/out
+
+# CHECK:      __auth_got {{[0-9a-f]+}} {{0*}}[[#%x,AUTH_GOT:]] DATA
+# CHECK:      __got      {{[0-9a-f]+}} {{0*}}[[#%x,GOT:]] DATA
+# CHECK-LABEL: # Sections:
+# CHECK:      0x[[#%X,AUTH_GOT]] 0x00000008 __DATA_CONST __auth_got
+# CHECK:      0x[[#%X,GOT]] 0x00000008 __DATA_CONST __got
+# CHECK-LABEL: # Symbols:
+# CHECK:      0x[[#%X,AUTH_GOT]] 0x00000008 [ 0] non-lazy-pointer-to-local: _foo
+# CHECK:      0x[[#%X,GOT]] 0x00000008 [ 0] non-lazy-pointer-to-local: _foo
+
+#--- foo.s
+.globl _foo
+_foo:
+  ret
+
+#--- test.s
+.globl _main
+.p2align 2
+_main:
+  bl _foo
+  adrp x0, _foo at GOTPAGE
+  ldr x0, [x0, _foo at GOTPAGEOFF]
+  ret
diff --git a/lld/test/MachO/arm64e-tlv-bootstrap.s b/lld/test/MachO/arm64e-tlv-bootstrap.s
new file mode 100644
index 0000000000000..44c0cd98db2ab
--- /dev/null
+++ b/lld/test/MachO/arm64e-tlv-bootstrap.s
@@ -0,0 +1,30 @@
+# REQUIRES: aarch64
+## On arm64e, the thunk pointer of a thread-local variable descriptor, a bind to
+## __tlv_bootstrap, is an authenticated bind with the IA key and no diversity,
+## because TLV accesses call it with blraaz. On arm64 it is a plain bind.
+
+# RUN: llvm-mc -filetype=obj -triple=arm64e-apple-macos -o %t-arm64e.o %s
+# RUN: %lld -arch arm64e -platform_version macos 13.0 13.0 -dylib %t-arm64e.o \
+# RUN:   -o %t-arm64e.dylib
+# RUN: llvm-objdump --macho --dyld-info %t-arm64e.dylib | \
+# RUN:   FileCheck %s --check-prefix=ARM64E
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o %t-arm64.o %s
+# RUN: %lld -arch arm64 -platform_version macos 13.0 13.0 -dylib %t-arm64.o \
+# RUN:   -o %t-arm64.dylib
+# RUN: llvm-objdump --macho --dyld-info %t-arm64.dylib | \
+# RUN:   FileCheck %s --check-prefix=ARM64
+
+# ARM64E: __DATA __thread_vars 0x{{[0-9A-F]+}} 0x{{[0-9A-F]+}} bind 0x0000 false IA 0x0 libSystem __tlv_bootstrap
+# ARM64:  __DATA __thread_vars 0x{{[0-9A-F]+}} 0x{{[0-9A-F]+}} bind 0x0 libSystem __tlv_bootstrap
+
+.section __DATA,__thread_data,thread_local_regular
+.p2align 2
+_tv$tlv$init:
+  .long 5
+
+.section __DATA,__thread_vars,thread_local_variables
+.globl _tv
+_tv:
+  .quad __tlv_bootstrap
+  .quad 0
+  .quad _tv$tlv$init

``````````

</details>


https://github.com/llvm/llvm-project/pull/234004


More information about the llvm-branch-commits mailing list