xref: /openbsd-src/gnu/llvm/lld/MachO/Relocations.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
11cf9926bSpatrick //===- Relocations.cpp ----------------------------------------------------===//
21cf9926bSpatrick //
31cf9926bSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41cf9926bSpatrick // See https://llvm.org/LICENSE.txt for license information.
51cf9926bSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61cf9926bSpatrick //
71cf9926bSpatrick //===----------------------------------------------------------------------===//
81cf9926bSpatrick 
91cf9926bSpatrick #include "Relocations.h"
10*dfe94b16Srobert #include "ConcatOutputSection.h"
111cf9926bSpatrick #include "Symbols.h"
121cf9926bSpatrick #include "SyntheticSections.h"
131cf9926bSpatrick #include "Target.h"
141cf9926bSpatrick 
151cf9926bSpatrick #include "lld/Common/ErrorHandler.h"
161cf9926bSpatrick 
171cf9926bSpatrick using namespace llvm;
181cf9926bSpatrick using namespace lld;
191cf9926bSpatrick using namespace lld::macho;
201cf9926bSpatrick 
21*dfe94b16Srobert static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24,
22*dfe94b16Srobert               "Try to minimize Reloc's size; we create many instances");
23*dfe94b16Srobert 
validateSymbolRelocation(const Symbol * sym,const InputSection * isec,const Reloc & r)241cf9926bSpatrick bool macho::validateSymbolRelocation(const Symbol *sym,
251cf9926bSpatrick                                      const InputSection *isec, const Reloc &r) {
261cf9926bSpatrick   const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
271cf9926bSpatrick   bool valid = true;
28*dfe94b16Srobert   auto message = [&](const Twine &diagnostic) {
291cf9926bSpatrick     valid = false;
30*dfe94b16Srobert     return (isec->getLocation(r.offset) + ": " + relocAttrs.name +
31*dfe94b16Srobert             " relocation " + diagnostic)
321cf9926bSpatrick         .str();
331cf9926bSpatrick   };
341cf9926bSpatrick 
351cf9926bSpatrick   if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv())
36*dfe94b16Srobert     error(message(Twine("requires that symbol ") + sym->getName() + " " +
371cf9926bSpatrick                   (sym->isTlv() ? "not " : "") + "be thread-local"));
381cf9926bSpatrick 
391cf9926bSpatrick   return valid;
401cf9926bSpatrick }
411cf9926bSpatrick 
42*dfe94b16Srobert // Given an offset in the output buffer, figure out which ConcatInputSection (if
43*dfe94b16Srobert // any) maps to it. At the same time, update the offset such that it is relative
44*dfe94b16Srobert // to the InputSection rather than to the output buffer.
45*dfe94b16Srobert //
46*dfe94b16Srobert // Obtaining the InputSection allows us to have better error diagnostics.
47*dfe94b16Srobert // However, many of our relocation-handling methods do not take the InputSection
48*dfe94b16Srobert // as a parameter. Since we are already passing the buffer offsets to our Target
49*dfe94b16Srobert // methods, this function allows us to emit better errors without threading an
50*dfe94b16Srobert // additional InputSection argument through the call stack.
51*dfe94b16Srobert //
52*dfe94b16Srobert // This is implemented as a slow linear search through OutputSegments,
53*dfe94b16Srobert // OutputSections, and finally the InputSections themselves. However, this
54*dfe94b16Srobert // function should be called only on error paths, so some overhead is fine.
offsetToInputSection(uint64_t * off)55*dfe94b16Srobert InputSection *macho::offsetToInputSection(uint64_t *off) {
56*dfe94b16Srobert   for (OutputSegment *seg : outputSegments) {
57*dfe94b16Srobert     if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize)
58*dfe94b16Srobert       continue;
59*dfe94b16Srobert 
60*dfe94b16Srobert     const std::vector<OutputSection *> &sections = seg->getSections();
61*dfe94b16Srobert     size_t osecIdx = 0;
62*dfe94b16Srobert     for (; osecIdx < sections.size(); ++osecIdx)
63*dfe94b16Srobert       if (*off < sections[osecIdx]->fileOff)
64*dfe94b16Srobert         break;
65*dfe94b16Srobert     assert(osecIdx > 0);
66*dfe94b16Srobert     // We should be only calling this function on offsets that belong to
67*dfe94b16Srobert     // ConcatOutputSections.
68*dfe94b16Srobert     auto *osec = cast<ConcatOutputSection>(sections[osecIdx - 1]);
69*dfe94b16Srobert     *off -= osec->fileOff;
70*dfe94b16Srobert 
71*dfe94b16Srobert     size_t isecIdx = 0;
72*dfe94b16Srobert     for (; isecIdx < osec->inputs.size(); ++isecIdx) {
73*dfe94b16Srobert       const ConcatInputSection *isec = osec->inputs[isecIdx];
74*dfe94b16Srobert       if (*off < isec->outSecOff)
75*dfe94b16Srobert         break;
76*dfe94b16Srobert     }
77*dfe94b16Srobert     assert(isecIdx > 0);
78*dfe94b16Srobert     ConcatInputSection *isec = osec->inputs[isecIdx - 1];
79*dfe94b16Srobert     *off -= isec->outSecOff;
80*dfe94b16Srobert     return isec;
81*dfe94b16Srobert   }
82*dfe94b16Srobert   return nullptr;
83*dfe94b16Srobert }
84*dfe94b16Srobert 
reportRangeError(void * loc,const Reloc & r,const Twine & v,uint8_t bits,int64_t min,uint64_t max)85*dfe94b16Srobert void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v,
86*dfe94b16Srobert                              uint8_t bits, int64_t min, uint64_t max) {
871cf9926bSpatrick   std::string hint;
88*dfe94b16Srobert   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
89*dfe94b16Srobert   const InputSection *isec = offsetToInputSection(&off);
90*dfe94b16Srobert   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
911cf9926bSpatrick   if (auto *sym = r.referent.dyn_cast<Symbol *>())
921cf9926bSpatrick     hint = "; references " + toString(*sym);
93*dfe94b16Srobert   error(locStr + ": relocation " + target->getRelocAttrs(r.type).name +
941cf9926bSpatrick         " is out of range: " + v + " is not in [" + Twine(min) + ", " +
951cf9926bSpatrick         Twine(max) + "]" + hint);
961cf9926bSpatrick }
971cf9926bSpatrick 
reportRangeError(void * loc,SymbolDiagnostic d,const Twine & v,uint8_t bits,int64_t min,uint64_t max)98*dfe94b16Srobert void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v,
99*dfe94b16Srobert                              uint8_t bits, int64_t min, uint64_t max) {
100*dfe94b16Srobert   // FIXME: should we use `loc` somehow to provide a better error message?
1011cf9926bSpatrick   std::string hint;
1021cf9926bSpatrick   if (d.symbol)
1031cf9926bSpatrick     hint = "; references " + toString(*d.symbol);
1041cf9926bSpatrick   error(d.reason + " is out of range: " + v + " is not in [" + Twine(min) +
1051cf9926bSpatrick         ", " + Twine(max) + "]" + hint);
1061cf9926bSpatrick }
1071cf9926bSpatrick 
1081cf9926bSpatrick const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0};
109