]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/got.cpp
3e6824f415af25663e82735d6046dd108c2a7427
[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 "ld.hpp"
35 #include "got.h"
36
37 namespace ld {
38 namespace passes {
39 namespace got {
40
41 class File; // forward reference
42
43 class GOTEntryAtom : public ld::Atom {
44 public:
45 GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport)
46 : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
47 ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
48 symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
49 _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target),
50 _target(target)
51 { _fixup.weakImport = weakImport; internal.addAtom(*this); }
52
53 virtual const ld::File* file() const { return NULL; }
54 virtual const char* name() const { return _target->name(); }
55 virtual uint64_t size() const { return 8; }
56 virtual uint64_t objectAddress() const { return 0; }
57 virtual void copyRawContent(uint8_t buffer[]) const { }
58 virtual void setScope(Scope) { }
59 virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; }
60 virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; }
61
62 private:
63 mutable ld::Fixup _fixup;
64 const ld::Atom* _target;
65
66 static ld::Section _s_section;
67 };
68
69 ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
70
71
72 static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable)
73 {
74 switch (fixup->kind) {
75 case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
76 // start by assuming this can be optimized
77 *optimizable = true;
78 // cannot do LEA optimization if target is in another dylib
79 if ( targetOfGOT->definition() == ld::Atom::definitionProxy )
80 *optimizable = false;
81 // cannot do LEA optimization if target in __huge section
82 if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024)
83 && ( (targetOfGOT->section().type() == ld::Section::typeZeroFill)
84 || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) {
85 *optimizable = false;
86 }
87 if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) {
88 // cannot do LEA optimization if target is weak exported symbol
89 if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) {
90 switch ( opts.outputKind() ) {
91 case Options::kDynamicExecutable:
92 case Options::kDynamicLibrary:
93 case Options::kDynamicBundle:
94 case Options::kKextBundle:
95 *optimizable = false;
96 break;
97 case Options::kStaticExecutable:
98 case Options::kDyld:
99 case Options::kPreload:
100 case Options::kObjectFile:
101 break;
102 }
103 }
104 // cannot do LEA optimization if target is interposable
105 if ( opts.interposable(targetOfGOT->name()) )
106 *optimizable = false;
107 // cannot do LEA optimization if target is resolver function
108 if ( targetOfGOT->contentType() == ld::Atom::typeResolver )
109 *optimizable = false;
110 // cannot do LEA optimization for flat-namespace
111 if ( opts.nameSpace() != Options::kTwoLevelNameSpace )
112 *optimizable = false;
113 }
114 else if ( targetOfGOT->scope() == ld::Atom::scopeLinkageUnit) {
115 // <rdar://problem/12379969> don't do optimization if target is in custom segment
116 if ( opts.sharedRegionEligible() ) {
117 const char* segName = targetOfGOT->section().segmentName();
118 if ( (strcmp(segName, "__TEXT") != 0) && (strcmp(segName, "__DATA") != 0) ) {
119 *optimizable = false;
120 }
121 }
122 }
123 return true;
124 case ld::Fixup::kindStoreX86PCRel32GOT:
125 *optimizable = false;
126 return true;
127 case ld::Fixup::kindNoneGroupSubordinatePersonality:
128 *optimizable = false;
129 return true;
130 default:
131 break;
132 }
133
134 return false;
135 }
136
137 struct AtomByNameSorter
138 {
139 bool operator()(const ld::Atom* left, const ld::Atom* right)
140 {
141 return (strcmp(left->name(), right->name()) < 0);
142 }
143 };
144
145 void doPass(const Options& opts, ld::Internal& internal)
146 {
147 const bool log = false;
148
149 // only make got section in final linked images
150 if ( opts.outputKind() == Options::kObjectFile )
151 return;
152
153 // walk all atoms and fixups looking for GOT-able references
154 // don't create GOT atoms during this loop because that could invalidate the sections iterator
155 std::vector<const ld::Atom*> atomsReferencingGOT;
156 std::map<const ld::Atom*,ld::Atom*> gotMap;
157 std::map<const ld::Atom*,bool> weakImportMap;
158 atomsReferencingGOT.reserve(128);
159 for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
160 ld::Internal::FinalSection* sect = *sit;
161 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
162 const ld::Atom* atom = *ait;
163 bool atomUsesGOT = false;
164 const ld::Atom* targetOfGOT = NULL;
165 bool targetIsWeakImport = false;
166 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
167 if ( fit->firstInCluster() )
168 targetOfGOT = NULL;
169 switch ( fit->binding ) {
170 case ld::Fixup::bindingsIndirectlyBound:
171 targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
172 targetIsWeakImport = fit->weakImport;
173 break;
174 case ld::Fixup::bindingDirectlyBound:
175 targetOfGOT = fit->u.target;
176 targetIsWeakImport = fit->weakImport;
177 break;
178 default:
179 break;
180 }
181 bool optimizable;
182 if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) )
183 continue;
184 if ( optimizable ) {
185 // change from load of GOT entry to lea of target
186 if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name());
187 switch ( fit->binding ) {
188 case ld::Fixup::bindingsIndirectlyBound:
189 case ld::Fixup::bindingDirectlyBound:
190 fit->binding = ld::Fixup::bindingDirectlyBound;
191 fit->u.target = targetOfGOT;
192 fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA;
193 break;
194 default:
195 assert(0 && "unsupported GOT reference");
196 break;
197 }
198 }
199 else {
200 // remember that we need to use GOT in this function
201 if ( log ) fprintf(stderr, "found GOT use in %s to %s\n", atom->name(), targetOfGOT->name());
202 if ( !atomUsesGOT ) {
203 atomsReferencingGOT.push_back(atom);
204 atomUsesGOT = true;
205 }
206 gotMap[targetOfGOT] = NULL;
207 // record weak_import attribute
208 std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT);
209 if ( pos == weakImportMap.end() ) {
210 // target not in weakImportMap, so add
211 if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport);
212 weakImportMap[targetOfGOT] = targetIsWeakImport;
213 }
214 else {
215 // target in weakImportMap, check for weakness mismatch
216 if ( pos->second != targetIsWeakImport ) {
217 // found mismatch
218 switch ( opts.weakReferenceMismatchTreatment() ) {
219 case Options::kWeakReferenceMismatchError:
220 throwf("mismatching weak references for symbol: %s", targetOfGOT->name());
221 case Options::kWeakReferenceMismatchWeak:
222 pos->second = true;
223 break;
224 case Options::kWeakReferenceMismatchNonWeak:
225 pos->second = false;
226 break;
227 }
228 }
229 }
230 }
231 }
232 }
233 }
234
235 // make GOT entries
236 for (std::map<const ld::Atom*,ld::Atom*>::iterator it = gotMap.begin(); it != gotMap.end(); ++it) {
237 it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first]);
238 }
239
240 // update atoms to use GOT entries
241 for (std::vector<const ld::Atom*>::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) {
242 const ld::Atom* atom = *it;
243 const ld::Atom* targetOfGOT = NULL;
244 ld::Fixup::iterator fitThatSetTarget = NULL;
245 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
246 if ( fit->firstInCluster() ) {
247 targetOfGOT = NULL;
248 fitThatSetTarget = NULL;
249 }
250 switch ( fit->binding ) {
251 case ld::Fixup::bindingsIndirectlyBound:
252 targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
253 fitThatSetTarget = fit;
254 break;
255 case ld::Fixup::bindingDirectlyBound:
256 targetOfGOT = fit->u.target;
257 fitThatSetTarget = fit;
258 break;
259 default:
260 break;
261 }
262 bool optimizable;
263 if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) )
264 continue;
265 if ( !optimizable ) {
266 // GOT use not optimized away, update to bind to GOT entry
267 assert(fitThatSetTarget != NULL);
268 switch ( fitThatSetTarget->binding ) {
269 case ld::Fixup::bindingsIndirectlyBound:
270 case ld::Fixup::bindingDirectlyBound:
271 fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound;
272 fitThatSetTarget->u.target = gotMap[targetOfGOT];
273 break;
274 default:
275 assert(0 && "unsupported GOT reference");
276 break;
277 }
278 }
279 }
280 }
281
282 // sort new atoms so links are consistent
283 for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
284 ld::Internal::FinalSection* sect = *sit;
285 if ( sect->type() == ld::Section::typeNonLazyPointer ) {
286 std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter());
287 }
288 }
289 }
290
291
292 } // namespace got
293 } // namespace passes
294 } // namespace ld