| 1 | //===- ARM64Common.h --------------------------------------------*- C++ -*-===// |
| 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 | #ifndef LLD_MACHO_ARCH_ARM64COMMON_H |
| 10 | #define LLD_MACHO_ARCH_ARM64COMMON_H |
| 11 | |
| 12 | #include "InputFiles.h" |
| 13 | #include "Symbols.h" |
| 14 | #include "SyntheticSections.h" |
| 15 | #include "Target.h" |
| 16 | |
| 17 | #include "llvm/BinaryFormat/MachO.h" |
| 18 | |
| 19 | namespace lld::macho { |
| 20 | |
| 21 | struct ARM64Common : TargetInfo { |
| 22 | template <class LP> ARM64Common(LP lp) : TargetInfo(lp) {} |
| 23 | |
| 24 | int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, |
| 25 | const llvm::MachO::relocation_info) const override; |
| 26 | void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, |
| 27 | uint64_t pc) const override; |
| 28 | |
| 29 | void relaxGotLoad(uint8_t *loc, uint8_t type) const override; |
| 30 | uint64_t getPageSize() const override { return 16 * 1024; } |
| 31 | |
| 32 | void handleDtraceReloc(const Symbol *sym, const Reloc &r, |
| 33 | uint8_t *loc) const override; |
| 34 | }; |
| 35 | |
| 36 | inline uint64_t bitField(uint64_t value, int right, int width, int left) { |
| 37 | return ((value >> right) & ((1 << width) - 1)) << left; |
| 38 | } |
| 39 | |
| 40 | // 25 0 |
| 41 | // +-----------+---------------------------------------------------+ |
| 42 | // | | imm26 | |
| 43 | // +-----------+---------------------------------------------------+ |
| 44 | |
| 45 | inline void encodeBranch26(uint32_t *loc, const Reloc &r, uint32_t base, |
| 46 | uint64_t va) { |
| 47 | checkInt(loc, d: r, v: va, bits: 28); |
| 48 | // Since branch destinations are 4-byte aligned, the 2 least- |
| 49 | // significant bits are 0. They are right shifted off the end. |
| 50 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 2, width: 26, left: 0)); |
| 51 | } |
| 52 | |
| 53 | inline void encodeBranch26(uint32_t *loc, SymbolDiagnostic d, uint32_t base, |
| 54 | uint64_t va) { |
| 55 | checkInt(loc, d, v: va, bits: 28); |
| 56 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 2, width: 26, left: 0)); |
| 57 | } |
| 58 | |
| 59 | // 30 29 23 5 |
| 60 | // +-+---+---------+-------------------------------------+---------+ |
| 61 | // | |ilo| | immhi | | |
| 62 | // +-+---+---------+-------------------------------------+---------+ |
| 63 | |
| 64 | inline void encodePage21(uint32_t *loc, const Reloc &r, uint32_t base, |
| 65 | uint64_t va) { |
| 66 | checkInt(loc, d: r, v: va, bits: 35); |
| 67 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 12, width: 2, left: 29) | |
| 68 | bitField(value: va, right: 14, width: 19, left: 5)); |
| 69 | } |
| 70 | |
| 71 | inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base, |
| 72 | uint64_t va) { |
| 73 | checkInt(loc, d, v: va, bits: 35); |
| 74 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 12, width: 2, left: 29) | |
| 75 | bitField(value: va, right: 14, width: 19, left: 5)); |
| 76 | } |
| 77 | |
| 78 | void reportUnalignedLdrStr(void *loc, const Reloc &, uint64_t va, int align); |
| 79 | void reportUnalignedLdrStr(void *loc, SymbolDiagnostic, uint64_t va, int align); |
| 80 | |
| 81 | // 21 10 |
| 82 | // +-------------------+-----------------------+-------------------+ |
| 83 | // | | imm12 | | |
| 84 | // +-------------------+-----------------------+-------------------+ |
| 85 | |
| 86 | template <typename Target> |
| 87 | inline void encodePageOff12(uint32_t *loc, Target t, uint32_t base, |
| 88 | uint64_t va) { |
| 89 | int scale = 0; |
| 90 | if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store |
| 91 | scale = base >> 30; |
| 92 | if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant |
| 93 | scale = 4; |
| 94 | } |
| 95 | const int size = 1 << scale; |
| 96 | if ((va & (size - 1)) != 0) |
| 97 | reportUnalignedLdrStr(loc, t, va, size); |
| 98 | |
| 99 | // TODO(gkm): extract embedded addend and warn if != 0 |
| 100 | // uint64_t addend = ((base & 0x003FFC00) >> 10); |
| 101 | llvm::support::endian::write32le(P: loc, |
| 102 | V: base | bitField(value: va, right: scale, width: 12 - scale, left: 10)); |
| 103 | } |
| 104 | |
| 105 | inline uint64_t pageBits(uint64_t address) { |
| 106 | const uint64_t pageMask = ~0xfffull; |
| 107 | return address & pageMask; |
| 108 | } |
| 109 | |
| 110 | inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3], |
| 111 | const macho::Symbol &sym, uint64_t pointerVA) { |
| 112 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
| 113 | constexpr size_t stubCodeSize = 3 * sizeof(uint32_t); |
| 114 | SymbolDiagnostic d = {.symbol: &sym, .reason: "stub" }; |
| 115 | uint64_t pcPageBits = |
| 116 | pageBits(address: in.stubs->addr + sym.stubsIndex * stubCodeSize); |
| 117 | encodePage21(loc: &buf32[0], d, base: stubCode[0], va: pageBits(address: pointerVA) - pcPageBits); |
| 118 | encodePageOff12(loc: &buf32[1], t: d, base: stubCode[1], va: pointerVA); |
| 119 | buf32[2] = stubCode[2]; |
| 120 | } |
| 121 | |
| 122 | template <class LP> |
| 123 | inline void (uint8_t *buf8, |
| 124 | const uint32_t [6]) { |
| 125 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
| 126 | auto pcPageBits = [](int i) { |
| 127 | return pageBits(address: in.stubHelper->addr + i * sizeof(uint32_t)); |
| 128 | }; |
| 129 | uint64_t loaderVA = in.imageLoaderCache->getVA(); |
| 130 | SymbolDiagnostic d = {.symbol: nullptr, .reason: "stub header helper" }; |
| 131 | encodePage21(&buf32[0], d, stubHelperHeaderCode[0], |
| 132 | pageBits(address: loaderVA) - pcPageBits(0)); |
| 133 | encodePageOff12(loc: &buf32[1], t: d, base: stubHelperHeaderCode[1], va: loaderVA); |
| 134 | buf32[2] = stubHelperHeaderCode[2]; |
| 135 | uint64_t binderVA = |
| 136 | in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize; |
| 137 | encodePage21(&buf32[3], d, stubHelperHeaderCode[3], |
| 138 | pageBits(address: binderVA) - pcPageBits(3)); |
| 139 | encodePageOff12(loc: &buf32[4], t: d, base: stubHelperHeaderCode[4], va: binderVA); |
| 140 | buf32[5] = stubHelperHeaderCode[5]; |
| 141 | } |
| 142 | |
| 143 | inline void writeStubHelperEntry(uint8_t *buf8, |
| 144 | const uint32_t stubHelperEntryCode[3], |
| 145 | const Symbol &sym, uint64_t entryVA) { |
| 146 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
| 147 | auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); }; |
| 148 | uint64_t = in.stubHelper->addr; |
| 149 | buf32[0] = stubHelperEntryCode[0]; |
| 150 | encodeBranch26(loc: &buf32[1], d: {.symbol: &sym, .reason: "stub helper" }, base: stubHelperEntryCode[1], |
| 151 | va: stubHelperHeaderVA - pcVA(1)); |
| 152 | buf32[2] = sym.lazyBindOffset; |
| 153 | } |
| 154 | |
| 155 | template <class LP> |
| 156 | inline void writeObjCMsgSendFastStub(uint8_t *buf, |
| 157 | const uint32_t objcStubsFastCode[8], |
| 158 | Symbol *sym, uint64_t stubsAddr, |
| 159 | uint64_t stubOffset, uint64_t selrefVA, |
| 160 | uint64_t gotAddr, uint64_t msgSendIndex) { |
| 161 | SymbolDiagnostic d = {.symbol: sym, .reason: sym->getName()}; |
| 162 | auto *buf32 = reinterpret_cast<uint32_t *>(buf); |
| 163 | |
| 164 | auto pcPageBits = [stubsAddr, stubOffset](int i) { |
| 165 | return pageBits(address: stubsAddr + stubOffset + i * sizeof(uint32_t)); |
| 166 | }; |
| 167 | |
| 168 | encodePage21(&buf32[0], d, objcStubsFastCode[0], |
| 169 | pageBits(address: selrefVA) - pcPageBits(0)); |
| 170 | encodePageOff12(loc: &buf32[1], t: d, base: objcStubsFastCode[1], va: selrefVA); |
| 171 | uint64_t gotOffset = msgSendIndex * LP::wordSize; |
| 172 | encodePage21(&buf32[2], d, objcStubsFastCode[2], |
| 173 | pageBits(address: gotAddr + gotOffset) - pcPageBits(2)); |
| 174 | encodePageOff12(loc: &buf32[3], t: d, base: objcStubsFastCode[3], va: gotAddr + gotOffset); |
| 175 | buf32[4] = objcStubsFastCode[4]; |
| 176 | buf32[5] = objcStubsFastCode[5]; |
| 177 | buf32[6] = objcStubsFastCode[6]; |
| 178 | buf32[7] = objcStubsFastCode[7]; |
| 179 | } |
| 180 | |
| 181 | template <class LP> |
| 182 | inline void |
| 183 | writeObjCMsgSendSmallStub(uint8_t *buf, const uint32_t objcStubsSmallCode[3], |
| 184 | Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset, |
| 185 | uint64_t selrefVA, uint64_t msgSendAddr, |
| 186 | uint64_t msgSendIndex) { |
| 187 | SymbolDiagnostic d = {.symbol: sym, .reason: sym->getName()}; |
| 188 | auto *buf32 = reinterpret_cast<uint32_t *>(buf); |
| 189 | |
| 190 | auto pcPageBits = [stubsAddr, stubOffset](int i) { |
| 191 | return pageBits(address: stubsAddr + stubOffset + i * sizeof(uint32_t)); |
| 192 | }; |
| 193 | |
| 194 | encodePage21(&buf32[0], d, objcStubsSmallCode[0], |
| 195 | pageBits(address: selrefVA) - pcPageBits(0)); |
| 196 | encodePageOff12(loc: &buf32[1], t: d, base: objcStubsSmallCode[1], va: selrefVA); |
| 197 | uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize; |
| 198 | uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t); |
| 199 | encodeBranch26(loc: &buf32[2], d: {.symbol: nullptr, .reason: "objc_msgSend stub" }, |
| 200 | base: objcStubsSmallCode[2], va: msgSendStubVA - pcVA); |
| 201 | } |
| 202 | |
| 203 | } // namespace lld::macho |
| 204 | |
| 205 | #endif |
| 206 | |