diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/Arch/ARM64.cpp @@ -0,0 +1,283 @@ +//===- ARM64.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct ARM64 : TargetInfo { + ARM64(); + + uint64_t getImplicitAddend(MemoryBufferRef, const section_64 &, + const relocation_info &) const override; + void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override; + + void writeStub(uint8_t *buf, const macho::Symbol &) const override; + void writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const override; + + void prepareSymbolRelocation(lld::macho::Symbol *, const InputSection *, + const Reloc &) override; + uint64_t resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &, + uint8_t type) const override; +}; + +} // namespace + +struct ARM64RelocAttrs { + RelocationInfoType type; + llvm::Optional TLV; + bool PCrel; + uint32_t lengths; +}; + +static ARM64RelocAttrs arm64RelocAttrs[] = { + {ARM64_RELOC_UNSIGNED, llvm::None, false, 8}, + {ARM64_RELOC_SUBTRACTOR, llvm::None, false, 8}, + {ARM64_RELOC_BRANCH26, llvm::None, true, 4}, + {ARM64_RELOC_PAGE21, llvm::None, true, 4}, + {ARM64_RELOC_PAGEOFF12, llvm::None, false, 4}, + {ARM64_RELOC_GOT_LOAD_PAGE21, false, true, 4}, + {ARM64_RELOC_GOT_LOAD_PAGEOFF12, false, false, 4}, + {ARM64_RELOC_POINTER_TO_GOT, false, true, 4}, + {ARM64_RELOC_TLVP_LOAD_PAGE21, true, true, 4}, + {ARM64_RELOC_TLVP_LOAD_PAGEOFF12, true, false, 4}, + {ARM64_RELOC_ADDEND, llvm::None, false, 4}, +}; + +uint64_t ARM64::getImplicitAddend(MemoryBufferRef mb, const section_64 &sec, + const relocation_info &rel) const { + ARM64RelocAttrs &relocAttrs = arm64RelocAttrs[rel.r_type]; + assert(rel.r_type == relocAttrs.type); + assert(rel.r_pcrel == relocAttrs.PCrel); + const auto *buf = reinterpret_cast(mb.getBufferStart()); + const uint8_t *loc = buf + sec.offset + rel.r_address; + + if (isThreadLocalVariables(sec.flags) && rel.r_type != ARM64_RELOC_UNSIGNED) + error("relocations in thread-local variable sections must be " + "ARM64_RELOC_UNSIGNED"); + + switch (rel.r_length) { + case 0: + return *loc; + case 1: + return read16le(loc); + case 2: + return read32le(loc); + case 3: + return read64le(loc); + default: + llvm_unreachable("invalid r_length"); + } +} + +inline uint64_t bitField(uint64_t value, int right, int width, int left) { + return ((value >> right) & ((1 << width) - 1)) << left; +} + +inline uint64_t fixupBranch26(uint64_t encoding, uint64_t va) { + return (encoding | bitField(va, 2, 26, 0)); +} + +inline uint64_t fixupPage21(uint64_t encoding, uint64_t va) { + return (encoding | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +} + +inline uint64_t fixupPageOff12(uint64_t encoding, uint64_t va) { + return (encoding | bitField(va, 0, 12, 10)); +} + +void ARM64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t va) const { + // For instruction fixups, r.addend contains the base encoding + uint64_t fixup = 0; + switch (r.type) { + case ARM64_RELOC_BRANCH26: + assert((va & 0x3) == 0); + fixup = fixupBranch26(r.addend, va); + break; + case ARM64_RELOC_UNSIGNED: + assert(r.addend == 0); + fixup = va; + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGE21: + fixup = fixupPage21(r.addend, va); + break; + case ARM64_RELOC_PAGEOFF12: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + fixup = fixupPageOff12(r.addend, va); + break; + case ARM64_RELOC_POINTER_TO_GOT: + case ARM64_RELOC_SUBTRACTOR: + case ARM64_RELOC_ADDEND: + fatal("TODO(gkm): handle relocation type " + std::to_string(r.type)); + default: + llvm_unreachable("unexpected relocation type"); + } + + switch (r.length) { + case 2: + write32le(loc, fixup); + break; + case 3: + write64le(loc, fixup); + break; + default: + llvm_unreachable("invalid r_length"); + } +} + +static constexpr uint32_t stubCode[] = { + 0x90000010, // 00: adrp x16, lazy_pointer@page + 0xf9400210, // 04: ldr x16, [x16, lazy_pointer@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64::writeStub(uint8_t *buf8, const macho::Symbol &sym) const { + auto *buf32 = reinterpret_cast(buf8); + auto pcVA = [&](int i) { return in.stubs->addr + i * sizeof(uint32_t); }; + uint64_t lazyPointerVA = in.lazyPointers->addr + sym.stubsIndex * WordSize; + buf32[0] = fixupPage21(stubCode[0], lazyPointerVA - pcVA(0)); + buf32[1] = fixupPageOff12(stubCode[1], lazyPointerVA - pcVA(1)); + buf32[2] = stubCode[2]; +} + +static constexpr uint32_t stubHelperHeaderCode[] = { + 0x90000011, // 00: adrp x17, dyld_imageLoaderCache@page + 0x91000231, // 04: add x17, x17, dyld_imageLoaderCache@pageoff + 0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]! + 0x90000010, // 0c: adrp x16, _fast_lazy_bind@page + 0xf9400210, // 10: ldr x16, [x16,_fast_lazy_bind@pageoff] + 0xd61f0200, // 14: br x16 +}; + +void ARM64::writeStubHelperHeader(uint8_t *buf8) const { + auto *buf32 = reinterpret_cast(buf8); + auto pcVA = [&](int i) { return in.stubHelper->addr + i * sizeof(uint32_t); }; + uint64_t loaderVA = in.imageLoaderCache->getVA(); + buf32[0] = fixupPage21(stubHelperHeaderCode[0], loaderVA - pcVA(0)); + buf32[1] = fixupPageOff12(stubHelperHeaderCode[1], loaderVA - pcVA(1)); + buf32[2] = stubHelperHeaderCode[2]; + uint64_t binderVA = + in.got->addr + in.stubHelper->stubBinder->gotIndex * WordSize; + buf32[3] = fixupPage21(stubHelperHeaderCode[3], binderVA - pcVA(3)); + buf32[4] = fixupPageOff12(stubHelperHeaderCode[4], binderVA - pcVA(4)); + buf32[5] = stubHelperHeaderCode[5]; +} + +static constexpr uint32_t stubHelperEntryCode[] = { + 0x18000050, // 00: ldr w16, l0 + 0x14000000, // 04: b helperhelper + 0x00000000, // 08: l0: .long 0 +}; + +void ARM64::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, + uint64_t entryVA) const { + auto *buf32 = reinterpret_cast(buf8); + auto pcVA = [&](int i) { return in.stubHelper->addr + i * sizeof(uint32_t); }; + buf32[0] = stubHelperEntryCode[0]; + buf32[1] = fixupBranch26(stubHelperEntryCode[1], entryVA - pcVA(1)); + buf32[2] = sym.lazyBindOffset; +} + +void ARM64::prepareSymbolRelocation(lld::macho::Symbol *sym, + const InputSection *isec, const Reloc &r) { + ARM64RelocAttrs &relocAttrs = arm64RelocAttrs[r.type]; + assert(r.type == relocAttrs.type); + assert(relocAttrs.TLV == llvm::None || relocAttrs.TLV == sym->isTlv()); + assert((relocAttrs.lengths & (1 << r.length)) != 0); + + switch (r.type) { + case ARM64_RELOC_BRANCH26: + prepareBranchTarget(sym); + break; + case ARM64_RELOC_UNSIGNED: + addNonLazyBindingEntries(sym, isec, r.offset, r.addend); + break; + case ARM64_RELOC_GOT_LOAD_PAGE21: + if (needsBinding(sym)) + in.got->addEntry(sym); + break; + case ARM64_RELOC_TLVP_LOAD_PAGE21: + if (needsBinding(sym)) + in.tlvPointers->addEntry(sym); + break; + case ARM64_RELOC_POINTER_TO_GOT: + in.got->addEntry(sym); + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_PAGEOFF12: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + case ARM64_RELOC_ADDEND: + break; + case ARM64_RELOC_SUBTRACTOR: + fatal("TODO(gkm): handle relocation type " + std::to_string(r.type)); + break; + default: + llvm_unreachable("unexpected relocation type"); + } +} + +uint64_t ARM64::resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &sym, + uint8_t type) const { + switch (type) { + case ARM64_RELOC_BRANCH26: + if (sym.isInStubs()) + return in.stubs->addr + sym.stubsIndex * sizeof(stubCode); + return sym.getVA(); + case ARM64_RELOC_UNSIGNED: + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_PAGEOFF12: + return sym.getVA(); + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + return in.got->addr + sym.gotIndex * WordSize; + case ARM64_RELOC_TLVP_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + if (sym.isInGot()) + return in.tlvPointers->addr + sym.gotIndex * WordSize; + assert(isa(&sym)); + return sym.getVA(); + break; + case ARM64_RELOC_SUBTRACTOR: + fatal("TODO(gkm): handle relocation type " + std::to_string(type)); + default: + llvm_unreachable("unexpected relocation type"); + } +} + +ARM64::ARM64() { + cpuType = CPU_TYPE_ARM64; + cpuSubtype = CPU_SUBTYPE_ARM64_ALL; + + stubSize = sizeof(stubCode); + stubHelperHeaderSize = sizeof(stubHelperHeaderCode); + stubHelperEntrySize = sizeof(stubHelperEntryCode); +} + +TargetInfo *macho::createARM64TargetInfo() { + static ARM64 t; + return &t; +} diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp --- a/lld/MachO/Arch/X86_64.cpp +++ b/lld/MachO/Arch/X86_64.cpp @@ -121,7 +121,9 @@ } } -void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const { +void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t va) const { + // For instruction fixups, r.addend contains the base encoding + va += r.addend; switch (r.type) { case X86_64_RELOC_BRANCH: case X86_64_RELOC_SIGNED: @@ -134,7 +136,7 @@ // These types are only used for pc-relative relocations, so offset by 4 // since the RIP has advanced by 4 at this point. This is only valid when // r_length = 2, which is enforced by validateLength(). - val -= 4; + va -= 4; break; case X86_64_RELOC_UNSIGNED: break; @@ -145,16 +147,16 @@ switch (r.length) { case 0: - *loc = val; + *loc = va; break; case 1: - write16le(loc, val); + write16le(loc, va); break; case 2: - write32le(loc, val); + write32le(loc, va); break; case 3: - write64le(loc, val); + write64le(loc, va); break; default: llvm_unreachable("invalid r_length"); @@ -196,11 +198,6 @@ 0x90, // 0xf: nop }; -static constexpr uint8_t stubHelperEntry[] = { - 0x68, 0, 0, 0, 0, // 0x0: pushq - 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> -}; - void X86_64::writeStubHelperHeader(uint8_t *buf) const { memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA()); @@ -209,6 +206,11 @@ in.stubHelper->stubBinder->gotIndex * WordSize); } +static constexpr uint8_t stubHelperEntry[] = { + 0x68, 0, 0, 0, 0, // 0x0: pushq + 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> +}; + void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, uint64_t entryAddr) const { memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -6,6 +6,7 @@ add_lld_library(lldMachO2 Arch/X86_64.cpp + Arch/ARM64.cpp UnwindInfoSection.cpp Driver.cpp DriverUtils.cpp diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -166,6 +166,9 @@ case llvm::MachO::AK_x86_64: case llvm::MachO::AK_x86_64h: return createX86_64TargetInfo(); + case llvm::MachO::AK_arm64: + case llvm::MachO::AK_arm64e: + return createARM64TargetInfo(); default: fatal("missing or unsupported -arch " + arch); } @@ -414,7 +417,7 @@ } // TODO: Update when we extend support for other archs - if (arch != "x86_64") + if (arch != "x86_64" && arch != "arm64") continue; } diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -51,10 +51,9 @@ referentVA = referentIsec->getVA(); } - uint64_t referentVal = referentVA + r.addend; if (r.pcrel) - referentVal -= getVA() + r.offset; - target->relocateOne(buf + r.offset, r, referentVal); + referentVA -= getVA() + r.offset; + target->relocateOne(buf + r.offset, r, referentVA); } } diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -53,8 +53,8 @@ void MachHeaderSection::writeTo(uint8_t *buf) const { auto *hdr = reinterpret_cast(buf); hdr->magic = MachO::MH_MAGIC_64; - hdr->cputype = MachO::CPU_TYPE_X86_64; - hdr->cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL | MachO::CPU_SUBTYPE_LIB64; + hdr->cputype = target->cpuType; + hdr->cpusubtype = target->cpuSubtype | MachO::CPU_SUBTYPE_LIB64; hdr->filetype = config->outputType; hdr->ncmds = loadCommands.size(); hdr->sizeofcmds = sizeOfCmds; diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -68,6 +68,7 @@ }; TargetInfo *createX86_64TargetInfo(); +TargetInfo *createARM64TargetInfo(); extern TargetInfo *target; diff --git a/lld/test/MachO/relocations-arm64.s b/lld/test/MachO/relocations-arm64.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/relocations-arm64.s @@ -0,0 +1,49 @@ +# REQUIRES: aarch64 +# RUN: rm -fr %t +# RUN: split-file %s %t + +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-ios %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-ios %t/write.s -o %t/write.o +# RUN: %lld -lSystem -arch arm64 -o %t/main %t/main.o %t/write.o +# RUN: llvm-objdump --section-headers --syms -d %t/main | FileCheck %s + +# CHECK-LABEL: Sections: +# CHECK: __cstring {{[0-9a-f]+}} {{([[:xdigit:]]{13})}}[[#%.3x, CSTRING_OFF:]] DATA + +# CHECK-LABEL: SYMBOL TABLE: +# CHECK: [[#%x,FUNC_ADDR:]] {{.*}} _write + +# CHECK-LABEL: <_main>: +## Test ARM64_RELOC_PAGE21 / ARM64_RELOC_PAGEOFF12 +# CHECK: adrp x1, #0 +# CHECK-NEXT: add x1, x1, #[[#%u, CSTRING_OFF]] +## Test ARM64_RELOC_BRANCH26 +# CHECK: bl 0x[[#%x, FUNC_ADDR]] <_write> + +#--- main.s +.section __TEXT,__text +.globl _main +.p2align 2 +_main: + stp x29, x30, [sp, #-16]! + mov x29, sp +Lloh0: + adrp x1, l_.str@PAGE +Lloh1: + add x1, x1, l_.str@PAGEOFF + orr w0, wzr, #0x1 + mov w2, #13 + bl _write + mov w0, #0 + ldp x29, x30, [sp], #16 + ret + +.section __TEXT,__cstring +l_.str: + .asciz "hello world\n" + +#--- write.s +.globl _write +.p2align 2 +_write: + ret diff --git a/lld/test/MachO/relocations.s b/lld/test/MachO/relocations-x86_84.s rename from lld/test/MachO/relocations.s rename to lld/test/MachO/relocations-x86_84.s diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -67,6 +67,7 @@ [('--build-mode', {'DEBUG': 'debug'}), ('--assertion-mode', {'ON': 'asserts'}), ('--targets-built', {'AArch64': 'aarch64', + 'ARM64': 'arm64', 'AMDGPU': 'amdgpu', 'ARM': 'arm', 'AVR': 'avr',