]> git.saurik.com Git - apple/dyld.git/blame_incremental - launch-cache/MachOBinder.hpp
dyld-360.18.tar.gz
[apple/dyld.git] / launch-cache / MachOBinder.hpp
... / ...
CommitLineData
1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2006-2011 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#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>
43#include <unordered_map>
44#include <unordered_set>
45
46#include "MachOFileAbstraction.hpp"
47#include "Architectures.hpp"
48#include "MachOLayout.hpp"
49#include "MachORebaser.hpp"
50#include "MachOTrie.hpp"
51
52#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
53 #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
54#endif
55
56#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
57 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
58#endif
59
60
61template <typename A>
62class Binder : public Rebaser<A>
63{
64public:
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 };
74 struct CStringEquals {
75 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
76 };
77 typedef std::unordered_map<const char*, class Binder<A>*, CStringHash, CStringEquals> Map;
78
79
80 Binder(const MachOLayoutAbstraction&);
81 virtual ~Binder() {}
82
83 const char* getDylibID() const;
84 void setDependentBinders(const Map& map);
85 void bind(std::vector<void*>&);
86 void optimize();
87 void addResolverClient(Binder<A>* clientDylib, const char* symbolName);
88private:
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; };
93 struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; };
94 typedef std::unordered_map<const char*, pint_t, CStringHash, CStringEquals> NameToAddrMap;
95 typedef std::unordered_set<const char*, CStringHash, CStringEquals> NameSet;
96 typedef std::unordered_map<const char*, std::set<Binder<A>*>, CStringHash, CStringEquals> ResolverClientsMap;
97
98
99 static bool isPublicLocation(const char* pth);
100 void hoistPrivateRexports();
101 int ordinalOfDependentBinder(Binder<A>* dep);
102 void doBindDyldInfo(std::vector<void*>& pointersInData);
103 void doBindDyldLazyInfo(std::vector<void*>& pointersInData);
104 void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type,
105 int libraryOrdinal, int64_t addend,
106 const char* symbolName, bool lazyPointer, bool weakImport,
107 std::vector<void*>& pointersInData);
108 bool findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn,
109 bool* isResolverSymbol, bool* isAbsolute);
110 const char* parentUmbrella();
111 pint_t runtimeAddressFromNList(const macho_nlist<P>* sym);
112 void switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr);
113 void switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
114 pint_t findLazyPointerFor(const char* symbolName);
115 void shareLazyPointersToResolvers();
116
117
118 static uint8_t pointerRelocSize();
119 static uint8_t pointerRelocType();
120
121 std::vector<BinderAndReExportFlag> fDependentDylibs;
122 NameToAddrMap fHashTable;
123 NameSet fSymbolResolvers;
124 NameSet fAbsoluteSymbols;
125 std::vector<SymbolReExport> fReExportedSymbols;
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;
132 const macho_dyld_info_command<P>* fDyldInfo;
133 bool fOriginallyPrebound;
134 bool fReExportedSymbolsResolved;
135 ResolverClientsMap fResolverInfo;
136};
137
138template <>
139uint32_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
147template <typename A>
148typename A::P::uint_t Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* sym)
149{
150 return sym->n_value();
151}
152
153
154template <typename A>
155Binder<A>::Binder(const MachOLayoutAbstraction& layout)
156 : Rebaser<A>(layout),
157 fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL),
158 fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL),
159 fParentUmbrella(NULL), fReExportedSymbolsResolved(false)
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
166 const macho_symtab_command<P>* symtab;
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:
173 symtab = (macho_symtab_command<P>*)cmd;
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:
187 case LC_LOAD_UPWARD_DYLIB:
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;
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:
198 fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath());
199 break;
200 default:
201 if ( cmd->cmd() & LC_REQ_DYLD )
202 throwf("unknown required load command 0x%08X", cmd->cmd());
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";
210 if ( fDyldInfo == NULL )
211 throw "no LC_DYLD_INFO";
212 // build hash table
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 {
237 fHashTable[it->name] = it->address + baseAddress;
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;
250 }
251 //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name);
252 }
253}
254
255template <> uint8_t Binder<x86>::pointerRelocSize() { return 2; }
256template <> uint8_t Binder<x86_64>::pointerRelocSize() { return 3; }
257template <> uint8_t Binder<arm>::pointerRelocSize() { return 2; }
258template <> uint8_t Binder<arm64>::pointerRelocSize() { return 3; }
259
260template <> uint8_t Binder<x86>::pointerRelocType() { return GENERIC_RELOC_VANILLA; }
261template <> uint8_t Binder<x86_64>::pointerRelocType() { return X86_64_RELOC_UNSIGNED; }
262template <> uint8_t Binder<arm>::pointerRelocType() { return ARM_RELOC_VANILLA; }
263template <> uint8_t Binder<arm64>::pointerRelocType() { return ARM64_RELOC_UNSIGNED; }
264
265
266template <typename A>
267const char* Binder<A>::getDylibID() const
268{
269 if ( fDylibID != NULL )
270 return fDylibID->name();
271 else
272 return NULL;
273}
274
275template <typename A>
276const char* Binder<A>::parentUmbrella()
277{
278 if ( fParentUmbrella != NULL )
279 return fParentUmbrella->name();
280 else
281 return NULL;
282}
283
284
285template <typename A>
286bool 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}
309
310template <typename A>
311void Binder<A>::setDependentBinders(const Map& map)
312{
313 // build vector of dependent dylibs
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:
322 case LC_LOAD_UPWARD_DYLIB:
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 }
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 }
368 }
369 break;
370 }
371 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
372 }
373}
374
375template <typename A>
376int 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
385template <typename A>
386void 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 }
432}
433
434
435template <typename A>
436void Binder<A>::bind(std::vector<void*>& pointersInData)
437{
438 this->doBindDyldInfo(pointersInData);
439 this->doBindDyldLazyInfo(pointersInData);
440 // weak bind info is processed at launch time
441}
442
443template <typename A>
444void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
445 int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector<void*>& pointersInData)
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 )
453 throw "dynamic lookup linkage not allowed in dyld shared cache";
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;
470 bool isResolverSymbol = false;
471 bool isAbsolute = false;
472 Binder<A>* foundIn;
473 if ( weakImport && (binder == NULL) ) {
474 targetSymbolAddress = 0;
475 foundIn = NULL;
476 }
477 else {
478 if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) )
479 throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID());
480 }
481
482 // don't bind lazy pointers to resolver stubs in shared cache
483 if ( lazyPointer && isResolverSymbol ) {
484 if ( foundIn != this ) {
485 // record that this dylib has a lazy pointer to a resolver function
486 foundIn->addResolverClient(this, symbolName);
487 // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID());
488 }
489 return;
490 }
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 }
515 if ( !isAbsolute )
516 pointersInData.push_back(mappedAddr);
517}
518
519
520
521template <typename A>
522void Binder<A>::doBindDyldLazyInfo(std::vector<void*>& pointersInData)
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;
533 bool weakImport = false;
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:
558 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
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:
572 bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, pointersInData);
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
588template <typename A>
589void Binder<A>::doBindDyldInfo(std::vector<void*>& pointersInData)
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;
602 bool weakImport = false;
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:
628 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
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:
648 bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
649 segmentOffset += sizeof(pint_t);
650 break;
651 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
652 bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
653 segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
654 break;
655 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
656 bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
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) {
663 bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
664 segmentOffset += skip + sizeof(pint_t);
665 }
666 break;
667 default:
668 throwf("bad bind opcode %d", *p);
669 }
670 }
671}
672
673template <typename A>
674bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
675{
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;
685 bool isAb;
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
692 if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver, &isAb) )
693 throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID());
694
695 if ( isResolver )
696 fSymbolResolvers.insert(name);
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
710 // search this dylib
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());
715 *foundIn = this;
716 *isAbsolute = (fAbsoluteSymbols.count(name) != 0);
717 return true;
718 }
719
720 // search re-exported dylibs
721 for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
722 if ( it->reExport ) {
723 if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol, isAbsolute) )
724 return true;
725 }
726 }
727 //fprintf(stderr, "findExportedSymbolAddress(%s) => not found in %s\n", name, this->getDylibID());
728 return false;
729}
730
731// record which dylibs will be using this dylibs lazy pointer
732template <typename A>
733void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName)
734{
735 fResolverInfo[symbolName].insert(clientDylib);
736}
737
738
739template <typename A>
740typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
741{
742 static const bool log = false;
743
744 // lookup in lazy pointer section
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 = &sectionsStart[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 ) {
771 if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID());
772 return vmlocation;
773 }
774 break;
775 }
776 }
777 }
778 }
779 }
780 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
781 }
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
804 if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID());
805 return 0;
806}
807
808// called after all binding is done to optimize lazy pointers
809template <typename A>
810void Binder<A>::optimize()
811{
812 hoistPrivateRexports();
813 shareLazyPointersToResolvers();
814}
815
816template <typename A>
817void 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
834template <>
835void Binder<arm>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
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
856template <>
857void Binder<x86_64>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
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
873template <typename A>
874void Binder<A>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress)
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
881template <typename A>
882void Binder<A>::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr)
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 = &sectionsStart[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 )
914 this->switchStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
915 }
916 break;
917 }
918 }
919 }
920 }
921 }
922 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
923 }
924}
925
926
927#endif // __MACHO_BINDER__
928
929
930
931