13bc88eb3SJez Ng //===- ARM64Common.cpp ----------------------------------------------------===//
23bc88eb3SJez Ng //
33bc88eb3SJez Ng // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43bc88eb3SJez Ng // See https://llvm.org/LICENSE.txt for license information.
53bc88eb3SJez Ng // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63bc88eb3SJez Ng //
73bc88eb3SJez Ng //===----------------------------------------------------------------------===//
83bc88eb3SJez Ng
93bc88eb3SJez Ng #include "Arch/ARM64Common.h"
103bc88eb3SJez Ng
113bc88eb3SJez Ng #include "lld/Common/ErrorHandler.h"
123bc88eb3SJez Ng #include "llvm/Support/Endian.h"
133bc88eb3SJez Ng
143bc88eb3SJez Ng using namespace llvm::MachO;
153bc88eb3SJez Ng using namespace llvm::support::endian;
163bc88eb3SJez Ng using namespace lld;
173bc88eb3SJez Ng using namespace lld::macho;
183bc88eb3SJez Ng
getEmbeddedAddend(MemoryBufferRef mb,uint64_t offset,const relocation_info rel) const193bc88eb3SJez Ng int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
203bc88eb3SJez Ng const relocation_info rel) const {
213bc88eb3SJez Ng if (rel.r_type != ARM64_RELOC_UNSIGNED &&
223bc88eb3SJez Ng rel.r_type != ARM64_RELOC_SUBTRACTOR) {
233bc88eb3SJez Ng // All other reloc types should use the ADDEND relocation to store their
243bc88eb3SJez Ng // addends.
253bc88eb3SJez Ng // TODO(gkm): extract embedded addend just so we can assert that it is 0
263bc88eb3SJez Ng return 0;
273bc88eb3SJez Ng }
283bc88eb3SJez Ng
293bc88eb3SJez Ng const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
303bc88eb3SJez Ng const uint8_t *loc = buf + offset + rel.r_address;
313bc88eb3SJez Ng switch (rel.r_length) {
323bc88eb3SJez Ng case 2:
333bc88eb3SJez Ng return static_cast<int32_t>(read32le(loc));
343bc88eb3SJez Ng case 3:
353bc88eb3SJez Ng return read64le(loc);
363bc88eb3SJez Ng default:
373bc88eb3SJez Ng llvm_unreachable("invalid r_length");
383bc88eb3SJez Ng }
393bc88eb3SJez Ng }
403bc88eb3SJez Ng
writeValue(uint8_t * loc,const Reloc & r,uint64_t value)41a552fb2aSJez Ng static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
423bc88eb3SJez Ng switch (r.length) {
433bc88eb3SJez Ng case 2:
44a552fb2aSJez Ng checkInt(loc, r, value, 32);
453bc88eb3SJez Ng write32le(loc, value);
463bc88eb3SJez Ng break;
473bc88eb3SJez Ng case 3:
483bc88eb3SJez Ng write64le(loc, value);
493bc88eb3SJez Ng break;
503bc88eb3SJez Ng default:
513bc88eb3SJez Ng llvm_unreachable("invalid r_length");
523bc88eb3SJez Ng }
533bc88eb3SJez Ng }
543bc88eb3SJez Ng
55a552fb2aSJez Ng // For instruction relocations (load, store, add), the base
56a552fb2aSJez Ng // instruction is pre-populated in the text section. A pre-populated
57a552fb2aSJez Ng // instruction has opcode & register-operand bits set, with immediate
58a552fb2aSJez Ng // operands zeroed. We read it from text, OR-in the immediate
59a552fb2aSJez Ng // operands, then write-back the completed instruction.
relocateOne(uint8_t * loc,const Reloc & r,uint64_t value,uint64_t pc) const60a552fb2aSJez Ng void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
61a552fb2aSJez Ng uint64_t pc) const {
62a552fb2aSJez Ng auto loc32 = reinterpret_cast<uint32_t *>(loc);
63a552fb2aSJez Ng uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
64a552fb2aSJez Ng switch (r.type) {
65a552fb2aSJez Ng case ARM64_RELOC_BRANCH26:
66a552fb2aSJez Ng encodeBranch26(loc32, r, base, value - pc);
67a552fb2aSJez Ng break;
68a552fb2aSJez Ng case ARM64_RELOC_SUBTRACTOR:
69a552fb2aSJez Ng case ARM64_RELOC_UNSIGNED:
70a552fb2aSJez Ng writeValue(loc, r, value);
71a552fb2aSJez Ng break;
72a552fb2aSJez Ng case ARM64_RELOC_POINTER_TO_GOT:
73a552fb2aSJez Ng if (r.pcrel)
74a552fb2aSJez Ng value -= pc;
75a552fb2aSJez Ng writeValue(loc, r, value);
76a552fb2aSJez Ng break;
77a552fb2aSJez Ng case ARM64_RELOC_PAGE21:
78a552fb2aSJez Ng case ARM64_RELOC_GOT_LOAD_PAGE21:
79a552fb2aSJez Ng case ARM64_RELOC_TLVP_LOAD_PAGE21:
80a552fb2aSJez Ng assert(r.pcrel);
81a552fb2aSJez Ng encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
82a552fb2aSJez Ng break;
83a552fb2aSJez Ng case ARM64_RELOC_PAGEOFF12:
84a552fb2aSJez Ng case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
85a552fb2aSJez Ng case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
86a552fb2aSJez Ng assert(!r.pcrel);
871b65d209SDaniel Bertalan encodePageOff12(loc32, r, base, value);
88a552fb2aSJez Ng break;
89a552fb2aSJez Ng default:
90a552fb2aSJez Ng llvm_unreachable("unexpected relocation type");
91a552fb2aSJez Ng }
92a552fb2aSJez Ng }
93a552fb2aSJez Ng
relaxGotLoad(uint8_t * loc,uint8_t type) const943bc88eb3SJez Ng void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
953bc88eb3SJez Ng // The instruction format comments below are quoted from
963bc88eb3SJez Ng // Arm® Architecture Reference Manual
973bc88eb3SJez Ng // Armv8, for Armv8-A architecture profile
983bc88eb3SJez Ng // ARM DDI 0487G.a (ID011921)
993bc88eb3SJez Ng uint32_t instruction = read32le(loc);
1003bc88eb3SJez Ng // C6.2.132 LDR (immediate)
1011acda12dSJez Ng // This matches both the 64- and 32-bit variants:
1021acda12dSJez Ng // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
1031acda12dSJez Ng if ((instruction & 0xbfc00000) != 0xb9400000)
1043bc88eb3SJez Ng error(getRelocAttrs(type).name + " reloc requires LDR instruction");
1053bc88eb3SJez Ng assert(((instruction >> 10) & 0xfff) == 0 &&
1063bc88eb3SJez Ng "non-zero embedded LDR immediate");
1073bc88eb3SJez Ng // C6.2.4 ADD (immediate)
1083bc88eb3SJez Ng // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
1093bc88eb3SJez Ng instruction = ((instruction & 0x001fffff) | 0x91000000);
1103bc88eb3SJez Ng write32le(loc, instruction);
1113bc88eb3SJez Ng }
1126c641d0dSKaining Zhong
handleDtraceReloc(const Symbol * sym,const Reloc & r,uint8_t * loc) const1136c641d0dSKaining Zhong void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
1146c641d0dSKaining Zhong uint8_t *loc) const {
1156c641d0dSKaining Zhong assert(r.type == ARM64_RELOC_BRANCH26);
1166c641d0dSKaining Zhong
1176c641d0dSKaining Zhong if (config->outputType == MH_OBJECT)
1186c641d0dSKaining Zhong return;
1196c641d0dSKaining Zhong
120*8d85c96eSFangrui Song if (sym->getName().starts_with("___dtrace_probe")) {
1216c641d0dSKaining Zhong // change call site to a NOP
1226c641d0dSKaining Zhong write32le(loc, 0xD503201F);
123*8d85c96eSFangrui Song } else if (sym->getName().starts_with("___dtrace_isenabled")) {
1246c641d0dSKaining Zhong // change call site to 'MOVZ X0,0'
1256c641d0dSKaining Zhong write32le(loc, 0xD2800000);
1266c641d0dSKaining Zhong } else {
1276c641d0dSKaining Zhong error("Unrecognized dtrace symbol prefix: " + toString(*sym));
1286c641d0dSKaining Zhong }
1296c641d0dSKaining Zhong }
1301b65d209SDaniel Bertalan
reportUnalignedLdrStr(Twine loc,uint64_t va,int align,const Symbol * sym)1311b65d209SDaniel Bertalan static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
1321b65d209SDaniel Bertalan const Symbol *sym) {
1331b65d209SDaniel Bertalan std::string symbolHint;
1341b65d209SDaniel Bertalan if (sym)
1351b65d209SDaniel Bertalan symbolHint = " (" + toString(*sym) + ")";
1361b65d209SDaniel Bertalan error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
1371b65d209SDaniel Bertalan llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
1381b65d209SDaniel Bertalan "-byte aligned");
1391b65d209SDaniel Bertalan }
1401b65d209SDaniel Bertalan
reportUnalignedLdrStr(void * loc,const lld::macho::Reloc & r,uint64_t va,int align)1411b65d209SDaniel Bertalan void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
1421b65d209SDaniel Bertalan uint64_t va, int align) {
1431b65d209SDaniel Bertalan uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
1441b65d209SDaniel Bertalan const InputSection *isec = offsetToInputSection(&off);
1451b65d209SDaniel Bertalan std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
1461b65d209SDaniel Bertalan ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
1471b65d209SDaniel Bertalan }
1481b65d209SDaniel Bertalan
reportUnalignedLdrStr(void * loc,lld::macho::SymbolDiagnostic d,uint64_t va,int align)1491b65d209SDaniel Bertalan void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
1501b65d209SDaniel Bertalan uint64_t va, int align) {
1511b65d209SDaniel Bertalan ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
1521b65d209SDaniel Bertalan }
153