| 1 | //===- Relocations.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 "Relocations.h" |
| 10 | #include "ConcatOutputSection.h" |
| 11 | #include "Symbols.h" |
| 12 | #include "SyntheticSections.h" |
| 13 | #include "Target.h" |
| 14 | |
| 15 | #include "lld/Common/ErrorHandler.h" |
| 16 | |
| 17 | using namespace llvm; |
| 18 | using namespace lld; |
| 19 | using namespace lld::macho; |
| 20 | |
| 21 | static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24, |
| 22 | "Try to minimize Reloc's size; we create many instances" ); |
| 23 | |
| 24 | InputSection *Reloc::getReferentInputSection() const { |
| 25 | if (const auto *sym = referent.dyn_cast<Symbol *>()) { |
| 26 | if (const auto *d = dyn_cast<Defined>(Val: sym)) |
| 27 | return d->isec(); |
| 28 | return nullptr; |
| 29 | } else { |
| 30 | return cast<InputSection *>(Val: referent); |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | StringRef Reloc::getReferentString() const { |
| 35 | if (auto *isec = dyn_cast<InputSection *>(Val: referent)) { |
| 36 | const auto *cisec = dyn_cast<CStringInputSection>(Val: isec); |
| 37 | assert(cisec && "referent must be a CStringInputSection" ); |
| 38 | return cisec->getStringRefAtOffset(off: addend); |
| 39 | } |
| 40 | |
| 41 | auto *sym = dyn_cast<Defined>(Val: cast<Symbol *>(Val: referent)); |
| 42 | assert(sym && "referent must be a Defined symbol" ); |
| 43 | |
| 44 | auto *symIsec = sym->isec(); |
| 45 | auto symOffset = sym->value + addend; |
| 46 | |
| 47 | if (auto *s = dyn_cast_or_null<CStringInputSection>(Val: symIsec)) |
| 48 | return s->getStringRefAtOffset(off: symOffset); |
| 49 | |
| 50 | if (isa<ConcatInputSection>(Val: symIsec)) { |
| 51 | auto strData = symIsec->data.slice(N: symOffset); |
| 52 | const char *pszData = reinterpret_cast<const char *>(strData.data()); |
| 53 | return StringRef(pszData, strnlen(string: pszData, maxlen: strData.size())); |
| 54 | } |
| 55 | |
| 56 | llvm_unreachable("unknown reference section in getReferentString" ); |
| 57 | } |
| 58 | |
| 59 | bool macho::validateSymbolRelocation(const Symbol *sym, |
| 60 | const InputSection *isec, const Reloc &r) { |
| 61 | const RelocAttrs &relocAttrs = target->getRelocAttrs(type: r.type); |
| 62 | bool valid = true; |
| 63 | auto message = [&](const Twine &diagnostic) { |
| 64 | valid = false; |
| 65 | return (isec->getLocation(off: r.offset) + ": " + relocAttrs.name + |
| 66 | " relocation " + diagnostic) |
| 67 | .str(); |
| 68 | }; |
| 69 | |
| 70 | if (relocAttrs.hasAttr(b: RelocAttrBits::TLV) != sym->isTlv()) |
| 71 | error(msg: message(Twine("requires that symbol " ) + sym->getName() + " " + |
| 72 | (sym->isTlv() ? "not " : "" ) + "be thread-local" )); |
| 73 | |
| 74 | return valid; |
| 75 | } |
| 76 | |
| 77 | // Given an offset in the output buffer, figure out which ConcatInputSection (if |
| 78 | // any) maps to it. At the same time, update the offset such that it is relative |
| 79 | // to the InputSection rather than to the output buffer. |
| 80 | // |
| 81 | // Obtaining the InputSection allows us to have better error diagnostics. |
| 82 | // However, many of our relocation-handling methods do not take the InputSection |
| 83 | // as a parameter. Since we are already passing the buffer offsets to our Target |
| 84 | // methods, this function allows us to emit better errors without threading an |
| 85 | // additional InputSection argument through the call stack. |
| 86 | // |
| 87 | // This is implemented as a slow linear search through OutputSegments, |
| 88 | // OutputSections, and finally the InputSections themselves. However, this |
| 89 | // function should be called only on error paths, so some overhead is fine. |
| 90 | InputSection *macho::offsetToInputSection(uint64_t *off) { |
| 91 | for (OutputSegment *seg : outputSegments) { |
| 92 | if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize) |
| 93 | continue; |
| 94 | |
| 95 | const std::vector<OutputSection *> §ions = seg->getSections(); |
| 96 | size_t osecIdx = 0; |
| 97 | for (; osecIdx < sections.size(); ++osecIdx) |
| 98 | if (*off < sections[osecIdx]->fileOff) |
| 99 | break; |
| 100 | assert(osecIdx > 0); |
| 101 | // We should be only calling this function on offsets that belong to |
| 102 | // ConcatOutputSections. |
| 103 | auto *osec = cast<ConcatOutputSection>(Val: sections[osecIdx - 1]); |
| 104 | *off -= osec->fileOff; |
| 105 | |
| 106 | size_t isecIdx = 0; |
| 107 | for (; isecIdx < osec->inputs.size(); ++isecIdx) { |
| 108 | const ConcatInputSection *isec = osec->inputs[isecIdx]; |
| 109 | if (*off < isec->outSecOff) |
| 110 | break; |
| 111 | } |
| 112 | assert(isecIdx > 0); |
| 113 | ConcatInputSection *isec = osec->inputs[isecIdx - 1]; |
| 114 | *off -= isec->outSecOff; |
| 115 | return isec; |
| 116 | } |
| 117 | return nullptr; |
| 118 | } |
| 119 | |
| 120 | void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v, |
| 121 | uint8_t bits, int64_t min, uint64_t max) { |
| 122 | std::string hint; |
| 123 | uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart; |
| 124 | const InputSection *isec = offsetToInputSection(off: &off); |
| 125 | std::string locStr = isec ? isec->getLocation(off) : "(invalid location)" ; |
| 126 | if (auto *sym = r.referent.dyn_cast<Symbol *>()) |
| 127 | hint = "; references " + toString(*sym); |
| 128 | error(msg: locStr + ": relocation " + target->getRelocAttrs(type: r.type).name + |
| 129 | " is out of range: " + v + " is not in [" + Twine(min) + ", " + |
| 130 | Twine(max) + "]" + hint); |
| 131 | } |
| 132 | |
| 133 | void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v, |
| 134 | uint8_t bits, int64_t min, uint64_t max) { |
| 135 | // FIXME: should we use `loc` somehow to provide a better error message? |
| 136 | std::string hint; |
| 137 | if (d.symbol) |
| 138 | hint = "; references " + toString(*d.symbol); |
| 139 | error(msg: d.reason + " is out of range: " + v + " is not in [" + Twine(min) + |
| 140 | ", " + Twine(max) + "]" + hint); |
| 141 | } |
| 142 | |
| 143 | const RelocAttrs macho::invalidRelocAttrs{.name: "INVALID" , .bits: RelocAttrBits::_0}; |
| 144 | |