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 referent.get<InputSection *>(); |
31 | } |
32 | } |
33 | |
34 | bool macho::validateSymbolRelocation(const Symbol *sym, |
35 | const InputSection *isec, const Reloc &r) { |
36 | const RelocAttrs &relocAttrs = target->getRelocAttrs(type: r.type); |
37 | bool valid = true; |
38 | auto message = [&](const Twine &diagnostic) { |
39 | valid = false; |
40 | return (isec->getLocation(off: r.offset) + ": " + relocAttrs.name + |
41 | " relocation " + diagnostic) |
42 | .str(); |
43 | }; |
44 | |
45 | if (relocAttrs.hasAttr(b: RelocAttrBits::TLV) != sym->isTlv()) |
46 | error(msg: message(Twine("requires that symbol " ) + sym->getName() + " " + |
47 | (sym->isTlv() ? "not " : "" ) + "be thread-local" )); |
48 | |
49 | return valid; |
50 | } |
51 | |
52 | // Given an offset in the output buffer, figure out which ConcatInputSection (if |
53 | // any) maps to it. At the same time, update the offset such that it is relative |
54 | // to the InputSection rather than to the output buffer. |
55 | // |
56 | // Obtaining the InputSection allows us to have better error diagnostics. |
57 | // However, many of our relocation-handling methods do not take the InputSection |
58 | // as a parameter. Since we are already passing the buffer offsets to our Target |
59 | // methods, this function allows us to emit better errors without threading an |
60 | // additional InputSection argument through the call stack. |
61 | // |
62 | // This is implemented as a slow linear search through OutputSegments, |
63 | // OutputSections, and finally the InputSections themselves. However, this |
64 | // function should be called only on error paths, so some overhead is fine. |
65 | InputSection *macho::offsetToInputSection(uint64_t *off) { |
66 | for (OutputSegment *seg : outputSegments) { |
67 | if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize) |
68 | continue; |
69 | |
70 | const std::vector<OutputSection *> §ions = seg->getSections(); |
71 | size_t osecIdx = 0; |
72 | for (; osecIdx < sections.size(); ++osecIdx) |
73 | if (*off < sections[osecIdx]->fileOff) |
74 | break; |
75 | assert(osecIdx > 0); |
76 | // We should be only calling this function on offsets that belong to |
77 | // ConcatOutputSections. |
78 | auto *osec = cast<ConcatOutputSection>(Val: sections[osecIdx - 1]); |
79 | *off -= osec->fileOff; |
80 | |
81 | size_t isecIdx = 0; |
82 | for (; isecIdx < osec->inputs.size(); ++isecIdx) { |
83 | const ConcatInputSection *isec = osec->inputs[isecIdx]; |
84 | if (*off < isec->outSecOff) |
85 | break; |
86 | } |
87 | assert(isecIdx > 0); |
88 | ConcatInputSection *isec = osec->inputs[isecIdx - 1]; |
89 | *off -= isec->outSecOff; |
90 | return isec; |
91 | } |
92 | return nullptr; |
93 | } |
94 | |
95 | void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v, |
96 | uint8_t bits, int64_t min, uint64_t max) { |
97 | std::string hint; |
98 | uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart; |
99 | const InputSection *isec = offsetToInputSection(off: &off); |
100 | std::string locStr = isec ? isec->getLocation(off) : "(invalid location)" ; |
101 | if (auto *sym = r.referent.dyn_cast<Symbol *>()) |
102 | hint = "; references " + toString(*sym); |
103 | error(msg: locStr + ": relocation " + target->getRelocAttrs(type: r.type).name + |
104 | " is out of range: " + v + " is not in [" + Twine(min) + ", " + |
105 | Twine(max) + "]" + hint); |
106 | } |
107 | |
108 | void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v, |
109 | uint8_t bits, int64_t min, uint64_t max) { |
110 | // FIXME: should we use `loc` somehow to provide a better error message? |
111 | std::string hint; |
112 | if (d.symbol) |
113 | hint = "; references " + toString(*d.symbol); |
114 | error(msg: d.reason + " is out of range: " + v + " is not in [" + Twine(min) + |
115 | ", " + Twine(max) + "]" + hint); |
116 | } |
117 | |
118 | const RelocAttrs macho::invalidRelocAttrs{.name: "INVALID" , .bits: RelocAttrBits::_0}; |
119 | |