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