]>
Commit | Line | Data |
---|---|---|
a645023d A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2009 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | ||
26 | #include <stdint.h> | |
27 | #include <math.h> | |
28 | #include <unistd.h> | |
29 | #include <dlfcn.h> | |
30 | ||
31 | #include <vector> | |
32 | #include <map> | |
a645023d | 33 | |
f80fe69f | 34 | #include "MachOFileAbstraction.hpp" |
a645023d A |
35 | #include "ld.hpp" |
36 | #include "got.h" | |
f80fe69f | 37 | #include "configure.h" |
a645023d A |
38 | |
39 | namespace ld { | |
40 | namespace passes { | |
41 | namespace got { | |
42 | ||
43 | class File; // forward reference | |
44 | ||
45 | class GOTEntryAtom : public ld::Atom { | |
46 | public: | |
eaf282aa A |
47 | GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef, bool is64) |
48 | : ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, | |
a645023d | 49 | ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, |
f80fe69f A |
50 | symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), |
51 | _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), | |
52 | _target(target), | |
53 | _is64(is64) | |
a645023d A |
54 | { _fixup.weakImport = weakImport; internal.addAtom(*this); } |
55 | ||
56 | virtual const ld::File* file() const { return NULL; } | |
a645023d | 57 | virtual const char* name() const { return _target->name(); } |
f80fe69f | 58 | virtual uint64_t size() const { return (_is64 ? 8 : 4); } |
a645023d A |
59 | virtual uint64_t objectAddress() const { return 0; } |
60 | virtual void copyRawContent(uint8_t buffer[]) const { } | |
61 | virtual void setScope(Scope) { } | |
62 | virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } | |
63 | virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } | |
64 | ||
65 | private: | |
66 | mutable ld::Fixup _fixup; | |
67 | const ld::Atom* _target; | |
f80fe69f | 68 | bool _is64; |
a645023d A |
69 | |
70 | static ld::Section _s_section; | |
eaf282aa | 71 | static ld::Section _s_sectionWeak; |
a645023d A |
72 | }; |
73 | ||
74 | ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); | |
eaf282aa | 75 | ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer); |
a645023d A |
76 | |
77 | ||
eaf282aa | 78 | static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef) |
a645023d | 79 | { |
eaf282aa | 80 | *targetIsExternalWeakDef = false; |
a645023d A |
81 | switch (fixup->kind) { |
82 | case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: | |
f80fe69f A |
83 | #if SUPPORT_ARCH_arm64 |
84 | case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: | |
85 | case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: | |
86 | #endif | |
a645023d A |
87 | // start by assuming this can be optimized |
88 | *optimizable = true; | |
89 | // cannot do LEA optimization if target is in another dylib | |
90 | if ( targetOfGOT->definition() == ld::Atom::definitionProxy ) | |
91 | *optimizable = false; | |
92 | // cannot do LEA optimization if target in __huge section | |
93 | if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024) | |
94 | && ( (targetOfGOT->section().type() == ld::Section::typeZeroFill) | |
95 | || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) { | |
96 | *optimizable = false; | |
97 | } | |
eaf282aa | 98 | if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { |
a645023d | 99 | // cannot do LEA optimization if target is weak exported symbol |
eaf282aa | 100 | if ( ((targetOfGOT->definition() == ld::Atom::definitionRegular) || (targetOfGOT->definition() == ld::Atom::definitionProxy)) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { |
b2fa67a8 A |
101 | switch ( opts.outputKind() ) { |
102 | case Options::kDynamicExecutable: | |
103 | case Options::kDynamicLibrary: | |
104 | case Options::kDynamicBundle: | |
105 | case Options::kKextBundle: | |
eaf282aa | 106 | *targetIsExternalWeakDef = true; |
b2fa67a8 A |
107 | *optimizable = false; |
108 | break; | |
109 | case Options::kStaticExecutable: | |
110 | case Options::kDyld: | |
111 | case Options::kPreload: | |
112 | case Options::kObjectFile: | |
113 | break; | |
114 | } | |
115 | } | |
a645023d A |
116 | // cannot do LEA optimization if target is interposable |
117 | if ( opts.interposable(targetOfGOT->name()) ) | |
118 | *optimizable = false; | |
119 | // cannot do LEA optimization if target is resolver function | |
120 | if ( targetOfGOT->contentType() == ld::Atom::typeResolver ) | |
121 | *optimizable = false; | |
122 | // cannot do LEA optimization for flat-namespace | |
123 | if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) | |
124 | *optimizable = false; | |
125 | } | |
d425e388 A |
126 | else if ( targetOfGOT->scope() == ld::Atom::scopeLinkageUnit) { |
127 | // <rdar://problem/12379969> don't do optimization if target is in custom segment | |
128 | if ( opts.sharedRegionEligible() ) { | |
129 | const char* segName = targetOfGOT->section().segmentName(); | |
130 | if ( (strcmp(segName, "__TEXT") != 0) && (strcmp(segName, "__DATA") != 0) ) { | |
131 | *optimizable = false; | |
132 | } | |
133 | } | |
134 | } | |
a645023d A |
135 | return true; |
136 | case ld::Fixup::kindStoreX86PCRel32GOT: | |
f80fe69f A |
137 | #if SUPPORT_ARCH_arm64 |
138 | case ld::Fixup::kindStoreARM64PCRelToGOT: | |
139 | #endif | |
a645023d A |
140 | *optimizable = false; |
141 | return true; | |
afe874b1 A |
142 | case ld::Fixup::kindNoneGroupSubordinatePersonality: |
143 | *optimizable = false; | |
144 | return true; | |
a645023d A |
145 | default: |
146 | break; | |
147 | } | |
148 | ||
149 | return false; | |
150 | } | |
151 | ||
152 | struct AtomByNameSorter | |
153 | { | |
154 | bool operator()(const ld::Atom* left, const ld::Atom* right) | |
155 | { | |
156 | return (strcmp(left->name(), right->name()) < 0); | |
157 | } | |
158 | }; | |
159 | ||
160 | void doPass(const Options& opts, ld::Internal& internal) | |
161 | { | |
162 | const bool log = false; | |
163 | ||
164 | // only make got section in final linked images | |
165 | if ( opts.outputKind() == Options::kObjectFile ) | |
166 | return; | |
167 | ||
e667b16e A |
168 | // pre-fill gotMap with existing non-lazy pointers |
169 | std::map<const ld::Atom*, const ld::Atom*> gotMap; | |
170 | for (ld::Internal::FinalSection* sect : internal.sections) { | |
171 | if ( sect->type() != ld::Section::typeNonLazyPointer ) | |
172 | continue; | |
173 | for (const ld::Atom* atom : sect->atoms) { | |
174 | const ld::Atom* target = NULL; | |
175 | for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { | |
176 | switch (fit->kind) { | |
177 | case ld::Fixup::kindStoreTargetAddressLittleEndian64: | |
178 | case ld::Fixup::kindStoreTargetAddressLittleEndian32: | |
179 | switch ( fit->binding ) { | |
180 | case ld::Fixup::bindingsIndirectlyBound: | |
181 | target = internal.indirectBindingTable[fit->u.bindingIndex]; | |
182 | break; | |
183 | case ld::Fixup::bindingDirectlyBound: | |
184 | target = fit->u.target; | |
185 | break; | |
186 | default: | |
187 | fprintf(stderr, "non-pointer is got entry\n"); | |
188 | break; | |
189 | } | |
190 | break; | |
191 | default: | |
192 | break; | |
193 | } | |
194 | } | |
195 | if ( target != NULL ) { | |
196 | if (log) fprintf(stderr, "found existing got entry to %s\n", target->name()); | |
197 | gotMap[target] = atom; | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
afe874b1 A |
202 | // walk all atoms and fixups looking for GOT-able references |
203 | // don't create GOT atoms during this loop because that could invalidate the sections iterator | |
a645023d | 204 | std::vector<const ld::Atom*> atomsReferencingGOT; |
a645023d | 205 | std::map<const ld::Atom*,bool> weakImportMap; |
eaf282aa | 206 | std::map<const ld::Atom*,bool> weakDefMap; |
a645023d A |
207 | atomsReferencingGOT.reserve(128); |
208 | for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { | |
209 | ld::Internal::FinalSection* sect = *sit; | |
210 | for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { | |
211 | const ld::Atom* atom = *ait; | |
212 | bool atomUsesGOT = false; | |
213 | const ld::Atom* targetOfGOT = NULL; | |
afe874b1 | 214 | bool targetIsWeakImport = false; |
a645023d A |
215 | for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { |
216 | if ( fit->firstInCluster() ) | |
217 | targetOfGOT = NULL; | |
218 | switch ( fit->binding ) { | |
219 | case ld::Fixup::bindingsIndirectlyBound: | |
220 | targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; | |
afe874b1 | 221 | targetIsWeakImport = fit->weakImport; |
a645023d A |
222 | break; |
223 | case ld::Fixup::bindingDirectlyBound: | |
224 | targetOfGOT = fit->u.target; | |
afe874b1 | 225 | targetIsWeakImport = fit->weakImport; |
a645023d A |
226 | break; |
227 | default: | |
228 | break; | |
229 | } | |
230 | bool optimizable; | |
eaf282aa A |
231 | bool targetIsExternalWeakDef; |
232 | if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) | |
a645023d A |
233 | continue; |
234 | if ( optimizable ) { | |
235 | // change from load of GOT entry to lea of target | |
236 | if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name()); | |
237 | switch ( fit->binding ) { | |
238 | case ld::Fixup::bindingsIndirectlyBound: | |
239 | case ld::Fixup::bindingDirectlyBound: | |
240 | fit->binding = ld::Fixup::bindingDirectlyBound; | |
241 | fit->u.target = targetOfGOT; | |
f80fe69f A |
242 | switch ( fit->kind ) { |
243 | case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: | |
244 | fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; | |
245 | break; | |
246 | #if SUPPORT_ARCH_arm64 | |
247 | case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: | |
248 | fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21; | |
249 | break; | |
250 | case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: | |
251 | fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12; | |
252 | break; | |
253 | #endif | |
254 | default: | |
255 | assert(0 && "unsupported GOT reference kind"); | |
256 | break; | |
257 | } | |
a645023d A |
258 | break; |
259 | default: | |
260 | assert(0 && "unsupported GOT reference"); | |
261 | break; | |
262 | } | |
263 | } | |
264 | else { | |
265 | // remember that we need to use GOT in this function | |
e667b16e | 266 | if ( log ) fprintf(stderr, "found GOT use in %s\n", atom->name()); |
a645023d A |
267 | if ( !atomUsesGOT ) { |
268 | atomsReferencingGOT.push_back(atom); | |
269 | atomUsesGOT = true; | |
270 | } | |
e667b16e A |
271 | if ( gotMap.count(targetOfGOT) == 0 ) |
272 | gotMap[targetOfGOT] = NULL; | |
eaf282aa A |
273 | // record if target is weak def |
274 | weakDefMap[targetOfGOT] = targetIsExternalWeakDef; | |
a645023d A |
275 | // record weak_import attribute |
276 | std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT); | |
277 | if ( pos == weakImportMap.end() ) { | |
278 | // target not in weakImportMap, so add | |
afe874b1 A |
279 | if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport); |
280 | weakImportMap[targetOfGOT] = targetIsWeakImport; | |
a645023d A |
281 | } |
282 | else { | |
283 | // target in weakImportMap, check for weakness mismatch | |
afe874b1 | 284 | if ( pos->second != targetIsWeakImport ) { |
a645023d A |
285 | // found mismatch |
286 | switch ( opts.weakReferenceMismatchTreatment() ) { | |
287 | case Options::kWeakReferenceMismatchError: | |
288 | throwf("mismatching weak references for symbol: %s", targetOfGOT->name()); | |
289 | case Options::kWeakReferenceMismatchWeak: | |
290 | pos->second = true; | |
291 | break; | |
292 | case Options::kWeakReferenceMismatchNonWeak: | |
293 | pos->second = false; | |
294 | break; | |
295 | } | |
296 | } | |
297 | } | |
298 | } | |
299 | } | |
300 | } | |
301 | } | |
302 | ||
f80fe69f A |
303 | bool is64 = false; |
304 | switch ( opts.architecture() ) { | |
305 | #if SUPPORT_ARCH_i386 | |
306 | case CPU_TYPE_I386: | |
307 | is64 = false; | |
308 | break; | |
309 | #endif | |
310 | #if SUPPORT_ARCH_x86_64 | |
311 | case CPU_TYPE_X86_64: | |
312 | is64 = true; | |
313 | break; | |
314 | #endif | |
315 | #if SUPPORT_ARCH_arm_any | |
316 | case CPU_TYPE_ARM: | |
317 | is64 = false; | |
318 | break; | |
319 | #endif | |
320 | #if SUPPORT_ARCH_arm64 | |
321 | case CPU_TYPE_ARM64: | |
322 | is64 = true; | |
323 | break; | |
324 | #endif | |
325 | } | |
326 | ||
e667b16e A |
327 | // make GOT entries |
328 | for (auto& entry : gotMap) { | |
329 | if ( entry.second == NULL ) { | |
eaf282aa | 330 | entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], opts.useDataConstSegment() && weakDefMap[entry.first], is64); |
e667b16e A |
331 | if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second); |
332 | } | |
a645023d | 333 | } |
e667b16e A |
334 | |
335 | ||
a645023d A |
336 | // update atoms to use GOT entries |
337 | for (std::vector<const ld::Atom*>::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) { | |
338 | const ld::Atom* atom = *it; | |
339 | const ld::Atom* targetOfGOT = NULL; | |
340 | ld::Fixup::iterator fitThatSetTarget = NULL; | |
341 | for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { | |
342 | if ( fit->firstInCluster() ) { | |
343 | targetOfGOT = NULL; | |
344 | fitThatSetTarget = NULL; | |
345 | } | |
346 | switch ( fit->binding ) { | |
347 | case ld::Fixup::bindingsIndirectlyBound: | |
348 | targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; | |
349 | fitThatSetTarget = fit; | |
350 | break; | |
351 | case ld::Fixup::bindingDirectlyBound: | |
352 | targetOfGOT = fit->u.target; | |
353 | fitThatSetTarget = fit; | |
354 | break; | |
355 | default: | |
356 | break; | |
357 | } | |
358 | bool optimizable; | |
eaf282aa A |
359 | bool targetIsExternalWeakDef; |
360 | if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) | |
a645023d A |
361 | continue; |
362 | if ( !optimizable ) { | |
363 | // GOT use not optimized away, update to bind to GOT entry | |
364 | assert(fitThatSetTarget != NULL); | |
365 | switch ( fitThatSetTarget->binding ) { | |
366 | case ld::Fixup::bindingsIndirectlyBound: | |
367 | case ld::Fixup::bindingDirectlyBound: | |
e667b16e | 368 | if ( log ) fprintf(stderr, "updating GOT use in %s to %s\n", atom->name(), targetOfGOT->name()); |
a645023d A |
369 | fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound; |
370 | fitThatSetTarget->u.target = gotMap[targetOfGOT]; | |
371 | break; | |
372 | default: | |
373 | assert(0 && "unsupported GOT reference"); | |
374 | break; | |
375 | } | |
376 | } | |
377 | } | |
378 | } | |
379 | ||
380 | // sort new atoms so links are consistent | |
381 | for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { | |
382 | ld::Internal::FinalSection* sect = *sit; | |
383 | if ( sect->type() == ld::Section::typeNonLazyPointer ) { | |
384 | std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); | |
385 | } | |
386 | } | |
387 | } | |
388 | ||
389 | ||
390 | } // namespace got | |
391 | } // namespace passes | |
392 | } // namespace ld |