]>
Commit | Line | Data |
---|---|---|
bac542e6 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
412ebb8e | 3 | * Copyright (c) 2006-2011 Apple Inc. All rights reserved. |
bac542e6 A |
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 | #ifndef __MACHO_BINDER__ | |
26 | #define __MACHO_BINDER__ | |
27 | ||
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <sys/mman.h> | |
31 | #include <mach/mach.h> | |
32 | #include <limits.h> | |
33 | #include <stdarg.h> | |
34 | #include <stdio.h> | |
35 | #include <fcntl.h> | |
36 | #include <errno.h> | |
37 | #include <unistd.h> | |
38 | #include <mach-o/loader.h> | |
39 | #include <mach-o/fat.h> | |
40 | ||
41 | #include <vector> | |
42 | #include <set> | |
2fd3f4e8 A |
43 | #include <unordered_map> |
44 | #include <unordered_set> | |
bac542e6 A |
45 | |
46 | #include "MachOFileAbstraction.hpp" | |
47 | #include "Architectures.hpp" | |
48 | #include "MachOLayout.hpp" | |
49 | #include "MachORebaser.hpp" | |
39a8cd10 | 50 | #include "MachOTrie.hpp" |
bac542e6 | 51 | |
412ebb8e A |
52 | #ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER |
53 | #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 | |
54 | #endif | |
bac542e6 | 55 | |
2fd3f4e8 A |
56 | #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE |
57 | #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 | |
58 | #endif | |
bac542e6 A |
59 | |
60 | ||
61 | template <typename A> | |
62 | class Binder : public Rebaser<A> | |
63 | { | |
64 | public: | |
2fd3f4e8 A |
65 | class CStringHash { |
66 | public: | |
67 | size_t operator()(const char* __s) const { | |
68 | size_t __h = 0; | |
69 | for ( ; *__s; ++__s) | |
70 | __h = 5 * __h + *__s; | |
71 | return __h; | |
72 | }; | |
73 | }; | |
bac542e6 A |
74 | struct CStringEquals { |
75 | bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } | |
76 | }; | |
2fd3f4e8 | 77 | typedef std::unordered_map<const char*, class Binder<A>*, CStringHash, CStringEquals> Map; |
bac542e6 A |
78 | |
79 | ||
df9d6cf7 | 80 | Binder(const MachOLayoutAbstraction&); |
bac542e6 A |
81 | virtual ~Binder() {} |
82 | ||
83 | const char* getDylibID() const; | |
84 | void setDependentBinders(const Map& map); | |
412ebb8e A |
85 | void bind(std::vector<void*>&); |
86 | void optimize(); | |
87 | void addResolverClient(Binder<A>* clientDylib, const char* symbolName); | |
bac542e6 A |
88 | private: |
89 | typedef typename A::P P; | |
90 | typedef typename A::P::E E; | |
91 | typedef typename A::P::uint_t pint_t; | |
92 | struct BinderAndReExportFlag { Binder<A>* binder; bool reExport; }; | |
412ebb8e | 93 | struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; }; |
2fd3f4e8 A |
94 | typedef std::unordered_map<const char*, pint_t, CStringHash, CStringEquals> NameToAddrMap; |
95 | typedef std::unordered_set<const char*, CStringHash, CStringEquals> NameSet; | |
df9d6cf7 A |
96 | typedef std::unordered_map<const char*, std::set<Binder<A>*>, CStringHash, CStringEquals> ResolverClientsMap; |
97 | ||
bac542e6 | 98 | |
412ebb8e | 99 | static bool isPublicLocation(const char* pth); |
412ebb8e A |
100 | void hoistPrivateRexports(); |
101 | int ordinalOfDependentBinder(Binder<A>* dep); | |
102 | void doBindDyldInfo(std::vector<void*>& pointersInData); | |
103 | void doBindDyldLazyInfo(std::vector<void*>& pointersInData); | |
39a8cd10 | 104 | void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, |
412ebb8e | 105 | int libraryOrdinal, int64_t addend, |
832b6fce | 106 | const char* symbolName, bool lazyPointer, bool weakImport, |
412ebb8e | 107 | std::vector<void*>& pointersInData); |
2fd3f4e8 A |
108 | bool findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, |
109 | bool* isResolverSymbol, bool* isAbsolute); | |
bac542e6 | 110 | const char* parentUmbrella(); |
412ebb8e | 111 | pint_t runtimeAddressFromNList(const macho_nlist<P>* sym); |
df9d6cf7 A |
112 | void switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr); |
113 | void switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); | |
412ebb8e | 114 | pint_t findLazyPointerFor(const char* symbolName); |
df9d6cf7 | 115 | void shareLazyPointersToResolvers(); |
412ebb8e | 116 | |
bac542e6 A |
117 | |
118 | static uint8_t pointerRelocSize(); | |
119 | static uint8_t pointerRelocType(); | |
120 | ||
121 | std::vector<BinderAndReExportFlag> fDependentDylibs; | |
39a8cd10 | 122 | NameToAddrMap fHashTable; |
412ebb8e | 123 | NameSet fSymbolResolvers; |
2fd3f4e8 | 124 | NameSet fAbsoluteSymbols; |
412ebb8e | 125 | std::vector<SymbolReExport> fReExportedSymbols; |
bac542e6 A |
126 | const macho_nlist<P>* fSymbolTable; |
127 | const char* fStrings; | |
128 | const macho_dysymtab_command<P>* fDynamicInfo; | |
129 | const macho_segment_command<P>* fFristWritableSegment; | |
130 | const macho_dylib_command<P>* fDylibID; | |
131 | const macho_dylib_command<P>* fParentUmbrella; | |
39a8cd10 | 132 | const macho_dyld_info_command<P>* fDyldInfo; |
bac542e6 | 133 | bool fOriginallyPrebound; |
412ebb8e | 134 | bool fReExportedSymbolsResolved; |
df9d6cf7 | 135 | ResolverClientsMap fResolverInfo; |
bac542e6 A |
136 | }; |
137 | ||
412ebb8e A |
138 | template <> |
139 | uint32_t Binder<arm>::runtimeAddressFromNList(const macho_nlist<Pointer32<LittleEndian> >* sym) | |
140 | { | |
141 | if (sym->n_desc() & N_ARM_THUMB_DEF) | |
142 | return sym->n_value() + 1; | |
143 | else | |
144 | return sym->n_value(); | |
145 | } | |
146 | ||
147 | template <typename A> | |
148 | typename A::P::uint_t Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* sym) | |
149 | { | |
150 | return sym->n_value(); | |
151 | } | |
152 | ||
bac542e6 A |
153 | |
154 | template <typename A> | |
df9d6cf7 A |
155 | Binder<A>::Binder(const MachOLayoutAbstraction& layout) |
156 | : Rebaser<A>(layout), | |
bac542e6 | 157 | fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), |
39a8cd10 | 158 | fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL), |
412ebb8e | 159 | fParentUmbrella(NULL), fReExportedSymbolsResolved(false) |
bac542e6 A |
160 | { |
161 | fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0); | |
162 | // update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit) | |
163 | ((macho_header<P>*)this->fHeader)->set_flags(this->fHeader->flags() | MH_PREBOUND | MH_SPLIT_SEGS | 0x80000000); | |
164 | ||
165 | // calculate fDynamicInfo, fStrings, fSymbolTable | |
2028a915 | 166 | const macho_symtab_command<P>* symtab; |
bac542e6 A |
167 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>)); |
168 | const uint32_t cmd_count = this->fHeader->ncmds(); | |
169 | const macho_load_command<P>* cmd = cmds; | |
170 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
171 | switch (cmd->cmd()) { | |
172 | case LC_SYMTAB: | |
2028a915 | 173 | symtab = (macho_symtab_command<P>*)cmd; |
bac542e6 A |
174 | fSymbolTable = (macho_nlist<P>*)(&this->fLinkEditBase[symtab->symoff()]); |
175 | fStrings = (const char*)&this->fLinkEditBase[symtab->stroff()]; | |
176 | break; | |
177 | case LC_DYSYMTAB: | |
178 | fDynamicInfo = (macho_dysymtab_command<P>*)cmd; | |
179 | break; | |
180 | case LC_ID_DYLIB: | |
181 | ((macho_dylib_command<P>*)cmd)->set_timestamp(0); | |
182 | fDylibID = (macho_dylib_command<P>*)cmd; | |
183 | break; | |
184 | case LC_LOAD_DYLIB: | |
185 | case LC_LOAD_WEAK_DYLIB: | |
186 | case LC_REEXPORT_DYLIB: | |
412ebb8e | 187 | case LC_LOAD_UPWARD_DYLIB: |
bac542e6 A |
188 | ((macho_dylib_command<P>*)cmd)->set_timestamp(0); |
189 | break; | |
190 | case LC_SUB_FRAMEWORK: | |
191 | fParentUmbrella = (macho_dylib_command<P>*)cmd; | |
192 | break; | |
39a8cd10 A |
193 | case LC_DYLD_INFO: |
194 | case LC_DYLD_INFO_ONLY: | |
195 | fDyldInfo = (macho_dyld_info_command<P>*)cmd; | |
196 | break; | |
197 | case LC_RPATH: | |
df9d6cf7 | 198 | fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath()); |
39a8cd10 | 199 | break; |
bac542e6 A |
200 | default: |
201 | if ( cmd->cmd() & LC_REQ_DYLD ) | |
39a8cd10 | 202 | throwf("unknown required load command 0x%08X", cmd->cmd()); |
bac542e6 A |
203 | } |
204 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
205 | } | |
206 | if ( fDynamicInfo == NULL ) | |
207 | throw "no LC_DYSYMTAB"; | |
208 | if ( fSymbolTable == NULL ) | |
209 | throw "no LC_SYMTAB"; | |
df9d6cf7 A |
210 | if ( fDyldInfo == NULL ) |
211 | throw "no LC_DYLD_INFO"; | |
bac542e6 | 212 | // build hash table |
df9d6cf7 A |
213 | //fprintf(stderr, "exports for %s\n", layout.getFilePath()); |
214 | std::vector<mach_o::trie::Entry> exports; | |
215 | const uint8_t* exportsStart = layout.getDyldInfoExports(); | |
216 | const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; | |
217 | mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); | |
218 | pint_t baseAddress = layout.getSegments()[0].newAddress(); | |
219 | for(std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) { | |
220 | switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { | |
221 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: | |
222 | if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { | |
223 | fSymbolResolvers.insert(it->name); | |
224 | } | |
225 | if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { | |
226 | //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID()); | |
227 | SymbolReExport sym; | |
228 | sym.exportName = it->name; | |
229 | sym.dylibOrdinal = it->other; | |
230 | sym.importName = it->importName; | |
231 | if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) | |
232 | sym.importName = sym.exportName; | |
233 | fReExportedSymbols.push_back(sym); | |
234 | // fHashTable entry will be added in first call to findExportedSymbolAddress() | |
235 | } | |
236 | else { | |
412ebb8e | 237 | fHashTable[it->name] = it->address + baseAddress; |
df9d6cf7 A |
238 | } |
239 | break; | |
240 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: | |
241 | fHashTable[it->name] = it->address + baseAddress; | |
242 | break; | |
243 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: | |
244 | fHashTable[it->name] = it->address; | |
245 | fAbsoluteSymbols.insert(it->name); | |
246 | break; | |
247 | default: | |
248 | throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); | |
249 | break; | |
bac542e6 | 250 | } |
df9d6cf7 | 251 | //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); |
bac542e6 | 252 | } |
bac542e6 A |
253 | } |
254 | ||
bac542e6 A |
255 | template <> uint8_t Binder<x86>::pointerRelocSize() { return 2; } |
256 | template <> uint8_t Binder<x86_64>::pointerRelocSize() { return 3; } | |
39a8cd10 | 257 | template <> uint8_t Binder<arm>::pointerRelocSize() { return 2; } |
19894a12 | 258 | template <> uint8_t Binder<arm64>::pointerRelocSize() { return 3; } |
bac542e6 | 259 | |
bac542e6 A |
260 | template <> uint8_t Binder<x86>::pointerRelocType() { return GENERIC_RELOC_VANILLA; } |
261 | template <> uint8_t Binder<x86_64>::pointerRelocType() { return X86_64_RELOC_UNSIGNED; } | |
39a8cd10 | 262 | template <> uint8_t Binder<arm>::pointerRelocType() { return ARM_RELOC_VANILLA; } |
19894a12 | 263 | template <> uint8_t Binder<arm64>::pointerRelocType() { return ARM64_RELOC_UNSIGNED; } |
bac542e6 A |
264 | |
265 | ||
266 | template <typename A> | |
267 | const char* Binder<A>::getDylibID() const | |
268 | { | |
269 | if ( fDylibID != NULL ) | |
270 | return fDylibID->name(); | |
271 | else | |
272 | return NULL; | |
273 | } | |
274 | ||
275 | template <typename A> | |
276 | const char* Binder<A>::parentUmbrella() | |
277 | { | |
278 | if ( fParentUmbrella != NULL ) | |
279 | return fParentUmbrella->name(); | |
280 | else | |
281 | return NULL; | |
282 | } | |
283 | ||
284 | ||
412ebb8e A |
285 | template <typename A> |
286 | bool Binder<A>::isPublicLocation(const char* pth) | |
287 | { | |
288 | // /usr/lib is a public location | |
289 | if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) | |
290 | return true; | |
291 | ||
292 | // /System/Library/Frameworks/ is a public location | |
293 | if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { | |
294 | const char* frameworkDot = strchr(&pth[27], '.'); | |
295 | // but only top level framework | |
296 | // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true | |
297 | // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false | |
298 | // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false | |
299 | // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false | |
300 | if ( frameworkDot != NULL ) { | |
301 | int frameworkNameLen = frameworkDot - &pth[27]; | |
302 | if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) | |
303 | return true; | |
304 | } | |
305 | } | |
306 | ||
307 | return false; | |
308 | } | |
bac542e6 A |
309 | |
310 | template <typename A> | |
311 | void Binder<A>::setDependentBinders(const Map& map) | |
312 | { | |
df9d6cf7 | 313 | // build vector of dependent dylibs |
bac542e6 A |
314 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>)); |
315 | const uint32_t cmd_count = this->fHeader->ncmds(); | |
316 | const macho_load_command<P>* cmd = cmds; | |
317 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
318 | switch (cmd->cmd()) { | |
319 | case LC_LOAD_DYLIB: | |
320 | case LC_LOAD_WEAK_DYLIB: | |
321 | case LC_REEXPORT_DYLIB: | |
412ebb8e | 322 | case LC_LOAD_UPWARD_DYLIB: |
bac542e6 A |
323 | const char* path = ((struct macho_dylib_command<P>*)cmd)->name(); |
324 | typename Map::const_iterator pos = map.find(path); | |
325 | if ( pos != map.end() ) { | |
326 | BinderAndReExportFlag entry; | |
327 | entry.binder = pos->second; | |
328 | entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); | |
329 | fDependentDylibs.push_back(entry); | |
330 | } | |
331 | else { | |
332 | // the load command string does not match the install name of any loaded dylib | |
333 | // this could happen if there was not a world build and some dylib changed its | |
334 | // install path to be some symlinked path | |
335 | ||
336 | // use realpath() and walk map looking for a realpath match | |
337 | bool found = false; | |
338 | char targetPath[PATH_MAX]; | |
339 | if ( realpath(path, targetPath) != NULL ) { | |
340 | for(typename Map::const_iterator it=map.begin(); it != map.end(); ++it) { | |
341 | char aPath[PATH_MAX]; | |
342 | if ( realpath(it->first, aPath) != NULL ) { | |
343 | if ( strcmp(targetPath, aPath) == 0 ) { | |
344 | BinderAndReExportFlag entry; | |
345 | entry.binder = it->second; | |
346 | entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); | |
347 | fDependentDylibs.push_back(entry); | |
348 | found = true; | |
349 | fprintf(stderr, "update_dyld_shared_cache: warning mismatched install path in %s for %s\n", | |
350 | this->getDylibID(), path); | |
351 | break; | |
352 | } | |
353 | } | |
354 | } | |
355 | } | |
832b6fce A |
356 | if ( ! found ) { |
357 | if ( cmd->cmd() == LC_LOAD_WEAK_DYLIB ) { | |
358 | BinderAndReExportFlag entry; | |
359 | entry.binder = NULL; | |
360 | entry.reExport = false; | |
361 | fDependentDylibs.push_back(entry); | |
362 | break; | |
363 | } | |
364 | else { | |
365 | throwf("in %s can't find dylib %s", this->getDylibID(), path); | |
366 | } | |
367 | } | |
bac542e6 A |
368 | } |
369 | break; | |
370 | } | |
371 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
df9d6cf7 | 372 | } |
412ebb8e A |
373 | } |
374 | ||
375 | template <typename A> | |
376 | int Binder<A>::ordinalOfDependentBinder(Binder<A>* dep) | |
377 | { | |
378 | for (int i=0; i < fDependentDylibs.size(); ++i) { | |
379 | if ( fDependentDylibs[i].binder == dep ) | |
380 | return i+1; | |
381 | } | |
382 | throw "dependend dylib not found"; | |
383 | } | |
384 | ||
385 | template <typename A> | |
386 | void Binder<A>::hoistPrivateRexports() | |
387 | { | |
388 | std::vector<Binder<A>*> privateReExportedDylibs; | |
389 | for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { | |
390 | if ( it->reExport && ! isPublicLocation(it->binder->getDylibID()) ) | |
391 | privateReExportedDylibs.push_back(it->binder); | |
392 | } | |
393 | if ( privateReExportedDylibs.size() != 0 ) { | |
394 | // parse export info into vector of exports | |
395 | const uint8_t* exportsStart = this->fLayout.getDyldInfoExports(); | |
396 | const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; | |
397 | std::vector<mach_o::trie::Entry> exports; | |
398 | mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); | |
399 | //fprintf(stderr, "%s exports %lu symbols from trie of size %u \n", this->fLayout.getFilePath(), exports.size(), fDyldInfo->export_size()); | |
400 | ||
401 | // add re-exports for each export from an re-exported dylib | |
402 | for(typename std::vector<Binder<A>*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) { | |
403 | Binder<A>* binder = *it; | |
404 | int ordinal = ordinalOfDependentBinder(binder); | |
405 | const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports(); | |
406 | const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()]; | |
407 | std::vector<mach_o::trie::Entry> aDylibsExports; | |
408 | mach_o::trie::parseTrie(aDylibsExportsStart, aDylibsExportsEnd, aDylibsExports); | |
409 | //fprintf(stderr, "%s re-exports %lu symbols from %s\n", this->fLayout.getFilePath(), aDylibsExports.size(), binder->getDylibID()); | |
410 | for(std::vector<mach_o::trie::Entry>::iterator eit = aDylibsExports.begin(); eit != aDylibsExports.end(); ++eit) { | |
411 | mach_o::trie::Entry entry = *eit; | |
412 | entry.flags |= EXPORT_SYMBOL_FLAGS_REEXPORT; | |
413 | entry.other = ordinal; | |
414 | entry.importName = NULL; | |
415 | exports.push_back(entry); | |
416 | } | |
417 | } | |
418 | // rebuild new combined trie | |
419 | std::vector<uint8_t> newExportTrieBytes; | |
420 | newExportTrieBytes.reserve(fDyldInfo->export_size()); | |
421 | mach_o::trie::makeTrie(exports, newExportTrieBytes); | |
422 | //fprintf(stderr, "%s now exports %lu symbols from trie of size %lu\n", this->fLayout.getFilePath(), exports.size(), newExportTrieBytes.size()); | |
423 | ||
424 | // allocate new buffer and set export_off to use new buffer instead | |
425 | uint32_t newExportsSize = newExportTrieBytes.size(); | |
426 | uint8_t* sideTrie = new uint8_t[newExportsSize]; | |
427 | memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); | |
428 | this->fLayout.setDyldInfoExports(sideTrie); | |
429 | ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie | |
430 | ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize); | |
431 | } | |
bac542e6 A |
432 | } |
433 | ||
412ebb8e | 434 | |
bac542e6 | 435 | template <typename A> |
412ebb8e | 436 | void Binder<A>::bind(std::vector<void*>& pointersInData) |
bac542e6 | 437 | { |
df9d6cf7 A |
438 | this->doBindDyldInfo(pointersInData); |
439 | this->doBindDyldLazyInfo(pointersInData); | |
440 | // weak bind info is processed at launch time | |
bac542e6 A |
441 | } |
442 | ||
39a8cd10 | 443 | template <typename A> |
412ebb8e | 444 | void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, |
832b6fce | 445 | int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector<void*>& pointersInData) |
39a8cd10 A |
446 | { |
447 | //printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); | |
448 | const std::vector<MachOLayoutAbstraction::Segment>& segments = this->fLayout.getSegments(); | |
449 | if ( segmentIndex > segments.size() ) | |
450 | throw "bad segment index in rebase info"; | |
451 | ||
452 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) | |
412ebb8e | 453 | throw "dynamic lookup linkage not allowed in dyld shared cache"; |
39a8cd10 A |
454 | |
455 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) | |
456 | throw "linkage to main executable not allowed in dyld shared cache"; | |
457 | ||
458 | if ( libraryOrdinal < 0 ) | |
459 | throw "bad mach-o binary, special library ordinal not allowd in dyld shared cache"; | |
460 | ||
461 | if ( (unsigned)libraryOrdinal > fDependentDylibs.size() ) | |
462 | throw "bad mach-o binary, library ordinal too big"; | |
463 | ||
464 | Binder<A>* binder; | |
465 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) | |
466 | binder = this; | |
467 | else | |
468 | binder = fDependentDylibs[libraryOrdinal-1].binder; | |
469 | pint_t targetSymbolAddress; | |
2fd3f4e8 A |
470 | bool isResolverSymbol = false; |
471 | bool isAbsolute = false; | |
412ebb8e | 472 | Binder<A>* foundIn; |
832b6fce A |
473 | if ( weakImport && (binder == NULL) ) { |
474 | targetSymbolAddress = 0; | |
475 | foundIn = NULL; | |
832b6fce A |
476 | } |
477 | else { | |
2fd3f4e8 | 478 | if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) ) |
832b6fce A |
479 | throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); |
480 | } | |
481 | ||
412ebb8e A |
482 | // don't bind lazy pointers to resolver stubs in shared cache |
483 | if ( lazyPointer && isResolverSymbol ) { | |
2fd3f4e8 A |
484 | if ( foundIn != this ) { |
485 | // record that this dylib has a lazy pointer to a resolver function | |
412ebb8e A |
486 | foundIn->addResolverClient(this, symbolName); |
487 | // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID()); | |
488 | } | |
489 | return; | |
490 | } | |
39a8cd10 A |
491 | |
492 | // do actual update | |
493 | const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex]; | |
494 | uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segmentOffset; | |
495 | pint_t* mappedAddrP = (pint_t*)mappedAddr; | |
496 | uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; | |
497 | int32_t svalue32new; | |
498 | switch ( type ) { | |
499 | case BIND_TYPE_POINTER: | |
500 | P::setP(*mappedAddrP, targetSymbolAddress + addend); | |
501 | break; | |
502 | ||
503 | case BIND_TYPE_TEXT_ABSOLUTE32: | |
504 | E::set32(*mappedAddr32, targetSymbolAddress + addend); | |
505 | break; | |
506 | ||
507 | case BIND_TYPE_TEXT_PCREL32: | |
508 | svalue32new = seg.address() + segmentOffset + 4 - (targetSymbolAddress + addend); | |
509 | E::set32(*mappedAddr32, svalue32new); | |
510 | break; | |
511 | ||
512 | default: | |
513 | throw "bad bind type"; | |
514 | } | |
2fd3f4e8 A |
515 | if ( !isAbsolute ) |
516 | pointersInData.push_back(mappedAddr); | |
39a8cd10 A |
517 | } |
518 | ||
519 | ||
520 | ||
521 | template <typename A> | |
412ebb8e | 522 | void Binder<A>::doBindDyldLazyInfo(std::vector<void*>& pointersInData) |
39a8cd10 A |
523 | { |
524 | const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()]; | |
525 | const uint8_t* end = &p[fDyldInfo->lazy_bind_size()]; | |
526 | ||
527 | uint8_t type = BIND_TYPE_POINTER; | |
528 | uint64_t segmentOffset = 0; | |
529 | uint8_t segmentIndex = 0; | |
530 | const char* symbolName = NULL; | |
531 | int libraryOrdinal = 0; | |
532 | int64_t addend = 0; | |
832b6fce | 533 | bool weakImport = false; |
39a8cd10 A |
534 | while ( p < end ) { |
535 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
536 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
537 | ++p; | |
538 | switch (opcode) { | |
539 | case BIND_OPCODE_DONE: | |
540 | // this opcode marks the end of each lazy pointer binding | |
541 | break; | |
542 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
543 | libraryOrdinal = immediate; | |
544 | break; | |
545 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
546 | libraryOrdinal = read_uleb128(p, end); | |
547 | break; | |
548 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
549 | // the special ordinals are negative numbers | |
550 | if ( immediate == 0 ) | |
551 | libraryOrdinal = 0; | |
552 | else { | |
553 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
554 | libraryOrdinal = signExtended; | |
555 | } | |
556 | break; | |
557 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
832b6fce | 558 | weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); |
39a8cd10 A |
559 | symbolName = (char*)p; |
560 | while (*p != '\0') | |
561 | ++p; | |
562 | ++p; | |
563 | break; | |
564 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
565 | addend = read_sleb128(p, end); | |
566 | break; | |
567 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
568 | segmentIndex = immediate; | |
569 | segmentOffset = read_uleb128(p, end); | |
570 | break; | |
571 | case BIND_OPCODE_DO_BIND: | |
832b6fce | 572 | bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, pointersInData); |
39a8cd10 A |
573 | segmentOffset += sizeof(pint_t); |
574 | break; | |
575 | case BIND_OPCODE_SET_TYPE_IMM: | |
576 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
577 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
578 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
579 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
580 | default: | |
581 | throwf("bad lazy bind opcode %d", *p); | |
582 | } | |
583 | } | |
584 | ||
585 | ||
586 | } | |
587 | ||
588 | template <typename A> | |
412ebb8e | 589 | void Binder<A>::doBindDyldInfo(std::vector<void*>& pointersInData) |
39a8cd10 A |
590 | { |
591 | const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()]; | |
592 | const uint8_t* end = &p[fDyldInfo->bind_size()]; | |
593 | ||
594 | uint8_t type = 0; | |
595 | uint64_t segmentOffset = 0; | |
596 | uint8_t segmentIndex = 0; | |
597 | const char* symbolName = NULL; | |
598 | int libraryOrdinal = 0; | |
599 | int64_t addend = 0; | |
600 | uint32_t count; | |
601 | uint32_t skip; | |
832b6fce | 602 | bool weakImport = false; |
39a8cd10 A |
603 | bool done = false; |
604 | while ( !done && (p < end) ) { | |
605 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
606 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
607 | ++p; | |
608 | switch (opcode) { | |
609 | case BIND_OPCODE_DONE: | |
610 | done = true; | |
611 | break; | |
612 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
613 | libraryOrdinal = immediate; | |
614 | break; | |
615 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
616 | libraryOrdinal = read_uleb128(p, end); | |
617 | break; | |
618 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
619 | // the special ordinals are negative numbers | |
620 | if ( immediate == 0 ) | |
621 | libraryOrdinal = 0; | |
622 | else { | |
623 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
624 | libraryOrdinal = signExtended; | |
625 | } | |
626 | break; | |
627 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
832b6fce | 628 | weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); |
39a8cd10 A |
629 | symbolName = (char*)p; |
630 | while (*p != '\0') | |
631 | ++p; | |
632 | ++p; | |
633 | break; | |
634 | case BIND_OPCODE_SET_TYPE_IMM: | |
635 | type = immediate; | |
636 | break; | |
637 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
638 | addend = read_sleb128(p, end); | |
639 | break; | |
640 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
641 | segmentIndex = immediate; | |
642 | segmentOffset = read_uleb128(p, end); | |
643 | break; | |
644 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
645 | segmentOffset += read_uleb128(p, end); | |
646 | break; | |
647 | case BIND_OPCODE_DO_BIND: | |
832b6fce | 648 | bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); |
39a8cd10 A |
649 | segmentOffset += sizeof(pint_t); |
650 | break; | |
651 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
832b6fce | 652 | bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); |
39a8cd10 A |
653 | segmentOffset += read_uleb128(p, end) + sizeof(pint_t); |
654 | break; | |
655 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
832b6fce | 656 | bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); |
39a8cd10 A |
657 | segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t); |
658 | break; | |
659 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
660 | count = read_uleb128(p, end); | |
661 | skip = read_uleb128(p, end); | |
662 | for (uint32_t i=0; i < count; ++i) { | |
832b6fce | 663 | bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); |
39a8cd10 A |
664 | segmentOffset += skip + sizeof(pint_t); |
665 | } | |
666 | break; | |
667 | default: | |
668 | throwf("bad bind opcode %d", *p); | |
669 | } | |
bac542e6 A |
670 | } |
671 | } | |
672 | ||
673 | template <typename A> | |
2fd3f4e8 | 674 | bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, bool* isResolverSymbol, bool* isAbsolute) |
bac542e6 | 675 | { |
412ebb8e A |
676 | *foundIn = NULL; |
677 | // since re-export chains can be any length, re-exports cannot be resolved in setDependencies() | |
678 | // instead we lazily, recursively update | |
679 | if ( !fReExportedSymbolsResolved ) { | |
680 | ||
681 | // update fHashTable with any individual symbol re-exports | |
682 | for (typename std::vector<SymbolReExport>::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { | |
683 | pint_t targetSymbolAddress; | |
684 | bool isResolver; | |
2fd3f4e8 | 685 | bool isAb; |
412ebb8e A |
686 | |
687 | if ( it->dylibOrdinal <= 0 ) | |
688 | throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; | |
689 | ||
690 | Binder<A>* binder = fDependentDylibs[it->dylibOrdinal-1].binder; | |
691 | ||
2fd3f4e8 | 692 | if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver, &isAb) ) |
412ebb8e A |
693 | throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID()); |
694 | ||
2fd3f4e8 A |
695 | if ( isResolver ) |
696 | fSymbolResolvers.insert(name); | |
412ebb8e A |
697 | |
698 | fHashTable[it->exportName] = targetSymbolAddress; | |
699 | } | |
700 | // mark as done | |
701 | fReExportedSymbolsResolved = true; | |
702 | } | |
703 | ||
704 | *isResolverSymbol = false; | |
705 | if ( !fSymbolResolvers.empty() && fSymbolResolvers.count(name) ) { | |
706 | // lazy pointers should be left unbound, rather than bind to resolver stub | |
707 | *isResolverSymbol = true; | |
708 | } | |
709 | ||
2fd3f4e8 | 710 | // search this dylib |
39a8cd10 A |
711 | typename NameToAddrMap::iterator pos = fHashTable.find(name); |
712 | if ( pos != fHashTable.end() ) { | |
713 | *result = pos->second; | |
714 | //fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID()); | |
412ebb8e | 715 | *foundIn = this; |
2fd3f4e8 | 716 | *isAbsolute = (fAbsoluteSymbols.count(name) != 0); |
39a8cd10 A |
717 | return true; |
718 | } | |
bac542e6 | 719 | |
2fd3f4e8 | 720 | // search re-exported dylibs |
bac542e6 A |
721 | for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { |
722 | if ( it->reExport ) { | |
2fd3f4e8 | 723 | if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol, isAbsolute) ) |
39a8cd10 | 724 | return true; |
bac542e6 A |
725 | } |
726 | } | |
39a8cd10 A |
727 | //fprintf(stderr, "findExportedSymbolAddress(%s) => not found in %s\n", name, this->getDylibID()); |
728 | return false; | |
bac542e6 A |
729 | } |
730 | ||
412ebb8e A |
731 | // record which dylibs will be using this dylibs lazy pointer |
732 | template <typename A> | |
733 | void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName) | |
734 | { | |
df9d6cf7 | 735 | fResolverInfo[symbolName].insert(clientDylib); |
412ebb8e A |
736 | } |
737 | ||
412ebb8e A |
738 | |
739 | template <typename A> | |
740 | typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName) | |
741 | { | |
2fd3f4e8 | 742 | static const bool log = false; |
df9d6cf7 A |
743 | |
744 | // lookup in lazy pointer section | |
2fd3f4e8 A |
745 | const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; |
746 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>)); | |
747 | const uint32_t cmd_count = this->fHeader->ncmds(); | |
748 | const macho_load_command<P>* cmd = cmds; | |
749 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
750 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
751 | const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
752 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>)); | |
753 | const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
754 | for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
755 | uint8_t sectionType = sect->flags() & SECTION_TYPE; | |
756 | if ( sectionType == S_LAZY_SYMBOL_POINTERS) { | |
757 | uint32_t elementCount = sect->size() / sizeof(pint_t); | |
758 | const uint32_t indirectTableOffset = sect->reserved1(); | |
759 | pint_t vmlocation = sect->addr(); | |
760 | for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) { | |
761 | uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); | |
762 | switch ( symbolIndex ) { | |
763 | case INDIRECT_SYMBOL_ABS: | |
764 | case INDIRECT_SYMBOL_LOCAL: | |
765 | break; | |
766 | default: | |
767 | const macho_nlist<P>* aSymbol = &fSymbolTable[symbolIndex]; | |
768 | const char* aName = &fStrings[aSymbol->n_strx()]; | |
769 | //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]); | |
770 | if ( strcmp(aName, symbolName) == 0 ) { | |
df9d6cf7 | 771 | if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID()); |
2fd3f4e8 A |
772 | return vmlocation; |
773 | } | |
774 | break; | |
775 | } | |
776 | } | |
777 | } | |
778 | } | |
779 | } | |
780 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
781 | } | |
19894a12 A |
782 | |
783 | if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking for re-export symbol\n", symbolName, this->getDylibID()); | |
784 | for (typename std::vector<SymbolReExport>::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { | |
785 | if ( strcmp(it->exportName, symbolName) != 0 ) | |
786 | continue; | |
787 | ||
788 | if ( it->dylibOrdinal <= 0 ) | |
789 | throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; | |
790 | ||
791 | Binder<A>* binder = fDependentDylibs[it->dylibOrdinal-1].binder; | |
792 | return binder->findLazyPointerFor(it->importName); | |
793 | } | |
794 | ||
795 | if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking re-export dylibs\n", symbolName, this->getDylibID()); | |
796 | for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { | |
797 | if ( it->reExport ) { | |
798 | pint_t result = it->binder->findLazyPointerFor(symbolName); | |
799 | if ( result != 0 ) | |
800 | return result; | |
801 | } | |
802 | } | |
803 | ||
2fd3f4e8 | 804 | if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID()); |
412ebb8e A |
805 | return 0; |
806 | } | |
807 | ||
808 | // called after all binding is done to optimize lazy pointers | |
809 | template <typename A> | |
810 | void Binder<A>::optimize() | |
811 | { | |
df9d6cf7 A |
812 | hoistPrivateRexports(); |
813 | shareLazyPointersToResolvers(); | |
412ebb8e A |
814 | } |
815 | ||
df9d6cf7 A |
816 | template <typename A> |
817 | void Binder<A>::shareLazyPointersToResolvers() | |
818 | { | |
819 | for (const auto &entry : fResolverInfo) { | |
820 | const char* resolverSymbolName = entry.first; | |
821 | if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) { | |
822 | for (Binder<A>* clientDylib : entry.second) { | |
823 | clientDylib->switchStubToUseSharedLazyPointer(resolverSymbolName, lpVMAddr); | |
824 | } | |
825 | } | |
826 | else { | |
827 | fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", resolverSymbolName, this->getDylibID()); | |
828 | } | |
829 | } | |
830 | } | |
831 | ||
832 | ||
833 | ||
412ebb8e | 834 | template <> |
df9d6cf7 | 835 | void Binder<arm>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) |
412ebb8e A |
836 | { |
837 | if ( stubSize != 16 ) { | |
838 | fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID()); | |
839 | return; | |
840 | } | |
841 | uint32_t* instructions = (uint32_t*)stubMappedAddress; | |
842 | if ( (E::get32(instructions[0]) != 0xe59fc004) | |
843 | || (E::get32(instructions[1]) != 0xe08fc00c) | |
844 | || (E::get32(instructions[2]) != 0xe59cf000) | |
845 | ) { | |
846 | fprintf(stderr, "could not optimize ARM stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); | |
847 | return; | |
848 | } | |
849 | // last .long in stub is: lazyPtr - (stub+8) | |
850 | // alter to point to more optimal lazy pointer | |
851 | uint32_t betterOffset = lpVMAddr - (stubVMAddress + 12); | |
852 | E::set32(instructions[3], betterOffset); | |
853 | } | |
854 | ||
855 | ||
856 | template <> | |
df9d6cf7 | 857 | void Binder<x86_64>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) |
412ebb8e A |
858 | { |
859 | if ( stubSize != 6 ) { | |
860 | fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID()); | |
861 | return; | |
862 | } | |
863 | if ( (stubMappedAddress[0] != 0xFF) || (stubMappedAddress[1] != 0x25) ) { | |
864 | fprintf(stderr, "could not optimize stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); | |
865 | return; | |
866 | } | |
867 | // last four bytes in stub is RIP relative offset to lazy pointer | |
868 | // alter to point to more optimal lazy pointer | |
869 | uint32_t betterOffset = lpVMAddr - (stubVMAddress + 6); | |
870 | E::set32(*((uint32_t*)(&stubMappedAddress[2])), betterOffset); | |
871 | } | |
872 | ||
873 | template <typename A> | |
df9d6cf7 | 874 | void Binder<A>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress) |
412ebb8e A |
875 | { |
876 | // Remaining architectures are not optimized | |
877 | //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress); | |
878 | } | |
879 | ||
880 | // search for stub in this image that call target symbol name and then optimize its lazy pointer | |
881 | template <typename A> | |
df9d6cf7 | 882 | void Binder<A>::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr) |
412ebb8e A |
883 | { |
884 | // find named stub | |
885 | const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; | |
886 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>)); | |
887 | const uint32_t cmd_count = this->fHeader->ncmds(); | |
888 | const macho_load_command<P>* cmd = cmds; | |
889 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
890 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
891 | macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; | |
892 | macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>)); | |
893 | macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()]; | |
894 | for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
895 | if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { | |
896 | pint_t stubsVMStart = sect->addr(); | |
897 | uint8_t* stubsMappingStart = (uint8_t*)this->mappedAddressForNewAddress(stubsVMStart); | |
898 | const uint32_t indirectTableOffset = sect->reserved1(); | |
899 | const uint32_t stubSize = sect->reserved2(); | |
900 | uint32_t elementCount = sect->size() / stubSize; | |
901 | pint_t stubVMAddr = stubsVMStart; | |
902 | uint8_t* stubMappedAddr = stubsMappingStart; | |
903 | for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) { | |
904 | uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); | |
905 | switch ( symbolIndex ) { | |
906 | case INDIRECT_SYMBOL_ABS: | |
907 | case INDIRECT_SYMBOL_LOCAL: | |
908 | break; | |
909 | default: | |
910 | { | |
911 | const macho_nlist<P>* sym = &this->fSymbolTable[symbolIndex]; | |
912 | const char* symName = &fStrings[sym->n_strx()]; | |
913 | if ( strcmp(symName, stubName) == 0 ) | |
df9d6cf7 | 914 | this->switchStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr); |
412ebb8e A |
915 | } |
916 | break; | |
917 | } | |
918 | } | |
919 | } | |
920 | } | |
921 | } | |
922 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
923 | } | |
924 | } | |
bac542e6 A |
925 | |
926 | ||
927 | #endif // __MACHO_BINDER__ | |
928 | ||
929 | ||
930 | ||
931 |