File: | tools/lld/lib/ReaderWriter/MachO/GOTPass.cpp |
Warning: | line 165, column 7 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- lib/ReaderWriter/MachO/GOTPass.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 | /// \file | |||
11 | /// This linker pass transforms all GOT kind references to real references. | |||
12 | /// That is, in assembly you can write something like: | |||
13 | /// movq foo@GOTPCREL(%rip), %rax | |||
14 | /// which means you want to load a pointer to "foo" out of the GOT (global | |||
15 | /// Offsets Table). In the object file, the Atom containing this instruction | |||
16 | /// has a Reference whose target is an Atom named "foo" and the Reference | |||
17 | /// kind is a GOT load. The linker needs to instantiate a pointer sized | |||
18 | /// GOT entry. This is done be creating a GOT Atom to represent that pointer | |||
19 | /// sized data in this pass, and altering the Atom graph so the Reference now | |||
20 | /// points to the GOT Atom entry (corresponding to "foo") and changing the | |||
21 | /// Reference Kind to reflect it is now pointing to a GOT entry (rather | |||
22 | /// then needing a GOT entry). | |||
23 | /// | |||
24 | /// There is one optimization the linker can do here. If the target of the GOT | |||
25 | /// is in the same linkage unit and does not need to be interposable, and | |||
26 | /// the GOT use is just a load (not some other operation), this pass can | |||
27 | /// transform that load into an LEA (add). This optimizes away one memory load | |||
28 | /// which at runtime that could stall the pipeline. This optimization only | |||
29 | /// works for architectures in which a (GOT) load instruction can be change to | |||
30 | /// an LEA instruction that is the same size. The method isGOTAccess() should | |||
31 | /// only return true for "canBypassGOT" if this optimization is supported. | |||
32 | /// | |||
33 | //===----------------------------------------------------------------------===// | |||
34 | ||||
35 | #include "ArchHandler.h" | |||
36 | #include "File.h" | |||
37 | #include "MachOPasses.h" | |||
38 | #include "lld/Common/LLVM.h" | |||
39 | #include "lld/Core/DefinedAtom.h" | |||
40 | #include "lld/Core/File.h" | |||
41 | #include "lld/Core/Reference.h" | |||
42 | #include "lld/Core/Simple.h" | |||
43 | #include "llvm/ADT/DenseMap.h" | |||
44 | #include "llvm/ADT/STLExtras.h" | |||
45 | ||||
46 | namespace lld { | |||
47 | namespace mach_o { | |||
48 | ||||
49 | // | |||
50 | // GOT Entry Atom created by the GOT pass. | |||
51 | // | |||
52 | class GOTEntryAtom : public SimpleDefinedAtom { | |||
53 | public: | |||
54 | GOTEntryAtom(const File &file, bool is64, StringRef name) | |||
55 | : SimpleDefinedAtom(file), _is64(is64), _name(name) { } | |||
56 | ||||
57 | ~GOTEntryAtom() override = default; | |||
58 | ||||
59 | ContentType contentType() const override { | |||
60 | return DefinedAtom::typeGOT; | |||
61 | } | |||
62 | ||||
63 | Alignment alignment() const override { | |||
64 | return _is64 ? 8 : 4; | |||
65 | } | |||
66 | ||||
67 | uint64_t size() const override { | |||
68 | return _is64 ? 8 : 4; | |||
69 | } | |||
70 | ||||
71 | ContentPermissions permissions() const override { | |||
72 | return DefinedAtom::permRW_; | |||
73 | } | |||
74 | ||||
75 | ArrayRef<uint8_t> rawContent() const override { | |||
76 | static const uint8_t zeros[] = | |||
77 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |||
78 | return llvm::makeArrayRef(zeros, size()); | |||
79 | } | |||
80 | ||||
81 | StringRef slotName() const { | |||
82 | return _name; | |||
83 | } | |||
84 | ||||
85 | private: | |||
86 | const bool _is64; | |||
87 | StringRef _name; | |||
88 | }; | |||
89 | ||||
90 | /// Pass for instantiating and optimizing GOT slots. | |||
91 | /// | |||
92 | class GOTPass : public Pass { | |||
93 | public: | |||
94 | GOTPass(const MachOLinkingContext &context) | |||
95 | : _ctx(context), _archHandler(_ctx.archHandler()), | |||
96 | _file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) { | |||
97 | _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); | |||
98 | } | |||
99 | ||||
100 | private: | |||
101 | llvm::Error perform(SimpleFile &mergedFile) override { | |||
102 | // Scan all references in all atoms. | |||
103 | for (const DefinedAtom *atom : mergedFile.defined()) { | |||
104 | for (const Reference *ref : *atom) { | |||
105 | // Look at instructions accessing the GOT. | |||
106 | bool canBypassGOT; | |||
107 | if (!_archHandler.isGOTAccess(*ref, canBypassGOT)) | |||
| ||||
108 | continue; | |||
109 | const Atom *target = ref->target(); | |||
110 | assert(target != nullptr)((target != nullptr) ? static_cast<void> (0) : __assert_fail ("target != nullptr", "/build/llvm-toolchain-snapshot-8~svn345461/tools/lld/lib/ReaderWriter/MachO/GOTPass.cpp" , 110, __PRETTY_FUNCTION__)); | |||
111 | ||||
112 | if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) { | |||
113 | // Update reference kind to reflect that target is a direct accesss. | |||
114 | _archHandler.updateReferenceToGOT(ref, false); | |||
115 | } else { | |||
116 | // Replace the target with a reference to a GOT entry. | |||
117 | const DefinedAtom *gotEntry = makeGOTEntry(target); | |||
118 | const_cast<Reference *>(ref)->setTarget(gotEntry); | |||
119 | // Update reference kind to reflect that target is now a GOT entry. | |||
120 | _archHandler.updateReferenceToGOT(ref, true); | |||
121 | } | |||
122 | } | |||
123 | } | |||
124 | ||||
125 | // Sort and add all created GOT Atoms to master file | |||
126 | std::vector<const GOTEntryAtom *> entries; | |||
127 | entries.reserve(_targetToGOT.size()); | |||
128 | for (auto &it : _targetToGOT) | |||
129 | entries.push_back(it.second); | |||
130 | std::sort(entries.begin(), entries.end(), | |||
131 | [](const GOTEntryAtom *left, const GOTEntryAtom *right) { | |||
132 | return (left->slotName().compare(right->slotName()) < 0); | |||
133 | }); | |||
134 | for (const GOTEntryAtom *slot : entries) | |||
135 | mergedFile.addAtom(*slot); | |||
136 | ||||
137 | return llvm::Error::success(); | |||
138 | } | |||
139 | ||||
140 | bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) { | |||
141 | // Accesses to shared library symbols must go through GOT. | |||
142 | if (isa<SharedLibraryAtom>(target)) | |||
143 | return true; | |||
144 | // Accesses to interposable symbols in same linkage unit must also go | |||
145 | // through GOT. | |||
146 | const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); | |||
147 | if (defTarget != nullptr && | |||
148 | defTarget->interposable() != DefinedAtom::interposeNo) { | |||
149 | assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit)((defTarget->scope() != DefinedAtom::scopeTranslationUnit) ? static_cast<void> (0) : __assert_fail ("defTarget->scope() != DefinedAtom::scopeTranslationUnit" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/lld/lib/ReaderWriter/MachO/GOTPass.cpp" , 149, __PRETTY_FUNCTION__)); | |||
150 | return true; | |||
151 | } | |||
152 | // Target does not require indirection. So, if instruction allows GOT to be | |||
153 | // by-passed, do that optimization and don't create GOT entry. | |||
154 | return !canBypassGOT; | |||
155 | } | |||
156 | ||||
157 | const DefinedAtom *makeGOTEntry(const Atom *target) { | |||
158 | auto pos = _targetToGOT.find(target); | |||
159 | if (pos == _targetToGOT.end()) { | |||
160 | auto *gotEntry = new (_file.allocator()) | |||
161 | GOTEntryAtom(_file, _ctx.is64Bit(), target->name()); | |||
162 | _targetToGOT[target] = gotEntry; | |||
163 | const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo(). | |||
164 | nonLazyPointerReferenceToBinder; | |||
165 | gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch, | |||
| ||||
166 | nlInfo.kind, 0, target, 0); | |||
167 | return gotEntry; | |||
168 | } | |||
169 | return pos->second; | |||
170 | } | |||
171 | ||||
172 | const MachOLinkingContext &_ctx; | |||
173 | mach_o::ArchHandler &_archHandler; | |||
174 | MachOFile &_file; | |||
175 | llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT; | |||
176 | }; | |||
177 | ||||
178 | void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) { | |||
179 | assert(ctx.needsGOTPass())((ctx.needsGOTPass()) ? static_cast<void> (0) : __assert_fail ("ctx.needsGOTPass()", "/build/llvm-toolchain-snapshot-8~svn345461/tools/lld/lib/ReaderWriter/MachO/GOTPass.cpp" , 179, __PRETTY_FUNCTION__)); | |||
180 | pm.add(llvm::make_unique<GOTPass>(ctx)); | |||
181 | } | |||
182 | ||||
183 | } // end namesapce mach_o | |||
184 | } // end namesapce lld |