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,317 @@ +//===- 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/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct ARM64 : TargetInfo { + ARM64(); + + uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, + const relocation_info) const override; + void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, + uint64_t pc) 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; + void morphLoadIntoAdd(uint8_t *loc, uint8_t type) const override; +}; + +} // namespace + +// Random notes on reloc types: +// ADDEND always pairs with BRANCH26, PAGE21, or PAGEOFF12 +// SUBTRACTOR always pairs with UNSIGNED (a delta between two sections) +// POINTER_TO_GOT: 4-byte is pc-relative, 8-byte is absolute + +static TargetInfo::RelocAttrs relocAttrsVector[] = { +#define B(x) RelocAttrBits::x + {"UNSIGNED", B(TLS) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(_4) | B(_8)}, + {"SUBTRACTOR", B(SUBTRAHEND)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(_4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(_4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(_4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(_4)}, + {"GOT_LOAD_PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(MORPH) | B(_4)}, + {"POINTER_TO_GOT", B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(_4) | B(_8)}, + {"TLVP_LOAD_PAGE21", B(TLV) | B(PCREL) | B(EXTERN) | B(_4)}, + {"TLVP_LOAD_PAGEOFF12", + B(TLV) | B(ABSOLUTE) | B(EXTERN) | B(MORPH) | B(_4)}, + {"ADDEND", B(ADDEND)}, +#undef B +}; + +uint64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, + const relocation_info rel) const { + // TODO(gkm): extract embedded addend just so we can assert that it is 0 + return 0; +} + +inline uint64_t bitField(uint64_t value, int right, int width, int left) { + return ((value >> right) & ((1 << width) - 1)) << left; +} + +// 25 0 +// +-----------+---------------------------------------------------+ +// | | imm26 | +// +-----------+---------------------------------------------------+ + +inline uint64_t encodeBranch26(uint64_t base, uint64_t va) { + // Since branch destinations are 4-byte aligned, the 2 least- + // significant bits are 0. They are right shifted off the end. + return (base | bitField(va, 2, 26, 0)); +} + +// 30 29 23 5 +// +-+---+---------+-------------------------------------+---------+ +// | |ilo| | immhi | | +// +-+---+---------+-------------------------------------+---------+ + +inline uint64_t encodePage21(uint64_t base, uint64_t va) { + return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +} + +// 21 10 +// +-------------------+-----------------------+-------------------+ +// | | imm12 | | +// +-------------------+-----------------------+-------------------+ + +inline uint64_t encodePageOff12(uint64_t base, uint64_t va) { + int scale = ((base & 0x3b000000) == 0x39000000) ? base >> 30 : 0; + // TODO(gkm): extract embedded addend and warn if != 0 + // uint64_t addend = ((base & 0x003FFC00) >> 10); + return (base | bitField(va, scale, 12 - scale, 10)); +} + +inline uint64_t pageBits(uint64_t address) { + const uint64_t pageMask = ~0xfffull; + return address & pageMask; +} + +// For instruction relocations (load/store & address arithmetic), +// r.addend contains the base encoding, which are opcode and +// non-relococatable operand fields pre-populated. The the relocatable +// operand field (BRANCH26, PAGE21, PAGEOFF12) are zero-filled. + +void ARM64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, + uint64_t pc) const { + uint32_t base = ((r.length == 2) ? read32le(loc) : 0); + value += r.addend; + switch (r.type) { + case ARM64_RELOC_BRANCH26: + value = encodeBranch26(base, value - pc); + break; + case ARM64_RELOC_UNSIGNED: + break; + case ARM64_RELOC_POINTER_TO_GOT: + if (r.pcrel) + value -= pc; + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGE21: + assert(r.pcrel); + value = encodePage21(base, pageBits(value) - pageBits(pc)); + break; + case ARM64_RELOC_PAGEOFF12: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + assert(!r.pcrel); + value = encodePageOff12(base, value); + break; + default: + llvm_unreachable("unexpected relocation type"); + } + + switch (r.length) { + case 2: + write32le(loc, value); + break; + case 3: + write64le(loc, value); + break; + default: + llvm_unreachable("invalid r_length"); + } +} + +static constexpr uint32_t stubCode[] = { + 0x90000010, // 00: adrp x16, __la_symbol_ptr@page + 0xf9400210, // 04: ldr x16, [x16, __la_symbol_ptr@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64::writeStub(uint8_t *buf8, const macho::Symbol &sym) const { + auto *buf32 = reinterpret_cast(buf8); + uint64_t pcPageBits = + pageBits(in.stubs->addr + sym.stubsIndex * sizeof(stubCode)); + uint64_t lazyPointerVA = in.lazyPointers->addr + sym.stubsIndex * WordSize; + buf32[0] = encodePage21(stubCode[0], pageBits(lazyPointerVA) - pcPageBits); + buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA); + buf32[2] = stubCode[2]; +} + +static constexpr uint32_t stubHelperHeaderCode[] = { + 0x90000011, // 00: adrp x17, _dyld_private@page + 0x91000231, // 04: add x17, x17, _dyld_private@pageoff + 0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]! + 0x90000010, // 0c: adrp x16, dyld_stub_binder@page + 0xf9400210, // 10: ldr x16, [x16, dyld_stub_binder@pageoff] + 0xd61f0200, // 14: br x16 +}; + +void ARM64::writeStubHelperHeader(uint8_t *buf8) const { + auto *buf32 = reinterpret_cast(buf8); + auto pcPageBits = [](int i) { + return pageBits(in.stubHelper->addr + i * sizeof(uint32_t)); + }; + uint64_t loaderVA = in.imageLoaderCache->getVA(); + buf32[0] = + encodePage21(stubHelperHeaderCode[0], pageBits(loaderVA) - pcPageBits(0)); + buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA); + buf32[2] = stubHelperHeaderCode[2]; + uint64_t binderVA = + in.got->addr + in.stubHelper->stubBinder->gotIndex * WordSize; + buf32[3] = + encodePage21(stubHelperHeaderCode[3], pageBits(binderVA) - pcPageBits(3)); + buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA); + buf32[5] = stubHelperHeaderCode[5]; +} + +static constexpr uint32_t stubHelperEntryCode[] = { + 0x18000050, // 00: ldr w16, l0 + 0x14000000, // 04: b stubHelperHeader + 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 = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); }; + uint64_t stubHelperHeaderVA = in.stubHelper->addr; + buf32[0] = stubHelperEntryCode[0]; + buf32[1] = + encodeBranch26(stubHelperEntryCode[1], stubHelperHeaderVA - pcVA(1)); + buf32[2] = sym.lazyBindOffset; +} + +void ARM64::prepareSymbolRelocation(lld::macho::Symbol *sym, + const InputSection *isec, const Reloc &r) { + // TODO(gkm): hoist into target-independent code driven by RelocAttrBits + switch (r.type) { + case ARM64_RELOC_BRANCH26: + prepareBranchTarget(sym); + break; + case ARM64_RELOC_UNSIGNED: + if (!(isThreadLocalVariables(isec->flags) && isa(sym))) + addNonLazyBindingEntries(sym, isec, r.offset, r.addend); + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_PAGEOFF12: + break; + case ARM64_RELOC_POINTER_TO_GOT: + in.got->addEntry(sym); + break; + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + if (needsBinding(sym)) + in.got->addEntry(sym); + break; + case ARM64_RELOC_TLVP_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + if (needsBinding(sym)) + in.tlvPointers->addEntry(sym); + break; + default: + llvm_unreachable("unexpected relocation type"); + } +} + +void ARM64::morphLoadIntoAdd(uint8_t *loc, uint8_t type) const { + uint32_t instruction = read32le(loc); + // C6.2.131 LDR (immediate) + // LDR , [{, #}] + if ((instruction & 0xffc00000) != 0xf9400000) + error(relocAttrsByType[type].name + " reloc requires LDR instruction"); + assert(((instruction >> 10) & 0xfff) == 0 && + "non-zero embedded LDR immediate"); + // C6.2.4 ADD (immediate) + // ADD , , #{, } + instruction = ((instruction & 0x001fffff) | 0x91000000); + write32le(loc, instruction); +} + +uint64_t ARM64::resolveSymbolVA(uint8_t *loc, const lld::macho::Symbol &sym, + uint8_t type) const { + // TODO(gkm): hoist into target-independent code driven by RelocAttrBits + switch (type) { + case ARM64_RELOC_BRANCH26: + if (sym.isInStubs()) + return in.stubs->addr + sym.stubsIndex * sizeof(stubCode); + return sym.getVA(); + case ARM64_RELOC_SUBTRACTOR: + case ARM64_RELOC_UNSIGNED: + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_PAGEOFF12: + return sym.getVA(); + case ARM64_RELOC_POINTER_TO_GOT: + assert(sym.isInGot()); + return in.got->addr + sym.gotIndex * WordSize; + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + if (sym.isInGot()) + return in.got->addr + sym.gotIndex * WordSize; + return sym.getVA(); + 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(); + 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); + relocAttrsByType = relocAttrsVector; +} + +TargetInfo *macho::createARM64TargetInfo() { + static ARM64 t; + return &t; +} 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 @@ -126,15 +126,16 @@ } static TargetInfo *createTargetInfo(opt::InputArgList &args) { - StringRef arch = args.getLastArgValue(OPT_arch, "x86_64"); - config->arch = MachO::getArchitectureFromName( - args.getLastArgValue(OPT_arch, arch)); - switch (config->arch) { - case MachO::AK_x86_64: - case MachO::AK_x86_64h: + // TODO: should unspecified arch be an error rather than defaulting? + StringRef archName = args.getLastArgValue(OPT_arch, "x86_64"); + config->arch = MachO::getArchitectureFromName(archName); + switch (MachO::getCPUTypeFromArchitecture(config->arch).first) { + case MachO::CPU_TYPE_X86_64: return createX86_64TargetInfo(); + case MachO::CPU_TYPE_ARM64: + return createARM64TargetInfo(); default: - fatal("missing or unsupported -arch " + arch); + fatal("missing or unsupported -arch " + archName); } } @@ -432,7 +433,8 @@ if (cpuType != CPU_TYPE_ANY) line = line.drop_until([](char c) { return c == ':'; }).drop_front(); // TODO: Update when we extend support for other CPUs - if (cpuType != CPU_TYPE_ANY && cpuType != CPU_TYPE_X86_64) + if (cpuType != CPU_TYPE_ANY && cpuType != CPU_TYPE_X86_64 && + cpuType != CPU_TYPE_ARM64) continue; constexpr std::array fileEnds = {".o:", ".o):"}; @@ -633,7 +635,8 @@ // TODO: add logic here as we support more archs. E.g. i386 should default // to PIE from 10.7, arm64 should always be PIE, etc - assert(config->arch == AK_x86_64 || config->arch == AK_x86_64h); + assert(config->arch == AK_x86_64 || config->arch == AK_x86_64h || + config->arch == AK_arm64 || config->arch == AK_arm64e); PlatformKind kind = config->platform.kind; if (kind == PlatformKind::macOS && diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -21,6 +21,7 @@ class InputSection; class OutputSection; class Symbol; +class Defined; struct Reloc { uint8_t type; @@ -32,7 +33,7 @@ // Adding this offset to the address of the referent symbol or subsection // gives the destination that this relocation refers to. uint64_t addend; - llvm::PointerUnion referent; + llvm::PointerUnion referent; }; class InputSection { diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -56,8 +56,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; @@ -380,9 +380,7 @@ in.rebase->addEntry(section, offset); if (defined->isExternalWeakDef()) in.weakBinding->addEntry(sym, section, offset, addend); - } else if (isa(sym)) { - error("cannot bind to " + DSOHandle::name); - } else { + } else if (!isa(sym)) { // Undefined symbols are filtered out in scanRelocations(); we should never // get here llvm_unreachable("cannot bind to an undefined symbol"); @@ -713,8 +711,11 @@ // TODO: when we implement -dead_strip, we should filter out symbols // that belong to dead sections. if (auto *defined = dyn_cast(sym)) { - if (!defined->isExternal()) - addSymbol(localSymbols, sym); + if (!defined->isExternal()) { + StringRef name = defined->getName(); + if (!name.startswith("l") && !name.startswith("L")) + addSymbol(localSymbols, sym); + } } } } diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -110,6 +110,7 @@ }; TargetInfo *createX86_64TargetInfo(); +TargetInfo *createARM64TargetInfo(); extern TargetInfo *target; diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd --- a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd +++ b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd @@ -1,42 +1,42 @@ --- !tapi-tbd-v3 -archs: [ x86_64 ] -uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000000' ] +archs: [ x86_64, arm64 ] +uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000000', 'arm64: 00000000-0000-0000-0000-000000000010' ] platform: macosx install-name: '/usr/lib/libSystem.B.dylib' current-version: 0001.001.1 exports: - - archs: [ 'x86_64' ] + - archs: [ 'x86_64', 'arm64' ] re-exports: [ '/usr/lib/system/libdyld.dylib', '/usr/lib/system/libsystem_c.dylib', '/usr/lib/system/libsystem_m.dylib' ] --- !tapi-tbd-v3 -archs: [ x86_64 ] -uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000001' ] +archs: [ x86_64, arm64 ] +uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000001', 'arm64: 00000000-0000-0000-0000-000000000011' ] platform: macosx install-name: '/usr/lib/system/libdyld.dylib' current-version: 0001.001.1 parent-umbrella: System exports: - - archs: [ 'x86_64' ] + - archs: [ 'x86_64', 'arm64' ] symbols: [ dyld_stub_binder, __tlv_bootstrap ] --- !tapi-tbd-v3 -archs: [ x86_64 ] -uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002' ] +archs: [ x86_64, arm64 ] +uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002', 'arm64: 00000000-0000-0000-0000-000000000012' ] platform: macosx install-name: '/usr/lib/system/libsystem_c.dylib' current-version: 0001.001.1 parent-umbrella: System exports: - - archs: [ 'x86_64' ] + - archs: [ 'x86_64', 'arm64' ] symbols: [ ] --- !tapi-tbd-v3 -archs: [ x86_64 ] -uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000003' ] +archs: [ x86_64, arm64 ] +uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000003', 'arm64: 00000000-0000-0000-0000-000000000013' ] platform: macosx install-name: '/usr/lib/system/libsystem_m.dylib' current-version: 0001.001.1 parent-umbrella: System exports: - - archs: [ 'x86_64' ] + - archs: [ 'x86_64', 'arm64' ] symbols: [ ___nan ] ... diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++.tbd --- a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++.tbd +++ b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++.tbd @@ -1,10 +1,10 @@ --- !tapi-tbd-v3 archs: [ i386, x86_64 ] -uuids: [ 'i386: 00000000-0000-0000-0000-000000000000', 'x86_64: 00000000-0000-0000-0000-000000000001' ] +uuids: [ 'i386: 00000000-0000-0000-0000-000000000000', 'x86_64: 00000000-0000-0000-0000-000000000001', 'arm64: 00000000-0000-0000-0000-000000000002' ] platform: macosx install-name: '/usr/lib/libc++.dylib' current-version: 1281 exports: - - archs: [ i386, x86_64 ] + - archs: [ i386, x86_64, arm64 ] re-exports: [ '/usr/lib/libc++abi.dylib' ] ... diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd --- a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd +++ b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd @@ -1,10 +1,10 @@ --- !tapi-tbd-v3 -archs: [ i386, x86_64 ] -uuids: [ 'i386: 00000000-0000-0000-0000-000000000000', 'x86_64: 00000000-0000-0000-0000-000000000001' ] +archs: [ i386, x86_64, arm64 ] +uuids: [ 'i386: 00000000-0000-0000-0000-000000000000', 'x86_64: 00000000-0000-0000-0000-000000000001', 'arm64: 00000000-0000-0000-0000-000000000002' ] platform: macosx install-name: '/usr/lib/libc++abi.dylib' current-version: 1281 exports: - - archs: [ i386, x86_64 ] + - archs: [ i386, x86_64, arm64 ] symbols: [ ___gxx_personality_v0 ] ... 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,55 @@ +# 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_PAGE{21,OFF12} +# CHECK: adrp x1, #0 +# CHECK-NEXT: add x1, x1, #[[#%u, CSTRING_OFF]] +## Test ARM64_RELOC_BRANCH26 +# CHECK: bl 0x[[#%x, FUNC_ADDR]] <_write> +## Test ARM64_ADDEND +## Test ARM64_RELOC_UNSIGNED +## Test ARM64_RELOC_SUBTRACTOR +## Test ARM64_RELOC_POINTER_TO_GOT +## Test ARM64_RELOC_GOT_PAGE{21,OFF12} +## Test ARM64_RELOC_TLVP_LOAD_PAGE{21,OFF12} + +#--- 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_64.s rename from lld/test/MachO/relocations.s rename to lld/test/MachO/relocations-x86_64.s