]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/got.cpp
ld64-242.2.tar.gz
[apple/ld64.git] / src / ld / passes / got.cpp
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>
33
34 #include "MachOFileAbstraction.hpp"
35 #include "ld.hpp"
36 #include "got.h"
37 #include "configure.h"
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:
47 GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64)
48 : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
49 ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
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)
54 { _fixup.weakImport = weakImport; internal.addAtom(*this); }
55
56 virtual const ld::File* file() const { return NULL; }
57 virtual const char* name() const { return _target->name(); }
58 virtual uint64_t size() const { return (_is64 ? 8 : 4); }
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;
68 bool _is64;
69
70 static ld::Section _s_section;
71 };
72
73 ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
74
75
76 static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable)
77 {
78 switch (fixup->kind) {
79 case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
80 #if SUPPORT_ARCH_arm64
81 case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
82 case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
83 #endif
84 // start by assuming this can be optimized
85 *optimizable = true;
86 // cannot do LEA optimization if target is in another dylib
87 if ( targetOfGOT->definition() == ld::Atom::definitionProxy )
88 *optimizable = false;
89 // cannot do LEA optimization if target in __huge section
90 if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024)
91 && ( (targetOfGOT->section().type() == ld::Section::typeZeroFill)
92 || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) {
93 *optimizable = false;
94 }
95 if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) {
96 // cannot do LEA optimization if target is weak exported symbol
97 if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) {
98 switch ( opts.outputKind() ) {
99 case Options::kDynamicExecutable:
100 case Options::kDynamicLibrary:
101 case Options::kDynamicBundle:
102 case Options::kKextBundle:
103 *optimizable = false;
104 break;
105 case Options::kStaticExecutable:
106 case Options::kDyld:
107 case Options::kPreload:
108 case Options::kObjectFile:
109 break;
110 }
111 }
112 // cannot do LEA optimization if target is interposable
113 if ( opts.interposable(targetOfGOT->name()) )
114 *optimizable = false;
115 // cannot do LEA optimization if target is resolver function
116 if ( targetOfGOT->contentType() == ld::Atom::typeResolver )
117 *optimizable = false;
118 // cannot do LEA optimization for flat-namespace
119 if ( opts.nameSpace() != Options::kTwoLevelNameSpace )
120 *optimizable = false;
121 }
122 else if ( targetOfGOT->scope() == ld::Atom::scopeLinkageUnit) {
123 // <rdar://problem/12379969> don't do optimization if target is in custom segment
124 if ( opts.sharedRegionEligible() ) {
125 const char* segName = targetOfGOT->section().segmentName();
126 if ( (strcmp(segName, "__TEXT") != 0) && (strcmp(segName, "__DATA") != 0) ) {
127 *optimizable = false;
128 }
129 }
130 }
131 return true;
132 case ld::Fixup::kindStoreX86PCRel32GOT:
133 #if SUPPORT_ARCH_arm64
134 case ld::Fixup::kindStoreARM64PCRelToGOT:
135 #endif
136 *optimizable = false;
137 return true;
138 case ld::Fixup::kindNoneGroupSubordinatePersonality:
139 *optimizable = false;
140 return true;
141 default:
142 break;
143 }
144
145 return false;
146 }
147
148 struct AtomByNameSorter
149 {
150 bool operator()(const ld::Atom* left, const ld::Atom* right)
151 {
152 return (strcmp(left->name(), right->name()) < 0);
153 }
154 };
155
156 void doPass(const Options& opts, ld::Internal& internal)
157 {
158 const bool log = false;
159
160 // only make got section in final linked images
161 if ( opts.outputKind() == Options::kObjectFile )
162 return;
163
164 // pre-fill gotMap with existing non-lazy pointers
165 std::map<const ld::Atom*, const ld::Atom*> gotMap;
166 for (ld::Internal::FinalSection* sect : internal.sections) {
167 if ( sect->type() != ld::Section::typeNonLazyPointer )
168 continue;
169 for (const ld::Atom* atom : sect->atoms) {
170 const ld::Atom* target = NULL;
171 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
172 switch (fit->kind) {
173 case ld::Fixup::kindStoreTargetAddressLittleEndian64:
174 case ld::Fixup::kindStoreTargetAddressLittleEndian32:
175 switch ( fit->binding ) {
176 case ld::Fixup::bindingsIndirectlyBound:
177 target = internal.indirectBindingTable[fit->u.bindingIndex];
178 break;
179 case ld::Fixup::bindingDirectlyBound:
180 target = fit->u.target;
181 break;
182 default:
183 fprintf(stderr, "non-pointer is got entry\n");
184 break;
185 }
186 break;
187 default:
188 break;
189 }
190 }
191 if ( target != NULL ) {
192 if (log) fprintf(stderr, "found existing got entry to %s\n", target->name());
193 gotMap[target] = atom;
194 }
195 }
196 }
197
198 // walk all atoms and fixups looking for GOT-able references
199 // don't create GOT atoms during this loop because that could invalidate the sections iterator
200 std::vector<const ld::Atom*> atomsReferencingGOT;
201 std::map<const ld::Atom*,bool> weakImportMap;
202 atomsReferencingGOT.reserve(128);
203 for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
204 ld::Internal::FinalSection* sect = *sit;
205 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
206 const ld::Atom* atom = *ait;
207 bool atomUsesGOT = false;
208 const ld::Atom* targetOfGOT = NULL;
209 bool targetIsWeakImport = false;
210 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
211 if ( fit->firstInCluster() )
212 targetOfGOT = NULL;
213 switch ( fit->binding ) {
214 case ld::Fixup::bindingsIndirectlyBound:
215 targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
216 targetIsWeakImport = fit->weakImport;
217 break;
218 case ld::Fixup::bindingDirectlyBound:
219 targetOfGOT = fit->u.target;
220 targetIsWeakImport = fit->weakImport;
221 break;
222 default:
223 break;
224 }
225 bool optimizable;
226 if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) )
227 continue;
228 if ( optimizable ) {
229 // change from load of GOT entry to lea of target
230 if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name());
231 switch ( fit->binding ) {
232 case ld::Fixup::bindingsIndirectlyBound:
233 case ld::Fixup::bindingDirectlyBound:
234 fit->binding = ld::Fixup::bindingDirectlyBound;
235 fit->u.target = targetOfGOT;
236 switch ( fit->kind ) {
237 case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
238 fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA;
239 break;
240 #if SUPPORT_ARCH_arm64
241 case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
242 fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21;
243 break;
244 case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
245 fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12;
246 break;
247 #endif
248 default:
249 assert(0 && "unsupported GOT reference kind");
250 break;
251 }
252 break;
253 default:
254 assert(0 && "unsupported GOT reference");
255 break;
256 }
257 }
258 else {
259 // remember that we need to use GOT in this function
260 if ( log ) fprintf(stderr, "found GOT use in %s\n", atom->name());
261 if ( !atomUsesGOT ) {
262 atomsReferencingGOT.push_back(atom);
263 atomUsesGOT = true;
264 }
265 if ( gotMap.count(targetOfGOT) == 0 )
266 gotMap[targetOfGOT] = NULL;
267 // record weak_import attribute
268 std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT);
269 if ( pos == weakImportMap.end() ) {
270 // target not in weakImportMap, so add
271 if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport);
272 weakImportMap[targetOfGOT] = targetIsWeakImport;
273 }
274 else {
275 // target in weakImportMap, check for weakness mismatch
276 if ( pos->second != targetIsWeakImport ) {
277 // found mismatch
278 switch ( opts.weakReferenceMismatchTreatment() ) {
279 case Options::kWeakReferenceMismatchError:
280 throwf("mismatching weak references for symbol: %s", targetOfGOT->name());
281 case Options::kWeakReferenceMismatchWeak:
282 pos->second = true;
283 break;
284 case Options::kWeakReferenceMismatchNonWeak:
285 pos->second = false;
286 break;
287 }
288 }
289 }
290 }
291 }
292 }
293 }
294
295 bool is64 = false;
296 switch ( opts.architecture() ) {
297 #if SUPPORT_ARCH_i386
298 case CPU_TYPE_I386:
299 is64 = false;
300 break;
301 #endif
302 #if SUPPORT_ARCH_x86_64
303 case CPU_TYPE_X86_64:
304 is64 = true;
305 break;
306 #endif
307 #if SUPPORT_ARCH_arm_any
308 case CPU_TYPE_ARM:
309 is64 = false;
310 break;
311 #endif
312 #if SUPPORT_ARCH_arm64
313 case CPU_TYPE_ARM64:
314 is64 = true;
315 break;
316 #endif
317 }
318
319 // make GOT entries
320 for (auto& entry : gotMap) {
321 if ( entry.second == NULL ) {
322 entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], is64);
323 if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second);
324 }
325 }
326
327
328 // update atoms to use GOT entries
329 for (std::vector<const ld::Atom*>::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) {
330 const ld::Atom* atom = *it;
331 const ld::Atom* targetOfGOT = NULL;
332 ld::Fixup::iterator fitThatSetTarget = NULL;
333 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
334 if ( fit->firstInCluster() ) {
335 targetOfGOT = NULL;
336 fitThatSetTarget = NULL;
337 }
338 switch ( fit->binding ) {
339 case ld::Fixup::bindingsIndirectlyBound:
340 targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
341 fitThatSetTarget = fit;
342 break;
343 case ld::Fixup::bindingDirectlyBound:
344 targetOfGOT = fit->u.target;
345 fitThatSetTarget = fit;
346 break;
347 default:
348 break;
349 }
350 bool optimizable;
351 if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) )
352 continue;
353 if ( !optimizable ) {
354 // GOT use not optimized away, update to bind to GOT entry
355 assert(fitThatSetTarget != NULL);
356 switch ( fitThatSetTarget->binding ) {
357 case ld::Fixup::bindingsIndirectlyBound:
358 case ld::Fixup::bindingDirectlyBound:
359 if ( log ) fprintf(stderr, "updating GOT use in %s to %s\n", atom->name(), targetOfGOT->name());
360 fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound;
361 fitThatSetTarget->u.target = gotMap[targetOfGOT];
362 break;
363 default:
364 assert(0 && "unsupported GOT reference");
365 break;
366 }
367 }
368 }
369 }
370
371 // sort new atoms so links are consistent
372 for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
373 ld::Internal::FinalSection* sect = *sit;
374 if ( sect->type() == ld::Section::typeNonLazyPointer ) {
375 std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter());
376 }
377 }
378 }
379
380
381 } // namespace got
382 } // namespace passes
383 } // namespace ld