From 83351d0508208a08475824d2f17badfbb8029012 Mon Sep 17 00:00:00 2001 From: Jez Ng 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 | 27 ++++++++++++++---- lld/MachO/InputFiles.cpp | 8 ++---- lld/MachO/Relocations.h | 28 ++++++++++--------- .../invalid/invalid-relocation-length.yaml | 2 +- lld/test/MachO/x86-64-relocs.s | 18 ++++++++++-- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp index 0a950f28f02ae..a7c4b452f990b 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 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)}, @@ -82,25 +82,40 @@ int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, relocation_info rel) const { auto *buf = reinterpret_cast(mb.getBufferStart()); const uint8_t *loc = buf + offset + rel.r_address; + int64_t addend; switch (rel.r_length) { + case 0: + addend = static_cast(*loc); + break; case 2: - return static_cast(read32le(loc)) + pcrelOffset(rel.r_type); + addend = static_cast(read32le(loc)); + break; case 3: - return read64le(loc) + pcrelOffset(rel.r_type); + addend = read64le(loc); + break; default: llvm_unreachable("invalid r_length"); } + + return addend + pcrelOffset(rel.r_type); } 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(1 << rel.r_length))) { - static SmallVector 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(relocAttrs.bits) >> 2) & 3] + + if (!relocAttrs.hasAttr(static_cast(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..530394877437e 100644 --- a/lld/test/MachO/x86-64-relocs.s +++ b/lld/test/MachO/x86-64-relocs.s @@ -7,11 +7,15 @@ # CHECK: __data {{[0-9a-z]+}} [[#%x, DATA_ADDR:]] # CHECK-LABEL: SYMBOL TABLE: -# CHECK: [[#%x, F_ADDR:]] {{.*}} _f +# CHECK-DAG: [[#%x, F_ADDR:]] {{.*}} _f +# CHECK-DAG: [[#%x, G_ADDR:]] {{.*}} _g # CHECK-LABEL: <_main>: ## Test X86_64_RELOC_BRANCH # CHECK: callq 0x[[#%x, F_ADDR]] <_f> +# CHECK: jrcxz 0x[[#%x, F_ADDR]] <_f> +# CHECK: callq 0x[[#%x, G_ADDR]] <_g> +# CHECK: jrcxz 0x[[#%x, G_ADDR]] <_g> ## Test extern (symbol) X86_64_RELOC_SIGNED # CHECK: leaq [[#%u, LOCAL_OFF:]](%rip), %rsi # CHECK-NEXT: [[#%x, DATA_ADDR - LOCAL_OFF]] @@ -24,12 +28,20 @@ # NONPCREL-NEXT: 100001000 08200000 01000000 08200000 01000000 .section __TEXT,__text -.globl _main, _f +.globl _main, _f, _g + _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 + # test negative addends + callq _f - 1 + jrcxz _f - 1 mov $0, %rax ret +_g: + .byte 0x0 + _f: leaq _local(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel symbol relocation leaq L_.private(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel section relocation