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

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 21 08:11:35 PDT 2025


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

Fixes #160894.

>From 245c582d075aca58a265e975efd72fc6311e377d Mon Sep 17 00:00:00 2001
From: Jez Ng <me at jezng.com>
Date: Tue, 21 Oct 2025 10:27:45 -0400
Subject: [PATCH] [lld][macho] Support 1-byte branch relocs for x86_64

Fixes #160894.
---
 lld/MachO/Arch/X86_64.cpp                     | 17 ++++++++---
 lld/MachO/InputFiles.cpp                      |  8 ++----
 lld/MachO/Relocations.h                       | 28 ++++++++++---------
 .../invalid/invalid-relocation-length.yaml    |  2 +-
 lld/test/MachO/x86-64-relocs.s                |  4 ++-
 5 files changed, 34 insertions(+), 25 deletions(-)

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
 



More information about the llvm-commits mailing list