[lld] [lld][macho] Support 1-byte branch relocs for x86_64 (PR #164439)

via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 21 08:12:22 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld

Author: Jez Ng (int3)

<details>
<summary>Changes</summary>

Fixes #<!-- -->160894.

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


5 Files Affected:

- (modified) lld/MachO/Arch/X86_64.cpp (+13-4) 
- (modified) lld/MachO/InputFiles.cpp (+2-6) 
- (modified) lld/MachO/Relocations.h (+15-13) 
- (modified) lld/test/MachO/invalid/invalid-relocation-length.yaml (+1-1) 
- (modified) lld/test/MachO/x86-64-relocs.s (+3-1) 


``````````diff
diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp
index 0a950f28f02ae..d3d7341aa6799 100644
--- a/lld/MachO/Arch/X86_64.cpp
+++ b/lld/MachO/Arch/X86_64.cpp
@@ -51,10 +51,10 @@ struct X86_64 : TargetInfo {
 
 static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
 #define B(x) RelocAttrBits::x
-    {"UNSIGNED",
-     B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
+    {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE1) |
+                     B(BYTE4) | B(BYTE8)},
     {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-    {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE1) | B(BYTE4)},
     {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
     {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
     {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
@@ -84,6 +84,8 @@ int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
   const uint8_t *loc = buf + offset + rel.r_address;
 
   switch (rel.r_length) {
+  case 0:
+    return *loc + pcrelOffset(rel.r_type);
   case 2:
     return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);
   case 3:
@@ -96,11 +98,18 @@ int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
 void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
                          uint64_t relocVA) const {
   if (r.pcrel) {
-    uint64_t pc = relocVA + 4 + pcrelOffset(r.type);
+    uint64_t pc = relocVA + (1 << r.length) + pcrelOffset(r.type);
     value -= pc;
   }
 
   switch (r.length) {
+  case 0:
+    if (r.type == X86_64_RELOC_UNSIGNED)
+      checkUInt(loc, r, value, 8);
+    else
+      checkInt(loc, r, value, 8);
+    *loc = value;
+    break;
   case 2:
     if (r.type == X86_64_RELOC_UNSIGNED)
       checkUInt(loc, r, value, 32);
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 442fc608865d2..20e4a1d755229 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -516,12 +516,8 @@ static bool validateRelocationInfo(InputFile *file, const SectionHeader &sec,
   if (isThreadLocalVariables(sec.flags) &&
       !relocAttrs.hasAttr(RelocAttrBits::UNSIGNED))
     error(message("not allowed in thread-local section, must be UNSIGNED"));
-  if (rel.r_length < 2 || rel.r_length > 3 ||
-      !relocAttrs.hasAttr(static_cast<RelocAttrBits>(1 << rel.r_length))) {
-    static SmallVector<StringRef, 4> widths{"0", "4", "8", "4 or 8"};
-    error(message("has width " + std::to_string(1 << rel.r_length) +
-                  " bytes, but must be " +
-                  widths[(static_cast<int>(relocAttrs.bits) >> 2) & 3] +
+  if (!relocAttrs.hasAttr(static_cast<RelocAttrBits>(1 << rel.r_length))) {
+    error(message("has invalid width of " + std::to_string(1 << rel.r_length) +
                   " bytes"));
   }
   return valid;
diff --git a/lld/MachO/Relocations.h b/lld/MachO/Relocations.h
index b2f621451349e..d3347de36781b 100644
--- a/lld/MachO/Relocations.h
+++ b/lld/MachO/Relocations.h
@@ -25,21 +25,23 @@ class InputSection;
 
 enum class RelocAttrBits {
   _0 = 0,              // invalid
-  PCREL = 1 << 0,      // Value is PC-relative offset
-  ABSOLUTE = 1 << 1,   // Value is an absolute address or fixed offset
+  BYTE1 = 1 << 0,      // 1 byte datum
+  BYTE2 = 1 << 1,      // 2 byte datum
   BYTE4 = 1 << 2,      // 4 byte datum
   BYTE8 = 1 << 3,      // 8 byte datum
-  EXTERN = 1 << 4,     // Can have an external symbol
-  LOCAL = 1 << 5,      // Can have a local symbol
-  ADDEND = 1 << 6,     // *_ADDEND paired prefix reloc
-  SUBTRAHEND = 1 << 7, // *_SUBTRACTOR paired prefix reloc
-  BRANCH = 1 << 8,     // Value is branch target
-  GOT = 1 << 9,        // References a symbol in the Global Offset Table
-  TLV = 1 << 10,       // References a thread-local symbol
-  LOAD = 1 << 11,      // Relaxable indirect load
-  POINTER = 1 << 12,   // Non-relaxable indirect load (pointer is taken)
-  UNSIGNED = 1 << 13,  // *_UNSIGNED relocs
-  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ (1 << 14) - 1),
+  PCREL = 1 << 4,      // Value is PC-relative offset
+  ABSOLUTE = 1 << 5,   // Value is an absolute address or fixed offset
+  EXTERN = 1 << 6,     // Can have an external symbol
+  LOCAL = 1 << 7,      // Can have a local symbol
+  ADDEND = 1 << 8,     // *_ADDEND paired prefix reloc
+  SUBTRAHEND = 1 << 9, // *_SUBTRACTOR paired prefix reloc
+  BRANCH = 1 << 10,    // Value is branch target
+  GOT = 1 << 11,       // References a symbol in the Global Offset Table
+  TLV = 1 << 12,       // References a thread-local symbol
+  LOAD = 1 << 13,      // Relaxable indirect load
+  POINTER = 1 << 14,   // Non-relaxable indirect load (pointer is taken)
+  UNSIGNED = 1 << 15,  // *_UNSIGNED relocs
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ (1 << 16) - 1),
 };
 // Note: SUBTRACTOR always pairs with UNSIGNED (a delta between two symbols).
 
diff --git a/lld/test/MachO/invalid/invalid-relocation-length.yaml b/lld/test/MachO/invalid/invalid-relocation-length.yaml
index ff8759b417478..2661e4ea165ec 100644
--- a/lld/test/MachO/invalid/invalid-relocation-length.yaml
+++ b/lld/test/MachO/invalid/invalid-relocation-length.yaml
@@ -2,7 +2,7 @@
 # RUN: yaml2obj %s -o %t.o
 # RUN: not %lld -o %t %t.o 2>&1 | FileCheck %s -DFILE=%t.o
 #
-# CHECK: error: UNSIGNED relocation has width 2 bytes, but must be 4 or 8 bytes at offset 1 of __TEXT,__text in [[FILE]]
+# CHECK: error: UNSIGNED relocation has invalid width of 2 bytes at offset 1 of __TEXT,__text in [[FILE]]
 
 !mach-o
 FileHeader:
diff --git a/lld/test/MachO/x86-64-relocs.s b/lld/test/MachO/x86-64-relocs.s
index cdeee96c182f0..422808df81357 100644
--- a/lld/test/MachO/x86-64-relocs.s
+++ b/lld/test/MachO/x86-64-relocs.s
@@ -12,6 +12,7 @@
 # CHECK-LABEL: <_main>:
 ## Test X86_64_RELOC_BRANCH
 # CHECK:       callq 0x[[#%x, F_ADDR]] <_f>
+# CHECK:       jrcxz 0x[[#%x, F_ADDR]] <_f>
 ## Test extern (symbol) X86_64_RELOC_SIGNED
 # CHECK:       leaq [[#%u, LOCAL_OFF:]](%rip), %rsi
 # CHECK-NEXT:  [[#%x, DATA_ADDR - LOCAL_OFF]]
@@ -26,7 +27,8 @@
 .section __TEXT,__text
 .globl _main, _f
 _main:
-  callq _f # X86_64_RELOC_BRANCH
+  callq _f # X86_64_RELOC_BRANCH with r_length=2
+  jrcxz _f # X86_64_RELOC_BRANCH with r_length=0
   mov $0, %rax
   ret
 

``````````

</details>


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


More information about the llvm-commits mailing list