| //===- 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 referent.get<InputSection *>(); |
| } |
| } |
| |
| 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}; |