diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp --- a/lld/MachO/Arch/ARM64.cpp +++ b/lld/MachO/Arch/ARM64.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "Arch/ARM64Common.h" #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -25,22 +26,13 @@ namespace { -struct ARM64 : TargetInfo { +struct ARM64 : ARM64Common { ARM64(); - - int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, - 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 Symbol &) const override; void writeStubHelperHeader(uint8_t *buf) const override; void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, uint64_t entryAddr) const override; - - void relaxGotLoad(uint8_t *loc, uint8_t type) const override; const RelocAttrs &getRelocAttrs(uint8_t type) const override; - uint64_t getPageSize() const override { return 16 * 1024; } }; } // namespace @@ -77,140 +69,6 @@ return relocAttrsArray[type]; } -int64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, - const relocation_info rel) const { - if (rel.r_type != ARM64_RELOC_UNSIGNED && - rel.r_type != ARM64_RELOC_SUBTRACTOR) { - // All other reloc types should use the ADDEND relocation to store their - // addends. - // TODO(gkm): extract embedded addend just so we can assert that it is 0 - return 0; - } - - auto *buf = reinterpret_cast(mb.getBufferStart()); - const uint8_t *loc = buf + offset + rel.r_address; - switch (rel.r_length) { - case 2: - return static_cast(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; -} - -// 25 0 -// +-----------+---------------------------------------------------+ -// | | imm26 | -// +-----------+---------------------------------------------------+ - -inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) { - checkInt(r, va, 28); - // 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)); -} - -inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { - checkInt(d, va, 28); - return (base | bitField(va, 2, 26, 0)); -} - -// 30 29 23 5 -// +-+---+---------+-------------------------------------+---------+ -// | |ilo| | immhi | | -// +-+---+---------+-------------------------------------+---------+ - -inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) { - checkInt(r, va, 35); - return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); -} - -inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) { - checkInt(d, va, 35); - return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); -} - -// 21 10 -// +-------------------+-----------------------+-------------------+ -// | | imm12 | | -// +-------------------+-----------------------+-------------------+ - -inline uint64_t encodePageOff12(uint32_t base, uint64_t va) { - int scale = 0; - if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store - scale = base >> 30; - if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant - scale = 4; - } - - // 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, add), the base -// instruction is pre-populated in the text section. A pre-populated -// instruction has opcode & register-operand bits set, with immediate -// operands zeroed. We read it from text, OR-in the immediate -// operands, then write-back the completed instruction. - -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(r, base, value - pc); - break; - case ARM64_RELOC_SUBTRACTOR: - case ARM64_RELOC_UNSIGNED: - if (r.length == 2) - checkInt(r, value, 32); - break; - case ARM64_RELOC_POINTER_TO_GOT: - if (r.pcrel) - value -= pc; - checkInt(r, value, 32); - break; - case ARM64_RELOC_PAGE21: - case ARM64_RELOC_GOT_LOAD_PAGE21: - case ARM64_RELOC_TLVP_LOAD_PAGE21: { - assert(r.pcrel); - value = encodePage21(r, 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] @@ -218,15 +76,7 @@ }; void ARM64::writeStub(uint8_t *buf8, const 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 * LP64::wordSize; - buf32[0] = encodePage21({&sym, "stub"}, stubCode[0], - pageBits(lazyPointerVA) - pcPageBits); - buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA); - buf32[2] = stubCode[2]; + ::writeStub(buf8, sym); } static constexpr uint32_t stubHelperHeaderCode[] = { @@ -239,22 +89,7 @@ }; 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(); - SymbolDiagnostic d = {nullptr, "stub header helper"}; - buf32[0] = encodePage21(d, 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 * LP64::wordSize; - buf32[3] = encodePage21(d, stubHelperHeaderCode[3], - pageBits(binderVA) - pcPageBits(3)); - buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA); - buf32[5] = stubHelperHeaderCode[5]; + ::writeStubHelperHeader(buf8); } static constexpr uint32_t stubHelperEntryCode[] = { @@ -265,34 +100,10 @@ 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({&sym, "stub helper"}, stubHelperEntryCode[1], - stubHelperHeaderVA - pcVA(1)); - buf32[2] = sym.lazyBindOffset; -} - -void ARM64::relaxGotLoad(uint8_t *loc, uint8_t type) const { - // The instruction format comments below are quoted from - // ArmĀ® Architecture Reference Manual - // Armv8, for Armv8-A architecture profile - // ARM DDI 0487G.a (ID011921) - uint32_t instruction = read32le(loc); - // C6.2.132 LDR (immediate) - // LDR , [{, #}] - if ((instruction & 0xffc00000) != 0xf9400000) - error(getRelocAttrs(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); + ::writeStubHelperEntry(buf8, sym, entryVA); } -ARM64::ARM64() : TargetInfo(LP64()) { +ARM64::ARM64() : ARM64Common(LP64()) { cpuType = CPU_TYPE_ARM64; cpuSubtype = CPU_SUBTYPE_ARM64_ALL; diff --git a/lld/MachO/Arch/ARM64Common.h b/lld/MachO/Arch/ARM64Common.h new file mode 100644 --- /dev/null +++ b/lld/MachO/Arch/ARM64Common.h @@ -0,0 +1,141 @@ +//===- ARM64Common.h --------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_ARCH_ARM64COMMON_H +#define LLD_MACHO_ARCH_ARM64COMMON_H + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "llvm/BinaryFormat/MachO.h" + +namespace lld { +namespace macho { + +struct ARM64Common : TargetInfo { + template ARM64Common(LP lp) : TargetInfo(lp) {} + + int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, + const llvm::MachO::relocation_info) const override; + void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, + uint64_t pc) const override; + + void relaxGotLoad(uint8_t *loc, uint8_t type) const override; + uint64_t getPageSize() const override { return 16 * 1024; } +}; + +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(const Reloc &r, uint64_t base, uint64_t va) { + checkInt(r, va, 28); + // 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)); +} + +inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { + checkInt(d, va, 28); + return (base | bitField(va, 2, 26, 0)); +} + +// 30 29 23 5 +// +-+---+---------+-------------------------------------+---------+ +// | |ilo| | immhi | | +// +-+---+---------+-------------------------------------+---------+ + +inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) { + checkInt(r, va, 35); + return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +} + +inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) { + checkInt(d, va, 35); + return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +} + +// 21 10 +// +-------------------+-----------------------+-------------------+ +// | | imm12 | | +// +-------------------+-----------------------+-------------------+ + +inline uint64_t encodePageOff12(uint32_t base, uint64_t va) { + int scale = 0; + if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store + scale = base >> 30; + if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant + scale = 4; + } + + // 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; +} + +template +inline void writeStub(uint8_t *buf8, const macho::Symbol &sym) { + auto *buf32 = reinterpret_cast(buf8); + uint64_t pcPageBits = + pageBits(in.stubs->addr + sym.stubsIndex * sizeof(stubCode)); + uint64_t lazyPointerVA = + in.lazyPointers->addr + sym.stubsIndex * LP::wordSize; + buf32[0] = encodePage21({&sym, "stub"}, stubCode[0], + pageBits(lazyPointerVA) - pcPageBits); + buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA); + buf32[2] = stubCode[2]; +} + +template +inline void writeStubHelperHeader(uint8_t *buf8) { + auto *buf32 = reinterpret_cast(buf8); + auto pcPageBits = [](int i) { + return pageBits(in.stubHelper->addr + i * sizeof(uint32_t)); + }; + uint64_t loaderVA = in.imageLoaderCache->getVA(); + SymbolDiagnostic d = {nullptr, "stub header helper"}; + buf32[0] = encodePage21(d, 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 * LP::wordSize; + buf32[3] = encodePage21(d, stubHelperHeaderCode[3], + pageBits(binderVA) - pcPageBits(3)); + buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA); + buf32[5] = stubHelperHeaderCode[5]; +} + +template +void writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, + uint64_t entryVA) { + 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({&sym, "stub helper"}, stubHelperEntryCode[1], + stubHelperHeaderVA - pcVA(1)); + buf32[2] = sym.lazyBindOffset; +} + +} // namespace macho +} // namespace lld + +#endif diff --git a/lld/MachO/Arch/ARM64Common.cpp b/lld/MachO/Arch/ARM64Common.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/Arch/ARM64Common.cpp @@ -0,0 +1,110 @@ +//===- ARM64Common.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 "Arch/ARM64Common.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, + const relocation_info rel) const { + if (rel.r_type != ARM64_RELOC_UNSIGNED && + rel.r_type != ARM64_RELOC_SUBTRACTOR) { + // All other reloc types should use the ADDEND relocation to store their + // addends. + // TODO(gkm): extract embedded addend just so we can assert that it is 0 + return 0; + } + + const auto *buf = reinterpret_cast(mb.getBufferStart()); + const uint8_t *loc = buf + offset + rel.r_address; + switch (rel.r_length) { + case 2: + return static_cast(read32le(loc)); + case 3: + return read64le(loc); + default: + llvm_unreachable("invalid r_length"); + } +} + +// For instruction relocations (load, store, add), the base +// instruction is pre-populated in the text section. A pre-populated +// instruction has opcode & register-operand bits set, with immediate +// operands zeroed. We read it from text, OR-in the immediate +// operands, then write-back the completed instruction. + +void ARM64Common::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(r, base, value - pc); + break; + case ARM64_RELOC_SUBTRACTOR: + case ARM64_RELOC_UNSIGNED: + if (r.length == 2) + checkInt(r, value, 32); + break; + case ARM64_RELOC_POINTER_TO_GOT: + if (r.pcrel) + value -= pc; + checkInt(r, value, 32); + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGE21: { + assert(r.pcrel); + value = encodePage21(r, 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"); + } +} + +void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { + // The instruction format comments below are quoted from + // ArmĀ® Architecture Reference Manual + // Armv8, for Armv8-A architecture profile + // ARM DDI 0487G.a (ID011921) + uint32_t instruction = read32le(loc); + // C6.2.132 LDR (immediate) + // LDR , [{, #}] + if ((instruction & 0xffc00000) != 0xf9400000) + error(getRelocAttrs(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); +} diff --git a/lld/MachO/Arch/ARM64_32.cpp b/lld/MachO/Arch/ARM64_32.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/Arch/ARM64_32.cpp @@ -0,0 +1,116 @@ +//===- ARM64_32.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 "Arch/ARM64Common.h" +#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_32 : ARM64Common { + ARM64_32(); + void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const override; + const RelocAttrs &getRelocAttrs(uint8_t type) const override; +}; + +} // namespace + +// These are very similar to ARM64's relocation attributes, except that we don't +// have the BYTE8 flag set. +const RelocAttrs &ARM64_32::getRelocAttrs(uint8_t type) const { + static const std::array relocAttrsArray{{ +#define B(x) RelocAttrBits::x + {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(BYTE4)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, + {"GOT_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, + {"TLVP_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"ADDEND", B(ADDEND)}, +#undef B + }}; + assert(type < relocAttrsArray.size() && "invalid relocation type"); + if (type >= relocAttrsArray.size()) + return invalidRelocAttrs; + return relocAttrsArray[type]; +} + +// The stub code is fairly similar to ARM64's, except that we load pointers into +// 32-bit 'w' registers, instead of the 64-bit 'x' ones. + +static constexpr uint32_t stubCode[] = { + 0x90000010, // 00: adrp x16, __la_symbol_ptr@page + 0xb9400210, // 04: ldr w16, [x16, __la_symbol_ptr@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym) const { + ::writeStub(buf8, sym); +} + +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 + 0xb9400210, // 10: ldr w16, [x16, dyld_stub_binder@pageoff] + 0xd61f0200, // 14: br x16 +}; + +void ARM64_32::writeStubHelperHeader(uint8_t *buf8) const { + ::writeStubHelperHeader(buf8); +} + +static constexpr uint32_t stubHelperEntryCode[] = { + 0x18000050, // 00: ldr w16, l0 + 0x14000000, // 04: b stubHelperHeader + 0x00000000, // 08: l0: .long 0 +}; + +void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, + uint64_t entryVA) const { + ::writeStubHelperEntry(buf8, sym, entryVA); +} + +ARM64_32::ARM64_32() : ARM64Common(ILP32()) { + cpuType = CPU_TYPE_ARM64_32; + cpuSubtype = CPU_SUBTYPE_ARM64_V8; + + stubSize = sizeof(stubCode); + stubHelperHeaderSize = sizeof(stubHelperHeaderCode); + stubHelperEntrySize = sizeof(stubHelperEntryCode); +} + +TargetInfo *macho::createARM64_32TargetInfo() { + static ARM64_32 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 @@ -7,6 +7,8 @@ add_lld_library(lldMachO2 Arch/X86_64.cpp Arch/ARM64.cpp + Arch/ARM64Common.cpp + Arch/ARM64_32.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 @@ -604,6 +604,8 @@ return createX86_64TargetInfo(); case CPU_TYPE_ARM64: return createARM64TargetInfo(); + case CPU_TYPE_ARM64_32: + return createARM64_32TargetInfo(); default: fatal("missing or unsupported -arch " + archName); } diff --git a/lld/MachO/MachOStructs.h b/lld/MachO/MachOStructs.h --- a/lld/MachO/MachOStructs.h +++ b/lld/MachO/MachOStructs.h @@ -37,6 +37,13 @@ llvm::support::ulittle32_t n_value; }; +struct entry_point_command { + llvm::support::ulittle32_t cmd; + llvm::support::ulittle32_t cmdsize; + llvm::support::ulittle64_t entryoff; + llvm::support::ulittle64_t stacksize; +}; + } // namespace structs } // namespace lld diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -79,6 +79,7 @@ TargetInfo *createX86_64TargetInfo(); TargetInfo *createARM64TargetInfo(); +TargetInfo *createARM64_32TargetInfo(); struct LP64 { using mach_header = llvm::MachO::mach_header_64; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -234,10 +234,12 @@ }; class LCMain : public LoadCommand { - uint32_t getSize() const override { return sizeof(entry_point_command); } + uint32_t getSize() const override { + return sizeof(structs::entry_point_command); + } void writeTo(uint8_t *buf) const override { - auto *c = reinterpret_cast(buf); + auto *c = reinterpret_cast(buf); c->cmd = LC_MAIN; c->cmdsize = getSize(); diff --git a/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libSystem.tbd b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libSystem.tbd new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libSystem.tbd @@ -0,0 +1,14 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ armv7k-watchos, arm64_32-watchos ] +uuids: + - target: armv7k-watchos + value: 00000000-0000-0000-0000-000000000001 + - target: arm64_32-watchos + value: 00000000-0000-0000-0000-000000000002 +install-name: '/usr/lib/libSystem.dylib' +current-version: 1.0.0 +exports: + - targets: [ arm64_32-watchos, armv7k-watchos ] + symbols: [ dyld_stub_binder ] +... diff --git a/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++.tbd b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++.tbd new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++.tbd @@ -0,0 +1,14 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ armv7k-watchos, arm64_32-watchos ] +uuids: + - target: armv7k-watchos + value: 00000000-0000-0000-0000-000000000001 + - target: arm64_32-watchos + value: 00000000-0000-0000-0000-000000000002 +install-name: '/usr/lib/libc++.dylib' +current-version: 1.0.0 +reexported-libraries: + - targets: [ arm64_32-watchos, armv7k-watchos ] + libraries: [ '/usr/lib/libc++abi.dylib' ] +... diff --git a/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++abi.tbd b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++abi.tbd new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/WatchOS.sdk/usr/lib/libc++abi.tbd @@ -0,0 +1,14 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ armv7k-watchos, arm64_32-watchos ] +uuids: + - target: armv7k-watchos + value: 00000000-0000-0000-0000-000000000001 + - target: arm64_32-watchos + value: 00000000-0000-0000-0000-000000000002 +install-name: '/usr/lib/libc++abi.dylib' +current-version: 1.0.0 +exports: + - targets: [ arm64_32-watchos, armv7k-watchos ] + symbols: [ ___gxx_personality_v0 ] +... diff --git a/lld/test/MachO/arm64-32-stubs.s b/lld/test/MachO/arm64-32-stubs.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/arm64-32-stubs.s @@ -0,0 +1,60 @@ +# REQUIRES: aarch64 + +## FIXME: This test is very similar to arm64-stubs.s, but has been split into a +## separate file because llvm-objdump doesn't correctly symbolize arm64_32. In +## particular, the "literal pool symbol address" comments are missing. + +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-watchos %t/foo.s -o %t/foo.o +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-watchos %t/bar.s -o %t/bar.o +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-watchos %t/test.s -o %t/test.o +# RUN: %lld-watchos -dylib -install_name @executable_path/libfoo.dylib %t/foo.o -o %t/libfoo.dylib +# RUN: %lld-watchos -dylib -install_name @executable_path/libbar.dylib %t/bar.o -o %t/libbar.dylib +# RUN: %lld-watchos -lSystem %t/libfoo.dylib %t/libbar.dylib %t/test.o -o %t/test + +# RUN: llvm-objdump --macho -d --no-show-raw-insn --section="__TEXT,__stubs" --section="__TEXT,__stub_helper" %t/test | FileCheck %s + +# CHECK: _main: +# CHECK-NEXT: bl 0x[[#%x,FOO:]] ; symbol stub for: _foo +# CHECK-NEXT: bl 0x[[#%x,BAR:]] ; symbol stub for: _bar +# CHECK-NEXT: ret + +# CHECK-LABEL: Contents of (__TEXT,__stubs) section +# CHECK-NEXT: [[#BAR]]: adrp x16 +# CHECK-NEXT: ldr w16, [x16{{.*}}] +# CHECK-NEXT: br x16 +# CHECK-NEXT: [[#FOO]]: adrp x16 +# CHECK-NEXT: ldr w16, [x16{{.*}}] +# CHECK-NEXT: br x16 + +# CHECK-LABEL: Contents of (__TEXT,__stub_helper) section +# CHECK-NEXT: [[#%x,HELPER_HEADER:]]: adrp x17 +# CHECK-NEXT: add x17, x17 +# CHECK-NEXT: stp x16, x17, [sp, #-16]! +# CHECK-NEXT: adrp x16 +# CHECK-NEXT: ldr w16, [x16] +# CHECK-NEXT: br x16 +# CHECK-NEXT: ldr w16, 0x[[#%x,BAR_BIND_OFF_ADDR:]] +# CHECK-NEXT: b 0x[[#HELPER_HEADER]] +# CHECK-NEXT: [[#BAR_BIND_OFF_ADDR]]: udf #0 +# CHECK-NEXT: ldr w16, 0x[[#%x,FOO_BIND_OFF_ADDR:]] +# CHECK-NEXT: b 0x[[#HELPER_HEADER]] +# CHECK-NEXT: [[#FOO_BIND_OFF_ADDR]]: udf #11 + +#--- foo.s +.globl _foo +_foo: + +#--- bar.s +.globl _bar +_bar: + +#--- test.s +.text +.globl _main + +.p2align 2 +_main: + bl _foo + bl _bar + ret diff --git a/lld/test/MachO/header.s b/lld/test/MachO/header.s --- a/lld/test/MachO/header.s +++ b/lld/test/MachO/header.s @@ -1,19 +1,27 @@ # REQUIRES: x86, aarch64 # RUN: rm -rf %t && mkdir -p %t -# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/x86_64-test.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/x86-64-test.o # RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t/arm64-test.o -# RUN: %lld -arch x86_64 -platform_version macos 10.5.0 11.0 -o %t/x86-64-executable %t/x86_64-test.o +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-watchos %s -o %t/arm64-32-test.o +# RUN: %lld -arch x86_64 -platform_version macos 10.5.0 11.0 -o %t/x86-64-executable %t/x86-64-test.o # RUN: %lld -arch arm64 -o %t/arm64-executable %t/arm64-test.o -# RUN: %lld -arch x86_64 -dylib -o %t/x86-64-dylib %t/x86_64-test.o +# RUN: %lld-watchos -o %t/arm64-32-executable %t/arm64-32-test.o +# RUN: %lld -arch x86_64 -dylib -o %t/x86-64-dylib %t/x86-64-test.o # RUN: %lld -arch arm64 -dylib -o %t/arm64-dylib %t/arm64-test.o +# RUN: %lld-watchos -dylib -o %t/arm64-32-dylib %t/arm64-32-test.o -# RUN: llvm-objdump --macho --private-header %t/x86-64-executable | FileCheck %s -DCAPS=LIB64 -# RUN: llvm-objdump --macho --private-header %t/arm64-executable | FileCheck %s -DCAPS=0x00 -# RUN: llvm-objdump --macho --private-header %t/x86-64-dylib | FileCheck %s -DCAPS=0x00 -# RUN: llvm-objdump --macho --private-header %t/arm64-dylib | FileCheck %s -DCAPS=0x00 +# RUN: llvm-objdump --macho --private-header %t/x86-64-executable | FileCheck %s -DCPU=X86_64 -DCAPS=LIB64 +# RUN: llvm-objdump --macho --private-header %t/arm64-executable | FileCheck %s -DCPU=ARM64 -DCAPS=0x00 +# RUN: llvm-objdump --macho --private-header %t/arm64-32-executable | FileCheck %s --check-prefix=ARM64-32 +# RUN: llvm-objdump --macho --private-header %t/x86-64-dylib | FileCheck %s -DCPU=X86_64 -DCAPS=0x00 +# RUN: llvm-objdump --macho --private-header %t/arm64-dylib | FileCheck %s -DCPU=ARM64 -DCAPS=0x00 +# RUN: llvm-objdump --macho --private-header %t/arm64-32-dylib | FileCheck %s --check-prefix=ARM64-32 -# CHECK: magic cputype cpusubtype caps filetype {{.*}} flags -# CHECK-NEXT: MH_MAGIC_64 {{.*}} ALL [[CAPS]] {{.*}} NOUNDEFS {{.*}} TWOLEVEL +# CHECK: magic cputype cpusubtype caps filetype {{.*}} flags +# CHECK-NEXT: MH_MAGIC_64 [[CPU]] ALL [[CAPS]] {{.*}} NOUNDEFS {{.*}} TWOLEVEL + +# ARM64-32: magic cputype cpusubtype caps filetype {{.*}} flags +# ARM64-32-NEXT: MH_MAGIC ARM64_32 V8 0x00 {{.*}} NOUNDEFS {{.*}} TWOLEVEL .globl _main _main: diff --git a/lld/test/MachO/lit.local.cfg b/lld/test/MachO/lit.local.cfg --- a/lld/test/MachO/lit.local.cfg +++ b/lld/test/MachO/lit.local.cfg @@ -2,12 +2,20 @@ import os -# We specify the most commonly-used arch and platform version in our tests here -# Tests which need different settings can just append to this, as only the last -# value will be used. +# We specify the most commonly-used archs and platform versions in our tests +# here. Tests which need different settings can just append to this, as only +# the last value will be used. # # Note however that this does not apply to `-syslibroot`: each instance of that -# flag will append to the set of library roots. +# flag will append to the set of library roots. As such, we define a separate +# alias for each platform. + +config.substitutions.append(('%lld-watchos', + 'ld64.lld -fatal_warnings -arch arm64_32 -platform_version watchos 7.0 8.0 -syslibroot ' + + os.path.join(config.test_source_root, "MachO", "Inputs", "WatchOS.sdk"))) + +# Since most of our tests are written around x86_64, we give this platform the +# shortest substitution of "%lld". lld = ('ld64.lld -arch x86_64 -platform_version macos 10.0 11.0 -syslibroot ' + os.path.join(config.test_source_root, "MachO", "Inputs", "MacOSX.sdk")) config.substitutions.append(('%lld', lld + ' -fatal_warnings')) diff --git a/lld/test/MachO/segments.s b/lld/test/MachO/segments.s --- a/lld/test/MachO/segments.s +++ b/lld/test/MachO/segments.s @@ -1,19 +1,28 @@ -# REQUIRES: x86 -# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o -# RUN: %lld -o %t %t.o -# RUN: llvm-readobj --macho-segment %t > %t.out -# RUN: echo "Total file size" >> %t.out -# RUN: wc -c %t >> %t.out -# RUN: FileCheck %s < %t.out +# REQUIRES: x86, aarch64 +# RUN: rm -rf %t; mkdir -p %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/x86_64.o +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-watchos %s -o %t/arm64-32.o +# RUN: %lld -o %t/x86_64 %t/x86_64.o +# RUN: %lld-watchos -o %t/arm64_32 %t/arm64-32.o + +# RUN: llvm-readobj --macho-segment %t/x86_64 > %t/x86_64.out +# RUN: echo "Total file size" >> %t/x86_64.out +# RUN: wc -c %t/x86_64 >> %t/x86_64.out +# RUN: FileCheck %s -DSUFFIX=_64 -DPAGEZERO_SIZE=0x100000000 -DTEXT_ADDR=0x100000000 < %t/x86_64.out + +# RUN: llvm-readobj --macho-segment %t/arm64_32 > %t/arm64-32.out +# RUN: echo "Total file size" >> %t/arm64-32.out +# RUN: wc -c %t/arm64_32 >> %t/arm64-32.out +# RUN: FileCheck %s -DSUFFIX= -DPAGEZERO_SIZE=0x1000 -DTEXT_ADDR=0x4000 < %t/arm64-32.out ## These two segments must always be present at the start of an executable. # CHECK-NOT: Segment { # CHECK: Segment { -# CHECK-NEXT: Cmd: LC_SEGMENT_64 +# CHECK-NEXT: Cmd: LC_SEGMENT[[SUFFIX]]{{$}} # CHECK-NEXT: Name: __PAGEZERO -# CHECK-NEXT: Size: 72 +# CHECK-NEXT: Size: # CHECK-NEXT: vmaddr: 0x0 -# CHECK-NEXT: vmsize: 0x100000000 +# CHECK-NEXT: vmsize: [[PAGEZERO_SIZE]] # CHECK-NEXT: fileoff: 0 # CHECK-NEXT: filesize: 0 ## The kernel won't execute a binary with the wrong protections for __PAGEZERO. @@ -23,10 +32,10 @@ # CHECK-NEXT: flags: 0x0 # CHECK-NEXT: } # CHECK-NEXT: Segment { -# CHECK-NEXT: Cmd: LC_SEGMENT_64 +# CHECK-NEXT: Cmd: LC_SEGMENT[[SUFFIX]]{{$}} # CHECK-NEXT: Name: __TEXT -# CHECK-NEXT: Size: 152 -# CHECK-NEXT: vmaddr: 0x100000000 +# CHECK-NEXT: Size: +# CHECK-NEXT: vmaddr: [[TEXT_ADDR]] # CHECK-NEXT: vmsize: ## dyld3 assumes that the __TEXT segment starts from the file header # CHECK-NEXT: fileoff: 0 @@ -38,7 +47,7 @@ # CHECK-NEXT: } ## Check that we handle max-length names correctly. -# CHECK: Cmd: LC_SEGMENT_64 +# CHECK: Cmd: LC_SEGMENT[[SUFFIX]]{{$}} # CHECK-NEXT: Name: maxlen_16ch_name ## This segment must always be present at the end of an executable, and cover @@ -51,7 +60,7 @@ # CHECK-NEXT: filesize: [[#%u, LINKEDIT_SIZE:]] # CHECK-NEXT: maxprot: r-- # CHECK-NEXT: initprot: r-- -# CHECK-NOT: Cmd: LC_SEGMENT_64 +# CHECK-NOT: Cmd: LC_SEGMENT[[SUFFIX]]{{$}} # CHECK-LABEL: Total file size # CHECK-NEXT: [[#%u, LINKEDIT_OFF + LINKEDIT_SIZE]] @@ -59,7 +68,6 @@ .text .global _main _main: - mov $0, %rax ret .section maxlen_16ch_name,foo