File: | tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp |
Warning: | line 270, column 5 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===// | |||
2 | // | |||
3 | // The LLVM Linker | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | // | |||
10 | // This linker pass updates call-sites which have references to shared library | |||
11 | // atoms to instead have a reference to a stub (PLT entry) for the specified | |||
12 | // symbol. Each file format defines a subclass of StubsPass which implements | |||
13 | // the abstract methods for creating the file format specific StubAtoms. | |||
14 | // | |||
15 | //===----------------------------------------------------------------------===// | |||
16 | ||||
17 | #include "ArchHandler.h" | |||
18 | #include "File.h" | |||
19 | #include "MachOPasses.h" | |||
20 | #include "lld/Common/LLVM.h" | |||
21 | #include "lld/Core/DefinedAtom.h" | |||
22 | #include "lld/Core/File.h" | |||
23 | #include "lld/Core/Reference.h" | |||
24 | #include "lld/Core/Simple.h" | |||
25 | #include "lld/ReaderWriter/MachOLinkingContext.h" | |||
26 | #include "llvm/ADT/DenseMap.h" | |||
27 | #include "llvm/ADT/SmallVector.h" | |||
28 | ||||
29 | namespace lld { | |||
30 | namespace mach_o { | |||
31 | ||||
32 | // | |||
33 | // Lazy Pointer Atom created by the stubs pass. | |||
34 | // | |||
35 | class LazyPointerAtom : public SimpleDefinedAtom { | |||
36 | public: | |||
37 | LazyPointerAtom(const File &file, bool is64) | |||
38 | : SimpleDefinedAtom(file), _is64(is64) { } | |||
39 | ||||
40 | ~LazyPointerAtom() override = default; | |||
41 | ||||
42 | ContentType contentType() const override { | |||
43 | return DefinedAtom::typeLazyPointer; | |||
44 | } | |||
45 | ||||
46 | Alignment alignment() const override { | |||
47 | return _is64 ? 8 : 4; | |||
48 | } | |||
49 | ||||
50 | uint64_t size() const override { | |||
51 | return _is64 ? 8 : 4; | |||
52 | } | |||
53 | ||||
54 | ContentPermissions permissions() const override { | |||
55 | return DefinedAtom::permRW_; | |||
56 | } | |||
57 | ||||
58 | ArrayRef<uint8_t> rawContent() const override { | |||
59 | static const uint8_t zeros[] = | |||
60 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |||
61 | return llvm::makeArrayRef(zeros, size()); | |||
62 | } | |||
63 | ||||
64 | private: | |||
65 | const bool _is64; | |||
66 | }; | |||
67 | ||||
68 | // | |||
69 | // NonLazyPointer (GOT) Atom created by the stubs pass. | |||
70 | // | |||
71 | class NonLazyPointerAtom : public SimpleDefinedAtom { | |||
72 | public: | |||
73 | NonLazyPointerAtom(const File &file, bool is64, ContentType contentType) | |||
74 | : SimpleDefinedAtom(file), _is64(is64), _contentType(contentType) { } | |||
75 | ||||
76 | ~NonLazyPointerAtom() override = default; | |||
77 | ||||
78 | ContentType contentType() const override { | |||
79 | return _contentType; | |||
80 | } | |||
81 | ||||
82 | Alignment alignment() const override { | |||
83 | return _is64 ? 8 : 4; | |||
84 | } | |||
85 | ||||
86 | uint64_t size() const override { | |||
87 | return _is64 ? 8 : 4; | |||
88 | } | |||
89 | ||||
90 | ContentPermissions permissions() const override { | |||
91 | return DefinedAtom::permRW_; | |||
92 | } | |||
93 | ||||
94 | ArrayRef<uint8_t> rawContent() const override { | |||
95 | static const uint8_t zeros[] = | |||
96 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |||
97 | return llvm::makeArrayRef(zeros, size()); | |||
98 | } | |||
99 | ||||
100 | private: | |||
101 | const bool _is64; | |||
102 | const ContentType _contentType; | |||
103 | }; | |||
104 | ||||
105 | // | |||
106 | // Stub Atom created by the stubs pass. | |||
107 | // | |||
108 | class StubAtom : public SimpleDefinedAtom { | |||
109 | public: | |||
110 | StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo) | |||
111 | : SimpleDefinedAtom(file), _stubInfo(stubInfo){ } | |||
112 | ||||
113 | ~StubAtom() override = default; | |||
114 | ||||
115 | ContentType contentType() const override { | |||
116 | return DefinedAtom::typeStub; | |||
117 | } | |||
118 | ||||
119 | Alignment alignment() const override { | |||
120 | return 1 << _stubInfo.codeAlignment; | |||
121 | } | |||
122 | ||||
123 | uint64_t size() const override { | |||
124 | return _stubInfo.stubSize; | |||
125 | } | |||
126 | ||||
127 | ContentPermissions permissions() const override { | |||
128 | return DefinedAtom::permR_X; | |||
129 | } | |||
130 | ||||
131 | ArrayRef<uint8_t> rawContent() const override { | |||
132 | return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize); | |||
133 | } | |||
134 | ||||
135 | private: | |||
136 | const ArchHandler::StubInfo &_stubInfo; | |||
137 | }; | |||
138 | ||||
139 | // | |||
140 | // Stub Helper Atom created by the stubs pass. | |||
141 | // | |||
142 | class StubHelperAtom : public SimpleDefinedAtom { | |||
143 | public: | |||
144 | StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo) | |||
145 | : SimpleDefinedAtom(file), _stubInfo(stubInfo) { } | |||
146 | ||||
147 | ~StubHelperAtom() override = default; | |||
148 | ||||
149 | ContentType contentType() const override { | |||
150 | return DefinedAtom::typeStubHelper; | |||
151 | } | |||
152 | ||||
153 | Alignment alignment() const override { | |||
154 | return 1 << _stubInfo.codeAlignment; | |||
155 | } | |||
156 | ||||
157 | uint64_t size() const override { | |||
158 | return _stubInfo.stubHelperSize; | |||
159 | } | |||
160 | ||||
161 | ContentPermissions permissions() const override { | |||
162 | return DefinedAtom::permR_X; | |||
163 | } | |||
164 | ||||
165 | ArrayRef<uint8_t> rawContent() const override { | |||
166 | return llvm::makeArrayRef(_stubInfo.stubHelperBytes, | |||
167 | _stubInfo.stubHelperSize); | |||
168 | } | |||
169 | ||||
170 | private: | |||
171 | const ArchHandler::StubInfo &_stubInfo; | |||
172 | }; | |||
173 | ||||
174 | // | |||
175 | // Stub Helper Common Atom created by the stubs pass. | |||
176 | // | |||
177 | class StubHelperCommonAtom : public SimpleDefinedAtom { | |||
178 | public: | |||
179 | StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo) | |||
180 | : SimpleDefinedAtom(file), _stubInfo(stubInfo) { } | |||
181 | ||||
182 | ~StubHelperCommonAtom() override = default; | |||
183 | ||||
184 | ContentType contentType() const override { | |||
185 | return DefinedAtom::typeStubHelper; | |||
186 | } | |||
187 | ||||
188 | Alignment alignment() const override { | |||
189 | return 1 << _stubInfo.stubHelperCommonAlignment; | |||
190 | } | |||
191 | ||||
192 | uint64_t size() const override { | |||
193 | return _stubInfo.stubHelperCommonSize; | |||
194 | } | |||
195 | ||||
196 | ContentPermissions permissions() const override { | |||
197 | return DefinedAtom::permR_X; | |||
198 | } | |||
199 | ||||
200 | ArrayRef<uint8_t> rawContent() const override { | |||
201 | return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes, | |||
202 | _stubInfo.stubHelperCommonSize); | |||
203 | } | |||
204 | ||||
205 | private: | |||
206 | const ArchHandler::StubInfo &_stubInfo; | |||
207 | }; | |||
208 | ||||
209 | class StubsPass : public Pass { | |||
210 | public: | |||
211 | StubsPass(const MachOLinkingContext &context) | |||
212 | : _ctx(context), _archHandler(_ctx.archHandler()), | |||
213 | _stubInfo(_archHandler.stubInfo()), | |||
214 | _file(*_ctx.make_file<MachOFile>("<mach-o Stubs pass>")) { | |||
215 | _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); | |||
216 | } | |||
217 | ||||
218 | llvm::Error perform(SimpleFile &mergedFile) override { | |||
219 | // Skip this pass if output format uses text relocations instead of stubs. | |||
220 | if (!this->noTextRelocs()) | |||
| ||||
221 | return llvm::Error::success(); | |||
222 | ||||
223 | // Scan all references in all atoms. | |||
224 | for (const DefinedAtom *atom : mergedFile.defined()) { | |||
225 | for (const Reference *ref : *atom) { | |||
226 | // Look at call-sites. | |||
227 | if (!this->isCallSite(*ref)) | |||
228 | continue; | |||
229 | const Atom *target = ref->target(); | |||
230 | assert(target != nullptr)(static_cast <bool> (target != nullptr) ? void (0) : __assert_fail ("target != nullptr", "/build/llvm-toolchain-snapshot-7~svn338205/tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp" , 230, __extension__ __PRETTY_FUNCTION__)); | |||
231 | if (isa<SharedLibraryAtom>(target)) { | |||
232 | // Calls to shared libraries go through stubs. | |||
233 | _targetToUses[target].push_back(ref); | |||
234 | continue; | |||
235 | } | |||
236 | const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); | |||
237 | if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){ | |||
238 | // Calls to interposable functions in same linkage unit must also go | |||
239 | // through a stub. | |||
240 | assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit)(static_cast <bool> (defTarget->scope() != DefinedAtom ::scopeTranslationUnit) ? void (0) : __assert_fail ("defTarget->scope() != DefinedAtom::scopeTranslationUnit" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp" , 240, __extension__ __PRETTY_FUNCTION__)); | |||
241 | _targetToUses[target].push_back(ref); | |||
242 | } | |||
243 | } | |||
244 | } | |||
245 | ||||
246 | // Exit early if no stubs needed. | |||
247 | if (_targetToUses.empty()) | |||
248 | return llvm::Error::success(); | |||
249 | ||||
250 | // First add help-common and GOT slots used by lazy binding. | |||
251 | SimpleDefinedAtom *helperCommonAtom = | |||
252 | new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo); | |||
253 | SimpleDefinedAtom *helperCacheNLPAtom = | |||
254 | new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(), | |||
255 | _stubInfo.stubHelperImageCacheContentType); | |||
256 | SimpleDefinedAtom *helperBinderNLPAtom = | |||
257 | new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(), | |||
258 | _stubInfo.stubHelperImageCacheContentType); | |||
259 | addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, | |||
260 | helperCacheNLPAtom); | |||
261 | addOptReference( | |||
262 | helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, | |||
263 | _stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom); | |||
264 | addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, | |||
265 | helperBinderNLPAtom); | |||
266 | addOptReference( | |||
267 | helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, | |||
268 | _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom); | |||
269 | mergedFile.addAtom(*helperCommonAtom); | |||
270 | mergedFile.addAtom(*helperBinderNLPAtom); | |||
| ||||
271 | mergedFile.addAtom(*helperCacheNLPAtom); | |||
272 | ||||
273 | // Add reference to dyld_stub_binder in libSystem.dylib | |||
274 | auto I = std::find_if( | |||
275 | mergedFile.sharedLibrary().begin(), mergedFile.sharedLibrary().end(), | |||
276 | [&](const SharedLibraryAtom *atom) { | |||
277 | return atom->name().equals(_stubInfo.binderSymbolName); | |||
278 | }); | |||
279 | assert(I != mergedFile.sharedLibrary().end() &&(static_cast <bool> (I != mergedFile.sharedLibrary().end () && "dyld_stub_binder not found") ? void (0) : __assert_fail ("I != mergedFile.sharedLibrary().end() && \"dyld_stub_binder not found\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp" , 280, __extension__ __PRETTY_FUNCTION__)) | |||
280 | "dyld_stub_binder not found")(static_cast <bool> (I != mergedFile.sharedLibrary().end () && "dyld_stub_binder not found") ? void (0) : __assert_fail ("I != mergedFile.sharedLibrary().end() && \"dyld_stub_binder not found\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp" , 280, __extension__ __PRETTY_FUNCTION__)); | |||
281 | addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I); | |||
282 | ||||
283 | // Sort targets by name, so stubs and lazy pointers are consistent | |||
284 | std::vector<const Atom *> targetsNeedingStubs; | |||
285 | for (auto it : _targetToUses) | |||
286 | targetsNeedingStubs.push_back(it.first); | |||
287 | std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(), | |||
288 | [](const Atom * left, const Atom * right) { | |||
289 | return (left->name().compare(right->name()) < 0); | |||
290 | }); | |||
291 | ||||
292 | // Make and append stubs, lazy pointers, and helpers in alphabetical order. | |||
293 | unsigned lazyOffset = 0; | |||
294 | for (const Atom *target : targetsNeedingStubs) { | |||
295 | auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo); | |||
296 | auto *lp = | |||
297 | new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit()); | |||
298 | auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo); | |||
299 | ||||
300 | addReference(stub, _stubInfo.stubReferenceToLP, lp); | |||
301 | addOptReference(stub, _stubInfo.stubReferenceToLP, | |||
302 | _stubInfo.optStubReferenceToLP, lp); | |||
303 | addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper); | |||
304 | addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target); | |||
305 | addReference(helper, _stubInfo.stubHelperReferenceToImm, helper); | |||
306 | addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper, | |||
307 | lazyOffset); | |||
308 | addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon, | |||
309 | helperCommonAtom); | |||
310 | ||||
311 | mergedFile.addAtom(*stub); | |||
312 | mergedFile.addAtom(*lp); | |||
313 | mergedFile.addAtom(*helper); | |||
314 | ||||
315 | // Update each reference to use stub. | |||
316 | for (const Reference *ref : _targetToUses[target]) { | |||
317 | assert(ref->target() == target)(static_cast <bool> (ref->target() == target) ? void (0) : __assert_fail ("ref->target() == target", "/build/llvm-toolchain-snapshot-7~svn338205/tools/lld/lib/ReaderWriter/MachO/StubsPass.cpp" , 317, __extension__ __PRETTY_FUNCTION__)); | |||
318 | // Switch call site to reference stub atom instead. | |||
319 | const_cast<Reference *>(ref)->setTarget(stub); | |||
320 | } | |||
321 | ||||
322 | // Calculate new offset | |||
323 | lazyOffset += target->name().size() + 12; | |||
324 | } | |||
325 | ||||
326 | return llvm::Error::success(); | |||
327 | } | |||
328 | ||||
329 | private: | |||
330 | bool noTextRelocs() { | |||
331 | return true; | |||
332 | } | |||
333 | ||||
334 | bool isCallSite(const Reference &ref) { | |||
335 | return _archHandler.isCallSite(ref); | |||
336 | } | |||
337 | ||||
338 | void addReference(SimpleDefinedAtom* atom, | |||
339 | const ArchHandler::ReferenceInfo &refInfo, | |||
340 | const lld::Atom* target) { | |||
341 | atom->addReference(Reference::KindNamespace::mach_o, | |||
342 | refInfo.arch, refInfo.kind, refInfo.offset, | |||
343 | target, refInfo.addend); | |||
344 | } | |||
345 | ||||
346 | void addReferenceAddend(SimpleDefinedAtom *atom, | |||
347 | const ArchHandler::ReferenceInfo &refInfo, | |||
348 | const lld::Atom *target, uint64_t addend) { | |||
349 | atom->addReference(Reference::KindNamespace::mach_o, refInfo.arch, | |||
350 | refInfo.kind, refInfo.offset, target, addend); | |||
351 | } | |||
352 | ||||
353 | void addOptReference(SimpleDefinedAtom* atom, | |||
354 | const ArchHandler::ReferenceInfo &refInfo, | |||
355 | const ArchHandler::OptionalRefInfo &optRef, | |||
356 | const lld::Atom* target) { | |||
357 | if (!optRef.used) | |||
358 | return; | |||
359 | atom->addReference(Reference::KindNamespace::mach_o, | |||
360 | refInfo.arch, optRef.kind, optRef.offset, | |||
361 | target, optRef.addend); | |||
362 | } | |||
363 | ||||
364 | typedef llvm::DenseMap<const Atom*, | |||
365 | llvm::SmallVector<const Reference *, 8>> TargetToUses; | |||
366 | ||||
367 | const MachOLinkingContext &_ctx; | |||
368 | mach_o::ArchHandler &_archHandler; | |||
369 | const ArchHandler::StubInfo &_stubInfo; | |||
370 | MachOFile &_file; | |||
371 | TargetToUses _targetToUses; | |||
372 | }; | |||
373 | ||||
374 | void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) { | |||
375 | pm.add(std::unique_ptr<Pass>(new StubsPass(ctx))); | |||
376 | } | |||
377 | ||||
378 | } // end namespace mach_o | |||
379 | } // end namespace lld |