| 1 | //===- Relocations.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_RELOCATIONS_H |
| 10 | #define LLD_MACHO_RELOCATIONS_H |
| 11 | |
| 12 | #include "llvm/ADT/BitmaskEnum.h" |
| 13 | #include "llvm/ADT/PointerUnion.h" |
| 14 | #include "llvm/BinaryFormat/MachO.h" |
| 15 | #include "llvm/Support/Endian.h" |
| 16 | |
| 17 | #include <cstddef> |
| 18 | #include <cstdint> |
| 19 | |
| 20 | namespace lld::macho { |
| 21 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); |
| 22 | |
| 23 | class Symbol; |
| 24 | class InputSection; |
| 25 | |
| 26 | enum class RelocAttrBits { |
| 27 | _0 = 0, // invalid |
| 28 | PCREL = 1 << 0, // Value is PC-relative offset |
| 29 | ABSOLUTE = 1 << 1, // Value is an absolute address or fixed offset |
| 30 | BYTE4 = 1 << 2, // 4 byte datum |
| 31 | BYTE8 = 1 << 3, // 8 byte datum |
| 32 | EXTERN = 1 << 4, // Can have an external symbol |
| 33 | LOCAL = 1 << 5, // Can have a local symbol |
| 34 | ADDEND = 1 << 6, // *_ADDEND paired prefix reloc |
| 35 | SUBTRAHEND = 1 << 7, // *_SUBTRACTOR paired prefix reloc |
| 36 | BRANCH = 1 << 8, // Value is branch target |
| 37 | GOT = 1 << 9, // References a symbol in the Global Offset Table |
| 38 | TLV = 1 << 10, // References a thread-local symbol |
| 39 | LOAD = 1 << 11, // Relaxable indirect load |
| 40 | POINTER = 1 << 12, // Non-relaxable indirect load (pointer is taken) |
| 41 | UNSIGNED = 1 << 13, // *_UNSIGNED relocs |
| 42 | LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ (1 << 14) - 1), |
| 43 | }; |
| 44 | // Note: SUBTRACTOR always pairs with UNSIGNED (a delta between two symbols). |
| 45 | |
| 46 | struct RelocAttrs { |
| 47 | llvm::StringRef name; |
| 48 | RelocAttrBits bits; |
| 49 | bool hasAttr(RelocAttrBits b) const { return (bits & b) == b; } |
| 50 | }; |
| 51 | |
| 52 | struct Reloc { |
| 53 | uint8_t type = llvm::MachO::GENERIC_RELOC_INVALID; |
| 54 | bool pcrel = false; |
| 55 | uint8_t length = 0; |
| 56 | // The offset from the start of the subsection that this relocation belongs |
| 57 | // to. |
| 58 | uint32_t offset = 0; |
| 59 | // Adding this offset to the address of the referent symbol or subsection |
| 60 | // gives the destination that this relocation refers to. |
| 61 | int64_t addend = 0; |
| 62 | llvm::PointerUnion<Symbol *, InputSection *> referent = nullptr; |
| 63 | |
| 64 | Reloc() = default; |
| 65 | |
| 66 | Reloc(uint8_t type, bool pcrel, uint8_t length, uint32_t offset, |
| 67 | int64_t addend, llvm::PointerUnion<Symbol *, InputSection *> referent) |
| 68 | : type(type), pcrel(pcrel), length(length), offset(offset), |
| 69 | addend(addend), referent(referent) {} |
| 70 | |
| 71 | InputSection *getReferentInputSection() const; |
| 72 | |
| 73 | // Must point to an offset within a CStringInputSection or a |
| 74 | // ConcatInputSection. |
| 75 | llvm::StringRef getReferentString() const; |
| 76 | }; |
| 77 | |
| 78 | bool validateSymbolRelocation(const Symbol *, const InputSection *, |
| 79 | const Reloc &); |
| 80 | |
| 81 | /* |
| 82 | * v: The value the relocation is attempting to encode |
| 83 | * bits: The number of bits actually available to encode this relocation |
| 84 | */ |
| 85 | void reportRangeError(void *loc, const Reloc &, const llvm::Twine &v, |
| 86 | uint8_t bits, int64_t min, uint64_t max); |
| 87 | |
| 88 | struct SymbolDiagnostic { |
| 89 | const Symbol *symbol; |
| 90 | llvm::StringRef reason; |
| 91 | }; |
| 92 | |
| 93 | void reportRangeError(void *loc, SymbolDiagnostic, const llvm::Twine &v, |
| 94 | uint8_t bits, int64_t min, uint64_t max); |
| 95 | |
| 96 | template <typename Diagnostic> |
| 97 | inline void checkInt(void *loc, Diagnostic d, int64_t v, int bits) { |
| 98 | if (v != llvm::SignExtend64(X: v, B: bits)) |
| 99 | reportRangeError(loc, d, llvm::Twine(v), bits, llvm::minIntN(N: bits), |
| 100 | llvm::maxIntN(N: bits)); |
| 101 | } |
| 102 | |
| 103 | template <typename Diagnostic> |
| 104 | inline void checkUInt(void *loc, Diagnostic d, uint64_t v, int bits) { |
| 105 | if ((v >> bits) != 0) |
| 106 | reportRangeError(loc, d, llvm::Twine(v), bits, 0, llvm::maxUIntN(N: bits)); |
| 107 | } |
| 108 | |
| 109 | inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) { |
| 110 | switch (length) { |
| 111 | case 2: |
| 112 | llvm::support::endian::write32le(P: loc, V: addr); |
| 113 | break; |
| 114 | case 3: |
| 115 | llvm::support::endian::write64le(P: loc, V: addr); |
| 116 | break; |
| 117 | default: |
| 118 | llvm_unreachable("invalid r_length" ); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | InputSection *offsetToInputSection(uint64_t *); |
| 123 | |
| 124 | extern const RelocAttrs invalidRelocAttrs; |
| 125 | |
| 126 | } // namespace lld::Macho |
| 127 | |
| 128 | #endif |
| 129 | |