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