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>(sym)) 27 return d->isec(); 28 return nullptr; 29 } else { 30 return cast<InputSection *>(referent); 31 } 32 } 33 34 StringRef Reloc::getReferentString() const { 35 if (auto *isec = referent.dyn_cast<InputSection *>()) { 36 const auto *cisec = dyn_cast<CStringInputSection>(isec); 37 assert(cisec && "referent must be a CStringInputSection"); 38 return cisec->getStringRefAtOffset(addend); 39 } 40 41 auto *sym = dyn_cast<Defined>(cast<Symbol *>(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>(symIsec)) 48 return s->getStringRefAtOffset(symOffset); 49 50 if (isa<ConcatInputSection>(symIsec)) { 51 auto strData = symIsec->data.slice(symOffset); 52 const char *pszData = reinterpret_cast<const char *>(strData.data()); 53 return StringRef(pszData, strnlen(pszData, 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(r.type); 62 bool valid = true; 63 auto message = [&](const Twine &diagnostic) { 64 valid = false; 65 return (isec->getLocation(r.offset) + ": " + relocAttrs.name + 66 " relocation " + diagnostic) 67 .str(); 68 }; 69 70 if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv()) 71 error(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>(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); 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(locStr + ": relocation " + target->getRelocAttrs(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(d.reason + " is out of range: " + v + " is not in [" + Twine(min) + 140 ", " + Twine(max) + "]" + hint); 141 } 142 143 const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0}; 144