]> git.saurik.com Git - apple/ld64.git/blame - src/ld/passes/got.cpp
ld64-274.2.tar.gz
[apple/ld64.git] / src / ld / passes / got.cpp
CommitLineData
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
39namespace ld {
40namespace passes {
41namespace got {
42
43class File; // forward reference
44
45class GOTEntryAtom : public ld::Atom {
46public:
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
65private:
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
74ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
eaf282aa 75ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer);
a645023d
A
76
77
eaf282aa 78static 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
152struct 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
160void 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