xref: /freebsd/contrib/llvm-project/lld/MachO/Arch/ARM64Common.cpp (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 //===- ARM64Common.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Arch/ARM64Common.h"
10 
11 #include "lld/Common/ErrorHandler.h"
12 #include "llvm/Support/Endian.h"
13 
14 using namespace llvm::MachO;
15 using namespace llvm::support::endian;
16 using namespace lld;
17 using namespace lld::macho;
18 
19 int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
20                                        const relocation_info rel) const {
21   if (rel.r_type != ARM64_RELOC_UNSIGNED &&
22       rel.r_type != ARM64_RELOC_SUBTRACTOR) {
23     // All other reloc types should use the ADDEND relocation to store their
24     // addends.
25     // TODO(gkm): extract embedded addend just so we can assert that it is 0
26     return 0;
27   }
28 
29   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
30   const uint8_t *loc = buf + offset + rel.r_address;
31   switch (rel.r_length) {
32   case 2:
33     return static_cast<int32_t>(read32le(loc));
34   case 3:
35     return read64le(loc);
36   default:
37     llvm_unreachable("invalid r_length");
38   }
39 }
40 
41 static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
42   switch (r.length) {
43   case 2:
44     checkInt(loc, r, value, 32);
45     write32le(loc, value);
46     break;
47   case 3:
48     write64le(loc, value);
49     break;
50   default:
51     llvm_unreachable("invalid r_length");
52   }
53 }
54 
55 // For instruction relocations (load, store, add), the base
56 // instruction is pre-populated in the text section. A pre-populated
57 // instruction has opcode & register-operand bits set, with immediate
58 // operands zeroed. We read it from text, OR-in the immediate
59 // operands, then write-back the completed instruction.
60 void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
61                               uint64_t pc) const {
62   auto loc32 = reinterpret_cast<uint32_t *>(loc);
63   uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
64   switch (r.type) {
65   case ARM64_RELOC_BRANCH26:
66     encodeBranch26(loc32, r, base, value - pc);
67     break;
68   case ARM64_RELOC_SUBTRACTOR:
69   case ARM64_RELOC_UNSIGNED:
70     writeValue(loc, r, value);
71     break;
72   case ARM64_RELOC_POINTER_TO_GOT:
73     if (r.pcrel)
74       value -= pc;
75     writeValue(loc, r, value);
76     break;
77   case ARM64_RELOC_PAGE21:
78   case ARM64_RELOC_GOT_LOAD_PAGE21:
79   case ARM64_RELOC_TLVP_LOAD_PAGE21:
80     assert(r.pcrel);
81     encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
82     break;
83   case ARM64_RELOC_PAGEOFF12:
84   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
85   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
86     assert(!r.pcrel);
87     encodePageOff12(loc32, r, base, value);
88     break;
89   default:
90     llvm_unreachable("unexpected relocation type");
91   }
92 }
93 
94 void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
95   // The instruction format comments below are quoted from
96   // Arm® Architecture Reference Manual
97   // Armv8, for Armv8-A architecture profile
98   // ARM DDI 0487G.a (ID011921)
99   uint32_t instruction = read32le(loc);
100   // C6.2.132 LDR (immediate)
101   // This matches both the 64- and 32-bit variants:
102   // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
103   if ((instruction & 0xbfc00000) != 0xb9400000)
104     error(getRelocAttrs(type).name + " reloc requires LDR instruction");
105   assert(((instruction >> 10) & 0xfff) == 0 &&
106          "non-zero embedded LDR immediate");
107   // C6.2.4 ADD (immediate)
108   // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
109   instruction = ((instruction & 0x001fffff) | 0x91000000);
110   write32le(loc, instruction);
111 }
112 
113 void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
114                                     uint8_t *loc) const {
115   assert(r.type == ARM64_RELOC_BRANCH26);
116 
117   if (config->outputType == MH_OBJECT)
118     return;
119 
120   if (sym->getName().starts_with("___dtrace_probe")) {
121     // change call site to a NOP
122     write32le(loc, 0xD503201F);
123   } else if (sym->getName().starts_with("___dtrace_isenabled")) {
124     // change call site to 'MOVZ X0,0'
125     write32le(loc, 0xD2800000);
126   } else {
127     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
128   }
129 }
130 
131 static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
132                                   const Symbol *sym) {
133   std::string symbolHint;
134   if (sym)
135     symbolHint = " (" + toString(*sym) + ")";
136   error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
137         llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
138         "-byte aligned");
139 }
140 
141 void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
142                                   uint64_t va, int align) {
143   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
144   const InputSection *isec = offsetToInputSection(&off);
145   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
146   ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
147 }
148 
149 void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
150                                   uint64_t va, int align) {
151   ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
152 }
153