|  | //===- Relocations.cpp ----------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Relocations.h" | 
|  | #include "ConcatOutputSection.h" | 
|  | #include "Symbols.h" | 
|  | #include "SyntheticSections.h" | 
|  | #include "Target.h" | 
|  |  | 
|  | #include "lld/Common/ErrorHandler.h" | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace lld; | 
|  | using namespace lld::macho; | 
|  |  | 
|  | static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24, | 
|  | "Try to minimize Reloc's size; we create many instances"); | 
|  |  | 
|  | InputSection *Reloc::getReferentInputSection() const { | 
|  | if (const auto *sym = referent.dyn_cast<Symbol *>()) { | 
|  | if (const auto *d = dyn_cast<Defined>(sym)) | 
|  | return d->isec(); | 
|  | return nullptr; | 
|  | } else { | 
|  | return cast<InputSection *>(referent); | 
|  | } | 
|  | } | 
|  |  | 
|  | StringRef Reloc::getReferentString() const { | 
|  | if (auto *isec = dyn_cast<InputSection *>(referent)) { | 
|  | const auto *cisec = dyn_cast<CStringInputSection>(isec); | 
|  | assert(cisec && "referent must be a CStringInputSection"); | 
|  | return cisec->getStringRefAtOffset(addend); | 
|  | } | 
|  |  | 
|  | auto *sym = dyn_cast<Defined>(cast<Symbol *>(referent)); | 
|  | assert(sym && "referent must be a Defined symbol"); | 
|  |  | 
|  | auto *symIsec = sym->isec(); | 
|  | auto symOffset = sym->value + addend; | 
|  |  | 
|  | if (auto *s = dyn_cast_or_null<CStringInputSection>(symIsec)) | 
|  | return s->getStringRefAtOffset(symOffset); | 
|  |  | 
|  | if (isa<ConcatInputSection>(symIsec)) { | 
|  | auto strData = symIsec->data.slice(symOffset); | 
|  | const char *pszData = reinterpret_cast<const char *>(strData.data()); | 
|  | return StringRef(pszData, strnlen(pszData, strData.size())); | 
|  | } | 
|  |  | 
|  | llvm_unreachable("unknown reference section in getReferentString"); | 
|  | } | 
|  |  | 
|  | bool macho::validateSymbolRelocation(const Symbol *sym, | 
|  | const InputSection *isec, const Reloc &r) { | 
|  | const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type); | 
|  | bool valid = true; | 
|  | auto message = [&](const Twine &diagnostic) { | 
|  | valid = false; | 
|  | return (isec->getLocation(r.offset) + ": " + relocAttrs.name + | 
|  | " relocation " + diagnostic) | 
|  | .str(); | 
|  | }; | 
|  |  | 
|  | if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv()) | 
|  | error(message(Twine("requires that symbol ") + sym->getName() + " " + | 
|  | (sym->isTlv() ? "not " : "") + "be thread-local")); | 
|  |  | 
|  | return valid; | 
|  | } | 
|  |  | 
|  | // Given an offset in the output buffer, figure out which ConcatInputSection (if | 
|  | // any) maps to it. At the same time, update the offset such that it is relative | 
|  | // to the InputSection rather than to the output buffer. | 
|  | // | 
|  | // Obtaining the InputSection allows us to have better error diagnostics. | 
|  | // However, many of our relocation-handling methods do not take the InputSection | 
|  | // as a parameter. Since we are already passing the buffer offsets to our Target | 
|  | // methods, this function allows us to emit better errors without threading an | 
|  | // additional InputSection argument through the call stack. | 
|  | // | 
|  | // This is implemented as a slow linear search through OutputSegments, | 
|  | // OutputSections, and finally the InputSections themselves. However, this | 
|  | // function should be called only on error paths, so some overhead is fine. | 
|  | InputSection *macho::offsetToInputSection(uint64_t *off) { | 
|  | for (OutputSegment *seg : outputSegments) { | 
|  | if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize) | 
|  | continue; | 
|  |  | 
|  | const std::vector<OutputSection *> §ions = seg->getSections(); | 
|  | size_t osecIdx = 0; | 
|  | for (; osecIdx < sections.size(); ++osecIdx) | 
|  | if (*off < sections[osecIdx]->fileOff) | 
|  | break; | 
|  | assert(osecIdx > 0); | 
|  | // We should be only calling this function on offsets that belong to | 
|  | // ConcatOutputSections. | 
|  | auto *osec = cast<ConcatOutputSection>(sections[osecIdx - 1]); | 
|  | *off -= osec->fileOff; | 
|  |  | 
|  | size_t isecIdx = 0; | 
|  | for (; isecIdx < osec->inputs.size(); ++isecIdx) { | 
|  | const ConcatInputSection *isec = osec->inputs[isecIdx]; | 
|  | if (*off < isec->outSecOff) | 
|  | break; | 
|  | } | 
|  | assert(isecIdx > 0); | 
|  | ConcatInputSection *isec = osec->inputs[isecIdx - 1]; | 
|  | *off -= isec->outSecOff; | 
|  | return isec; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v, | 
|  | uint8_t bits, int64_t min, uint64_t max) { | 
|  | std::string hint; | 
|  | uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart; | 
|  | const InputSection *isec = offsetToInputSection(&off); | 
|  | std::string locStr = isec ? isec->getLocation(off) : "(invalid location)"; | 
|  | if (auto *sym = r.referent.dyn_cast<Symbol *>()) | 
|  | hint = "; references " + toString(*sym); | 
|  | error(locStr + ": relocation " + target->getRelocAttrs(r.type).name + | 
|  | " is out of range: " + v + " is not in [" + Twine(min) + ", " + | 
|  | Twine(max) + "]" + hint); | 
|  | } | 
|  |  | 
|  | void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v, | 
|  | uint8_t bits, int64_t min, uint64_t max) { | 
|  | // FIXME: should we use `loc` somehow to provide a better error message? | 
|  | std::string hint; | 
|  | if (d.symbol) | 
|  | hint = "; references " + toString(*d.symbol); | 
|  | error(d.reason + " is out of range: " + v + " is not in [" + Twine(min) + | 
|  | ", " + Twine(max) + "]" + hint); | 
|  | } | 
|  |  | 
|  | const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0}; |