Path: blob/main/contrib/llvm-project/lld/MachO/Arch/ARM64.cpp
34889 views
//===- ARM64.cpp ----------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Arch/ARM64Common.h"9#include "InputFiles.h"10#include "Symbols.h"11#include "SyntheticSections.h"12#include "Target.h"1314#include "lld/Common/ErrorHandler.h"15#include "mach-o/compact_unwind_encoding.h"16#include "llvm/ADT/SmallVector.h"17#include "llvm/ADT/StringRef.h"18#include "llvm/BinaryFormat/MachO.h"19#include "llvm/Support/Endian.h"20#include "llvm/Support/LEB128.h"21#include "llvm/Support/MathExtras.h"2223using namespace llvm;24using namespace llvm::MachO;25using namespace llvm::support::endian;26using namespace lld;27using namespace lld::macho;2829namespace {3031struct ARM64 : ARM64Common {32ARM64();33void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override;34void writeStubHelperHeader(uint8_t *buf) const override;35void writeStubHelperEntry(uint8_t *buf, const Symbol &,36uint64_t entryAddr) const override;3738void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,39uint64_t &stubOffset, uint64_t selrefVA,40Symbol *objcMsgSend) const override;41void populateThunk(InputSection *thunk, Symbol *funcSym) override;42void applyOptimizationHints(uint8_t *, const ObjFile &) const override;43};4445} // namespace4647// Random notes on reloc types:48// ADDEND always pairs with BRANCH26, PAGE21, or PAGEOFF1249// POINTER_TO_GOT: ld64 supports a 4-byte pc-relative form as well as an 8-byte50// absolute version of this relocation. The semantics of the absolute relocation51// are weird -- it results in the value of the GOT slot being written, instead52// of the address. Let's not support it unless we find a real-world use case.53static constexpr std::array<RelocAttrs, 11> relocAttrsArray{{54#define B(x) RelocAttrBits::x55{"UNSIGNED",56B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},57{"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},58{"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},59{"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},60{"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},61{"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},62{"GOT_LOAD_PAGEOFF12",63B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},64{"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},65{"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},66{"TLVP_LOAD_PAGEOFF12",67B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},68{"ADDEND", B(ADDEND)},69#undef B70}};7172static constexpr uint32_t stubCode[] = {730x90000010, // 00: adrp x16, __la_symbol_ptr@page740xf9400210, // 04: ldr x16, [x16, __la_symbol_ptr@pageoff]750xd61f0200, // 08: br x1676};7778void ARM64::writeStub(uint8_t *buf8, const Symbol &sym,79uint64_t pointerVA) const {80::writeStub(buf8, stubCode, sym, pointerVA);81}8283static constexpr uint32_t stubHelperHeaderCode[] = {840x90000011, // 00: adrp x17, _dyld_private@page850x91000231, // 04: add x17, x17, _dyld_private@pageoff860xa9bf47f0, // 08: stp x16/x17, [sp, #-16]!870x90000010, // 0c: adrp x16, dyld_stub_binder@page880xf9400210, // 10: ldr x16, [x16, dyld_stub_binder@pageoff]890xd61f0200, // 14: br x1690};9192void ARM64::writeStubHelperHeader(uint8_t *buf8) const {93::writeStubHelperHeader<LP64>(buf8, stubHelperHeaderCode);94}9596static constexpr uint32_t stubHelperEntryCode[] = {970x18000050, // 00: ldr w16, l0980x14000000, // 04: b stubHelperHeader990x00000000, // 08: l0: .long 0100};101102void ARM64::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,103uint64_t entryVA) const {104::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA);105}106107static constexpr uint32_t objcStubsFastCode[] = {1080x90000001, // adrp x1, __objc_selrefs@page1090xf9400021, // ldr x1, [x1, @selector("foo")@pageoff]1100x90000010, // adrp x16, _got@page1110xf9400210, // ldr x16, [x16, _objc_msgSend@pageoff]1120xd61f0200, // br x161130xd4200020, // brk #0x11140xd4200020, // brk #0x11150xd4200020, // brk #0x1116};117118static constexpr uint32_t objcStubsSmallCode[] = {1190x90000001, // adrp x1, __objc_selrefs@page1200xf9400021, // ldr x1, [x1, @selector("foo")@pageoff]1210x14000000, // b _objc_msgSend122};123124void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,125uint64_t &stubOffset, uint64_t selrefVA,126Symbol *objcMsgSend) const {127uint64_t objcMsgSendAddr;128uint64_t objcStubSize;129uint64_t objcMsgSendIndex;130131if (config->objcStubsMode == ObjCStubsMode::fast) {132objcStubSize = target->objcStubsFastSize;133objcMsgSendAddr = in.got->addr;134objcMsgSendIndex = objcMsgSend->gotIndex;135::writeObjCMsgSendFastStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,136stubOffset, selrefVA, objcMsgSendAddr,137objcMsgSendIndex);138} else {139assert(config->objcStubsMode == ObjCStubsMode::small);140objcStubSize = target->objcStubsSmallSize;141if (auto *d = dyn_cast<Defined>(objcMsgSend)) {142objcMsgSendAddr = d->getVA();143objcMsgSendIndex = 0;144} else {145objcMsgSendAddr = in.stubs->addr;146objcMsgSendIndex = objcMsgSend->stubsIndex;147}148::writeObjCMsgSendSmallStub<LP64>(buf, objcStubsSmallCode, sym, stubsAddr,149stubOffset, selrefVA, objcMsgSendAddr,150objcMsgSendIndex);151}152stubOffset += objcStubSize;153}154155// A thunk is the relaxed variation of stubCode. We don't need the156// extra indirection through a lazy pointer because the target address157// is known at link time.158static constexpr uint32_t thunkCode[] = {1590x90000010, // 00: adrp x16, <thunk.ptr>@page1600x91000210, // 04: add x16, [x16,<thunk.ptr>@pageoff]1610xd61f0200, // 08: br x16162};163164void ARM64::populateThunk(InputSection *thunk, Symbol *funcSym) {165thunk->align = 4;166thunk->data = {reinterpret_cast<const uint8_t *>(thunkCode),167sizeof(thunkCode)};168thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGEOFF12,169/*pcrel=*/false, /*length=*/2,170/*offset=*/4, /*addend=*/0,171/*referent=*/funcSym);172thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGE21,173/*pcrel=*/true, /*length=*/2,174/*offset=*/0, /*addend=*/0,175/*referent=*/funcSym);176}177178ARM64::ARM64() : ARM64Common(LP64()) {179cpuType = CPU_TYPE_ARM64;180cpuSubtype = CPU_SUBTYPE_ARM64_ALL;181182stubSize = sizeof(stubCode);183thunkSize = sizeof(thunkCode);184185objcStubsFastSize = sizeof(objcStubsFastCode);186objcStubsFastAlignment = 32;187objcStubsSmallSize = sizeof(objcStubsSmallCode);188objcStubsSmallAlignment = 4;189190// Branch immediate is two's complement 26 bits, which is implicitly191// multiplied by 4 (since all functions are 4-aligned: The branch range192// is -4*(2**(26-1))..4*(2**(26-1) - 1).193backwardBranchRange = 128 * 1024 * 1024;194forwardBranchRange = backwardBranchRange - 4;195196modeDwarfEncoding = UNWIND_ARM64_MODE_DWARF;197subtractorRelocType = ARM64_RELOC_SUBTRACTOR;198unsignedRelocType = ARM64_RELOC_UNSIGNED;199200stubHelperHeaderSize = sizeof(stubHelperHeaderCode);201stubHelperEntrySize = sizeof(stubHelperEntryCode);202203relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};204}205206namespace {207struct Adrp {208uint32_t destRegister;209int64_t addend;210};211212struct Add {213uint8_t destRegister;214uint8_t srcRegister;215uint32_t addend;216};217218enum ExtendType { ZeroExtend = 1, Sign64 = 2, Sign32 = 3 };219220struct Ldr {221uint8_t destRegister;222uint8_t baseRegister;223uint8_t p2Size;224bool isFloat;225ExtendType extendType;226int64_t offset;227};228} // namespace229230static bool parseAdrp(uint32_t insn, Adrp &adrp) {231if ((insn & 0x9f000000) != 0x90000000)232return false;233adrp.destRegister = insn & 0x1f;234uint64_t immHi = (insn >> 5) & 0x7ffff;235uint64_t immLo = (insn >> 29) & 0x3;236adrp.addend = SignExtend64<21>(immLo | (immHi << 2)) * 4096;237return true;238}239240static bool parseAdd(uint32_t insn, Add &add) {241if ((insn & 0xffc00000) != 0x91000000)242return false;243add.destRegister = insn & 0x1f;244add.srcRegister = (insn >> 5) & 0x1f;245add.addend = (insn >> 10) & 0xfff;246return true;247}248249static bool parseLdr(uint32_t insn, Ldr &ldr) {250ldr.destRegister = insn & 0x1f;251ldr.baseRegister = (insn >> 5) & 0x1f;252uint8_t size = insn >> 30;253uint8_t opc = (insn >> 22) & 3;254255if ((insn & 0x3fc00000) == 0x39400000) {256// LDR (immediate), LDRB (immediate), LDRH (immediate)257ldr.p2Size = size;258ldr.extendType = ZeroExtend;259ldr.isFloat = false;260} else if ((insn & 0x3f800000) == 0x39800000) {261// LDRSB (immediate), LDRSH (immediate), LDRSW (immediate)262ldr.p2Size = size;263ldr.extendType = static_cast<ExtendType>(opc);264ldr.isFloat = false;265} else if ((insn & 0x3f400000) == 0x3d400000) {266// LDR (immediate, SIMD&FP)267ldr.extendType = ZeroExtend;268ldr.isFloat = true;269if (opc == 1)270ldr.p2Size = size;271else if (size == 0 && opc == 3)272ldr.p2Size = 4;273else274return false;275} else {276return false;277}278ldr.offset = ((insn >> 10) & 0xfff) << ldr.p2Size;279return true;280}281282static bool isValidAdrOffset(int32_t delta) { return isInt<21>(delta); }283284static void writeAdr(void *loc, uint32_t dest, int32_t delta) {285assert(isValidAdrOffset(delta));286uint32_t opcode = 0x10000000;287uint32_t immHi = (delta & 0x001ffffc) << 3;288uint32_t immLo = (delta & 0x00000003) << 29;289write32le(loc, opcode | immHi | immLo | dest);290}291292static void writeNop(void *loc) { write32le(loc, 0xd503201f); }293294static bool isLiteralLdrEligible(const Ldr &ldr) {295return ldr.p2Size > 1 && isShiftedInt<19, 2>(ldr.offset);296}297298static void writeLiteralLdr(void *loc, const Ldr &ldr) {299assert(isLiteralLdrEligible(ldr));300uint32_t imm19 = (ldr.offset / 4 & maskTrailingOnes<uint32_t>(19)) << 5;301uint32_t opcode;302switch (ldr.p2Size) {303case 2:304if (ldr.isFloat)305opcode = 0x1c000000;306else307opcode = ldr.extendType == Sign64 ? 0x98000000 : 0x18000000;308break;309case 3:310opcode = ldr.isFloat ? 0x5c000000 : 0x58000000;311break;312case 4:313opcode = 0x9c000000;314break;315default:316llvm_unreachable("Invalid literal ldr size");317}318write32le(loc, opcode | imm19 | ldr.destRegister);319}320321static bool isImmediateLdrEligible(const Ldr &ldr) {322// Note: We deviate from ld64's behavior, which converts to immediate loads323// only if ldr.offset < 4096, even though the offset is divided by the load's324// size in the 12-bit immediate operand. Only the unsigned offset variant is325// supported.326327uint32_t size = 1 << ldr.p2Size;328return ldr.offset >= 0 && (ldr.offset % size) == 0 &&329isUInt<12>(ldr.offset >> ldr.p2Size);330}331332static void writeImmediateLdr(void *loc, const Ldr &ldr) {333assert(isImmediateLdrEligible(ldr));334uint32_t opcode = 0x39000000;335if (ldr.isFloat) {336opcode |= 0x04000000;337assert(ldr.extendType == ZeroExtend);338}339opcode |= ldr.destRegister;340opcode |= ldr.baseRegister << 5;341uint8_t size, opc;342if (ldr.p2Size == 4) {343size = 0;344opc = 3;345} else {346opc = ldr.extendType;347size = ldr.p2Size;348}349uint32_t immBits = ldr.offset >> ldr.p2Size;350write32le(loc, opcode | (immBits << 10) | (opc << 22) | (size << 30));351}352353// Transforms a pair of adrp+add instructions into an adr instruction if the354// target is within the +/- 1 MiB range allowed by the adr's 21 bit signed355// immediate offset.356//357// adrp xN, _foo@PAGE358// add xM, xN, _foo@PAGEOFF359// ->360// adr xM, _foo361// nop362static void applyAdrpAdd(uint8_t *buf, const ConcatInputSection *isec,363uint64_t offset1, uint64_t offset2) {364uint32_t ins1 = read32le(buf + offset1);365uint32_t ins2 = read32le(buf + offset2);366Adrp adrp;367Add add;368if (!parseAdrp(ins1, adrp) || !parseAdd(ins2, add))369return;370if (adrp.destRegister != add.srcRegister)371return;372373uint64_t addr1 = isec->getVA() + offset1;374uint64_t referent = pageBits(addr1) + adrp.addend + add.addend;375int64_t delta = referent - addr1;376if (!isValidAdrOffset(delta))377return;378379writeAdr(buf + offset1, add.destRegister, delta);380writeNop(buf + offset2);381}382383// Transforms two adrp instructions into a single adrp if their referent384// addresses are located on the same 4096 byte page.385//386// adrp xN, _foo@PAGE387// adrp xN, _bar@PAGE388// ->389// adrp xN, _foo@PAGE390// nop391static void applyAdrpAdrp(uint8_t *buf, const ConcatInputSection *isec,392uint64_t offset1, uint64_t offset2) {393uint32_t ins1 = read32le(buf + offset1);394uint32_t ins2 = read32le(buf + offset2);395Adrp adrp1, adrp2;396if (!parseAdrp(ins1, adrp1) || !parseAdrp(ins2, adrp2))397return;398if (adrp1.destRegister != adrp2.destRegister)399return;400401uint64_t page1 = pageBits(offset1 + isec->getVA()) + adrp1.addend;402uint64_t page2 = pageBits(offset2 + isec->getVA()) + adrp2.addend;403if (page1 != page2)404return;405406writeNop(buf + offset2);407}408409// Transforms a pair of adrp+ldr (immediate) instructions into an ldr (literal)410// load from a PC-relative address if it is 4-byte aligned and within +/- 1 MiB,411// as ldr can encode a signed 19-bit offset that gets multiplied by 4.412//413// adrp xN, _foo@PAGE414// ldr xM, [xN, _foo@PAGEOFF]415// ->416// nop417// ldr xM, _foo418static void applyAdrpLdr(uint8_t *buf, const ConcatInputSection *isec,419uint64_t offset1, uint64_t offset2) {420uint32_t ins1 = read32le(buf + offset1);421uint32_t ins2 = read32le(buf + offset2);422Adrp adrp;423Ldr ldr;424if (!parseAdrp(ins1, adrp) || !parseLdr(ins2, ldr))425return;426if (adrp.destRegister != ldr.baseRegister)427return;428429uint64_t addr1 = isec->getVA() + offset1;430uint64_t addr2 = isec->getVA() + offset2;431uint64_t referent = pageBits(addr1) + adrp.addend + ldr.offset;432ldr.offset = referent - addr2;433if (!isLiteralLdrEligible(ldr))434return;435436writeNop(buf + offset1);437writeLiteralLdr(buf + offset2, ldr);438}439440// GOT loads are emitted by the compiler as a pair of adrp and ldr instructions,441// but they may be changed to adrp+add by relaxGotLoad(). This hint performs442// the AdrpLdr or AdrpAdd transformation depending on whether it was relaxed.443static void applyAdrpLdrGot(uint8_t *buf, const ConcatInputSection *isec,444uint64_t offset1, uint64_t offset2) {445uint32_t ins2 = read32le(buf + offset2);446Add add;447Ldr ldr;448if (parseAdd(ins2, add))449applyAdrpAdd(buf, isec, offset1, offset2);450else if (parseLdr(ins2, ldr))451applyAdrpLdr(buf, isec, offset1, offset2);452}453454// Optimizes an adrp+add+ldr sequence used for loading from a local symbol's455// address by loading directly if it's close enough, or to an adrp(p)+ldr456// sequence if it's not.457//458// adrp x0, _foo@PAGE459// add x1, x0, _foo@PAGEOFF460// ldr x2, [x1, #off]461static void applyAdrpAddLdr(uint8_t *buf, const ConcatInputSection *isec,462uint64_t offset1, uint64_t offset2,463uint64_t offset3) {464uint32_t ins1 = read32le(buf + offset1);465Adrp adrp;466if (!parseAdrp(ins1, adrp))467return;468uint32_t ins2 = read32le(buf + offset2);469Add add;470if (!parseAdd(ins2, add))471return;472uint32_t ins3 = read32le(buf + offset3);473Ldr ldr;474if (!parseLdr(ins3, ldr))475return;476if (adrp.destRegister != add.srcRegister)477return;478if (add.destRegister != ldr.baseRegister)479return;480481// Load from the target address directly.482// nop483// nop484// ldr x2, [_foo + #off]485uint64_t addr1 = isec->getVA() + offset1;486uint64_t addr3 = isec->getVA() + offset3;487uint64_t referent = pageBits(addr1) + adrp.addend + add.addend;488Ldr literalLdr = ldr;489literalLdr.offset += referent - addr3;490if (isLiteralLdrEligible(literalLdr)) {491writeNop(buf + offset1);492writeNop(buf + offset2);493writeLiteralLdr(buf + offset3, literalLdr);494return;495}496497// Load the target address into a register and load from there indirectly.498// adr x1, _foo499// nop500// ldr x2, [x1, #off]501int64_t adrOffset = referent - addr1;502if (isValidAdrOffset(adrOffset)) {503writeAdr(buf + offset1, ldr.baseRegister, adrOffset);504// Note: ld64 moves the offset into the adr instruction for AdrpAddLdr, but505// not for AdrpLdrGotLdr. Its effect is the same either way.506writeNop(buf + offset2);507return;508}509510// Move the target's page offset into the ldr's immediate offset.511// adrp x0, _foo@PAGE512// nop513// ldr x2, [x0, _foo@PAGEOFF + #off]514Ldr immediateLdr = ldr;515immediateLdr.baseRegister = adrp.destRegister;516immediateLdr.offset += add.addend;517if (isImmediateLdrEligible(immediateLdr)) {518writeNop(buf + offset2);519writeImmediateLdr(buf + offset3, immediateLdr);520return;521}522}523524// Relaxes a GOT-indirect load.525// If the referenced symbol is external and its GOT entry is within +/- 1 MiB,526// the GOT entry can be loaded with a single literal ldr instruction.527// If the referenced symbol is local and thus has been relaxed to adrp+add+ldr,528// we perform the AdrpAddLdr transformation.529static void applyAdrpLdrGotLdr(uint8_t *buf, const ConcatInputSection *isec,530uint64_t offset1, uint64_t offset2,531uint64_t offset3) {532uint32_t ins2 = read32le(buf + offset2);533Add add;534Ldr ldr2;535536if (parseAdd(ins2, add)) {537applyAdrpAddLdr(buf, isec, offset1, offset2, offset3);538} else if (parseLdr(ins2, ldr2)) {539// adrp x1, _foo@GOTPAGE540// ldr x2, [x1, _foo@GOTPAGEOFF]541// ldr x3, [x2, #off]542543uint32_t ins1 = read32le(buf + offset1);544Adrp adrp;545if (!parseAdrp(ins1, adrp))546return;547uint32_t ins3 = read32le(buf + offset3);548Ldr ldr3;549if (!parseLdr(ins3, ldr3))550return;551552if (ldr2.baseRegister != adrp.destRegister)553return;554if (ldr3.baseRegister != ldr2.destRegister)555return;556// Loads from the GOT must be pointer sized.557if (ldr2.p2Size != 3 || ldr2.isFloat)558return;559560uint64_t addr1 = isec->getVA() + offset1;561uint64_t addr2 = isec->getVA() + offset2;562uint64_t referent = pageBits(addr1) + adrp.addend + ldr2.offset;563// Load the GOT entry's address directly.564// nop565// ldr x2, _foo@GOTPAGE + _foo@GOTPAGEOFF566// ldr x3, [x2, #off]567Ldr literalLdr = ldr2;568literalLdr.offset = referent - addr2;569if (isLiteralLdrEligible(literalLdr)) {570writeNop(buf + offset1);571writeLiteralLdr(buf + offset2, literalLdr);572}573}574}575576static uint64_t readValue(const uint8_t *&ptr, const uint8_t *end) {577unsigned int n = 0;578uint64_t value = decodeULEB128(ptr, &n, end);579ptr += n;580return value;581}582583template <typename Callback>584static void forEachHint(ArrayRef<uint8_t> data, Callback callback) {585std::array<uint64_t, 3> args;586587for (const uint8_t *p = data.begin(), *end = data.end(); p < end;) {588uint64_t type = readValue(p, end);589if (type == 0)590break;591592uint64_t argCount = readValue(p, end);593// All known LOH types as of 2022-09 have 3 or fewer arguments; skip others.594if (argCount > 3) {595for (unsigned i = 0; i < argCount; ++i)596readValue(p, end);597continue;598}599600for (unsigned i = 0; i < argCount; ++i)601args[i] = readValue(p, end);602callback(type, ArrayRef<uint64_t>(args.data(), argCount));603}604}605606// On RISC architectures like arm64, materializing a memory address generally607// takes multiple instructions. If the referenced symbol is located close enough608// in memory, fewer instructions are needed.609//610// Linker optimization hints record where addresses are computed. After611// addresses have been assigned, if possible, we change them to a shorter612// sequence of instructions. The size of the binary is not modified; the613// eliminated instructions are replaced with NOPs. This still leads to faster614// code as the CPU can skip over NOPs quickly.615//616// LOHs are specified by the LC_LINKER_OPTIMIZATION_HINTS load command, which617// points to a sequence of ULEB128-encoded numbers. Each entry specifies a618// transformation kind, and 2 or 3 addresses where the instructions are located.619void ARM64::applyOptimizationHints(uint8_t *outBuf, const ObjFile &obj) const {620ArrayRef<uint8_t> data = obj.getOptimizationHints();621if (data.empty())622return;623624const ConcatInputSection *section = nullptr;625uint64_t sectionAddr = 0;626uint8_t *buf = nullptr;627628auto findSection = [&](uint64_t addr) {629if (section && addr >= sectionAddr &&630addr < sectionAddr + section->getSize())631return true;632633if (obj.sections.empty())634return false;635auto secIt = std::prev(llvm::upper_bound(636obj.sections, addr,637[](uint64_t off, const Section *sec) { return off < sec->addr; }));638const Section *sec = *secIt;639640if (sec->subsections.empty())641return false;642auto subsecIt = std::prev(llvm::upper_bound(643sec->subsections, addr - sec->addr,644[](uint64_t off, Subsection subsec) { return off < subsec.offset; }));645const Subsection &subsec = *subsecIt;646const ConcatInputSection *isec =647dyn_cast_or_null<ConcatInputSection>(subsec.isec);648if (!isec || isec->shouldOmitFromOutput())649return false;650651section = isec;652sectionAddr = subsec.offset + sec->addr;653buf = outBuf + section->outSecOff + section->parent->fileOff;654return true;655};656657auto isValidOffset = [&](uint64_t offset) {658if (offset < sectionAddr || offset >= sectionAddr + section->getSize()) {659error(toString(&obj) +660": linker optimization hint spans multiple sections");661return false;662}663return true;664};665666bool hasAdrpAdrp = false;667forEachHint(data, [&](uint64_t kind, ArrayRef<uint64_t> args) {668if (kind == LOH_ARM64_ADRP_ADRP) {669hasAdrpAdrp = true;670return;671}672673if (!findSection(args[0]))674return;675switch (kind) {676case LOH_ARM64_ADRP_ADD:677if (isValidOffset(args[1]))678applyAdrpAdd(buf, section, args[0] - sectionAddr,679args[1] - sectionAddr);680break;681case LOH_ARM64_ADRP_LDR:682if (isValidOffset(args[1]))683applyAdrpLdr(buf, section, args[0] - sectionAddr,684args[1] - sectionAddr);685break;686case LOH_ARM64_ADRP_LDR_GOT:687if (isValidOffset(args[1]))688applyAdrpLdrGot(buf, section, args[0] - sectionAddr,689args[1] - sectionAddr);690break;691case LOH_ARM64_ADRP_ADD_LDR:692if (isValidOffset(args[1]) && isValidOffset(args[2]))693applyAdrpAddLdr(buf, section, args[0] - sectionAddr,694args[1] - sectionAddr, args[2] - sectionAddr);695break;696case LOH_ARM64_ADRP_LDR_GOT_LDR:697if (isValidOffset(args[1]) && isValidOffset(args[2]))698applyAdrpLdrGotLdr(buf, section, args[0] - sectionAddr,699args[1] - sectionAddr, args[2] - sectionAddr);700break;701case LOH_ARM64_ADRP_ADD_STR:702case LOH_ARM64_ADRP_LDR_GOT_STR:703// TODO: Implement these704break;705}706});707708if (!hasAdrpAdrp)709return;710711// AdrpAdrp optimization hints are performed in a second pass because they712// might interfere with other transformations. For instance, consider the713// following input:714//715// adrp x0, _foo@PAGE716// add x1, x0, _foo@PAGEOFF717// adrp x0, _bar@PAGE718// add x2, x0, _bar@PAGEOFF719//720// If we perform the AdrpAdrp relaxation first, we get:721//722// adrp x0, _foo@PAGE723// add x1, x0, _foo@PAGEOFF724// nop725// add x2, x0, _bar@PAGEOFF726//727// If we then apply AdrpAdd to the first two instructions, the add will have a728// garbage value in x0:729//730// adr x1, _foo731// nop732// nop733// add x2, x0, _bar@PAGEOFF734forEachHint(data, [&](uint64_t kind, ArrayRef<uint64_t> args) {735if (kind != LOH_ARM64_ADRP_ADRP)736return;737if (!findSection(args[0]))738return;739if (isValidOffset(args[1]))740applyAdrpAdrp(buf, section, args[0] - sectionAddr, args[1] - sectionAddr);741});742}743744TargetInfo *macho::createARM64TargetInfo() {745static ARM64 t;746return &t;747}748749750