1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
25 #include "mega-dylib-utils.h"
26 #include "MachOFileAbstraction.hpp"
30 #include <sys/errno.h>
31 #include <sys/fcntl.h>
32 #include <mach-o/loader.h>
33 #include <mach-o/fat.h>
40 #include <unordered_map>
41 #include <unordered_set>
43 #include "dyld_cache_config.h"
46 #if !NEW_CACHE_FILE_FORMAT
47 #include "CacheFileAbstraction.hpp"
50 #define ALIGN_AS_TYPE(value, type) \
51 ((value + alignof(type) - 1) & (-alignof(type)))
56 class SortedStringPool
59 // add a string and symbol table entry index to be updated later
60 void add(uint32_t symbolIndex
, const char* symbolName
) {
61 _map
[symbolName
].push_back(symbolIndex
);
64 // copy sorted strings to buffer and update all symbol's string offsets
65 uint32_t copyPoolAndUpdateOffsets(char* dstStringPool
, macho_nlist
<P
>* symbolTable
) {
66 // make sorted list of strings
67 std::vector
<std::string
> allStrings
;
68 allStrings
.reserve(_map
.size());
69 for (auto& entry
: _map
) {
70 allStrings
.push_back(entry
.first
);
72 std::sort(allStrings
.begin(), allStrings
.end());
73 // walk sorted list of strings
74 dstStringPool
[0] = '\0'; // tradition for start of pool to be empty string
75 uint32_t poolOffset
= 1;
76 for (const std::string
& symName
: allStrings
) {
77 // append string to pool
78 strcpy(&dstStringPool
[poolOffset
], symName
.c_str());
79 // set each string offset of each symbol using it
80 for (uint32_t symbolIndex
: _map
[symName
]) {
81 symbolTable
[symbolIndex
].set_n_strx(poolOffset
);
83 poolOffset
+= symName
.size() + 1;
85 // return size of pool
90 std::unordered_map
<std::string
, std::vector
<uint32_t>> _map
;
96 struct LocalSymbolInfo
99 uint32_t nlistStartIndex
;
104 template <typename P
>
105 class LinkeditOptimizer
{
107 LinkeditOptimizer(void* cacheBuffer
, macho_header
<P
>* mh
);
109 uint32_t linkeditSize() { return _linkeditSize
; }
110 uint32_t linkeditOffset() { return _linkeditCacheOffset
; }
111 uint64_t linkeditAddr() { return _linkeditAddr
; }
112 const char* installName() { return _installName
; }
113 void copyWeakBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
114 void copyLazyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
115 void copyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
116 void copyExportInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
117 void copyExportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
);
118 void copyImportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
);
119 void copyLocalSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
,
120 bool redact
, std::vector
<LocalSymbolInfo
>& localSymbolInfos
,
121 std::vector
<macho_nlist
<P
>>& unmappedLocalSymbols
, SortedStringPool
<P
>& localSymbolsStringPool
);
122 void copyFunctionStarts(uint8_t* newLinkEditContent
, uint32_t& offset
);
123 void copyDataInCode(uint8_t* newLinkEditContent
, uint32_t& offset
);
124 void copyIndirectSymbolTable(uint8_t* newLinkEditContent
, uint32_t& offset
);
125 void updateLoadCommands(uint32_t linkeditStartOffset
, uint64_t mergedLinkeditAddr
, uint64_t newLinkeditSize
,
126 uint32_t sharedSymbolTableStartOffset
, uint32_t sharedSymbolTableCount
,
127 uint32_t sharedSymbolStringsOffset
, uint32_t sharedSymbolStringsSize
);
129 macho_header
<P
>* machHeader() { return _mh
; }
130 const std::vector
<const char*> getDownwardDependents() { return _downDependentPaths
; }
131 const std::vector
<const char*> getAllDependents() { return _allDependentPaths
; }
132 const std::vector
<const char*> getReExportPaths() { return _reExportPaths
; }
133 const std::vector
<uint64_t> initializerAddresses() { return _initializerAddresses
; }
134 const std::vector
<macho_section
<P
>*> dofSections() { return _dofSections
; }
135 uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset
; }
136 uint32_t exportsTrieLinkEditSize() { return _exportInfoSize
; }
137 uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset
; }
138 uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize
; }
139 uint64_t dyldSectionAddress() { return _dyldSectionAddr
; }
140 const std::vector
<macho_segment_command
<P
>*>& segCmds() { return _segCmds
; }
145 typedef typename
P::uint_t pint_t
;
146 typedef typename
P::E E
;
148 macho_header
<P
>* _mh
;
150 uint32_t _linkeditSize
= 0;
151 uint32_t _linkeditCacheOffset
= 0;
152 uint64_t _linkeditAddr
= 0;
153 const uint8_t* _linkeditBias
= nullptr;
154 const char* _installName
= nullptr;
155 macho_symtab_command
<P
>* _symTabCmd
= nullptr;
156 macho_dysymtab_command
<P
>* _dynSymTabCmd
= nullptr;
157 macho_dyld_info_command
<P
>* _dyldInfo
= nullptr;
158 macho_linkedit_data_command
<P
>* _functionStartsCmd
= nullptr;
159 macho_linkedit_data_command
<P
>* _dataInCodeCmd
= nullptr;
160 std::vector
<macho_segment_command
<P
>*> _segCmds
;
161 std::unordered_map
<uint32_t,uint32_t> _oldToNewSymbolIndexes
;
162 std::vector
<const char*> _reExportPaths
;
163 std::vector
<const char*> _downDependentPaths
;
164 std::vector
<const char*> _allDependentPaths
;
165 std::vector
<uint64_t> _initializerAddresses
;
166 std::vector
<macho_section
<P
>*> _dofSections
;
167 uint32_t _newWeakBindingInfoOffset
= 0;
168 uint32_t _newLazyBindingInfoOffset
= 0;
169 uint32_t _newBindingInfoOffset
= 0;
170 uint32_t _newExportInfoOffset
= 0;
171 uint32_t _exportInfoSize
= 0;
172 uint32_t _newWeakBindingSize
= 0;
173 uint32_t _newExportedSymbolsStartIndex
= 0;
174 uint32_t _newExportedSymbolCount
= 0;
175 uint32_t _newImportedSymbolsStartIndex
= 0;
176 uint32_t _newImportedSymbolCount
= 0;
177 uint32_t _newLocalSymbolsStartIndex
= 0;
178 uint32_t _newLocalSymbolCount
= 0;
179 uint32_t _newFunctionStartsOffset
= 0;
180 uint32_t _newDataInCodeOffset
= 0;
181 uint32_t _newIndirectSymbolTableOffset
= 0;
182 uint64_t _dyldSectionAddr
= 0;
187 template <typename P
>
188 class AcceleratorTables
{
190 AcceleratorTables(void* cacheBuffer
, uint64_t linkeditStartAddr
, const std::vector
<LinkeditOptimizer
<P
>*>& optimizers
);
192 uint32_t totalSize() const;
193 void copyTo(uint8_t* buffer
);
196 typedef typename
P::E E
;
201 std::vector
<DepNode
*> _dependents
;
203 const char* _installName
;
205 DepNode() : _depth(0), _installName(nullptr) { }
207 static void verifyUnreachable(DepNode
* target
, NodeChain
& chain
, std::unordered_set
<DepNode
*>& visitedNodes
, const std::vector
<DepNode
*>& from
);
215 std::unordered_map
<macho_header
<P
>*, DepNode
> _depDAG
;
216 std::vector
<dyldCacheImageInfoExtra
<E
>> _extraInfo
;
217 std::vector
<uint8_t> _trieBytes
;
218 std::vector
<uint16_t> _reExportArray
;
219 std::vector
<uint16_t> _dependencyArray
;
220 std::vector
<uint16_t> _bottomUpArray
;
221 std::vector
<dyldCacheAcceleratorInitializer
<E
>> _initializers
;
222 std::vector
<dyldCacheAcceleratorDOFEntry
<E
>> _dofSections
;
223 std::vector
<dyldCacheAcceleratorRangeEntry
<E
>> _rangeTable
;
224 std::unordered_map
<macho_header
<P
>*, uint32_t> _machHeaderToImageIndex
;
225 std::unordered_map
<std::string
, macho_header
<P
>*> _dylibPathToMachHeader
;
226 std::unordered_map
<macho_header
<P
>*, LinkeditOptimizer
<P
>*> _machHeaderToOptimizer
;
227 dyldCacheAcceleratorInfo
<E
> _acceleratorInfoHeader
;
231 template <typename P
>
232 void AcceleratorTables
<P
>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables
<P
>::DepNode
* target
, struct AcceleratorTables
<P
>::NodeChain
& chain
,
233 std::unordered_set
<DepNode
*>& visitedNodes
, const std::vector
<AcceleratorTables
<P
>::DepNode
*>& from
) {
234 for (DepNode
* node
: from
) {
235 bool foundCycle
= (node
== target
);
236 for (NodeChain
* c
= &chain
; c
->prev
!= nullptr; c
= c
->prev
) {
237 if ( c
->node
== node
)
241 NodeChain
* chp
= &chain
;
242 std::string msg
= std::string("found cycle for ") + target
->_installName
;
243 while (chp
!= nullptr) {
244 msg
= msg
+ "\n " + chp
->node
->_installName
;
247 warning("%s", msg
.c_str());
251 if ( visitedNodes
.count(node
) )
254 nextChain
.prev
= &chain
;
255 nextChain
.node
= node
;
256 verifyUnreachable(target
, nextChain
, visitedNodes
, node
->_dependents
);
257 visitedNodes
.insert(node
);
261 const uint16_t kBranchIslandDylibIndex
= 0x7FFF;
263 template <typename P
>
264 AcceleratorTables
<P
>::AcceleratorTables(void* cacheBuffer
, uint64_t linkeditStartAddr
, const std::vector
<LinkeditOptimizer
<P
>*>& optimizers
)
266 // build table mapping tables to map between mach_header, index, and optimizer
267 for ( LinkeditOptimizer
<P
>* op
: optimizers
) {
268 _machHeaderToOptimizer
[op
->machHeader()] = op
;
270 uint64_t cacheStartAddress
;
271 #if NEW_CACHE_FILE_FORMAT
272 #error new format support not implemented
274 typedef typename
P::E E
;
275 const dyldCacheHeader
<E
>* header
= (dyldCacheHeader
<E
>*)cacheBuffer
;
276 const dyldCacheFileMapping
<E
>* mappings
= (dyldCacheFileMapping
<E
>*)((uint8_t*)cacheBuffer
+ header
->mappingOffset());
277 cacheStartAddress
= mappings
[0].address();
278 const dyldCacheImageInfo
<E
>* images
= (dyldCacheImageInfo
<E
>*)((uint8_t*)cacheBuffer
+ header
->imagesOffset());
279 const unsigned imageCount
= header
->imagesCount();
280 for (unsigned i
=0; i
< imageCount
; ++i
) {
281 uint64_t segCacheFileOffset
= images
[i
].address() - cacheStartAddress
;
282 macho_header
<P
>* mhMapped
= (macho_header
<P
>*)((uint8_t*)cacheBuffer
+segCacheFileOffset
);
283 const char* path
= (char*)cacheBuffer
+ images
[i
].pathFileOffset();
284 _dylibPathToMachHeader
[path
] = mhMapped
;
285 // don't add alias entries (path offset in pool near start of cache) to header->index map
286 if ( images
[i
].pathFileOffset() > segCacheFileOffset
)
287 _machHeaderToImageIndex
[mhMapped
] = i
;
291 // build DAG of image dependencies
292 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
293 _depDAG
[op
->machHeader()]._installName
= op
->installName();
295 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
296 DepNode
& node
= _depDAG
[op
->machHeader()];
297 for (const char* depPath
: op
->getDownwardDependents()) {
298 macho_header
<P
>* depMH
= _dylibPathToMachHeader
[depPath
];
299 assert(depMH
!= NULL
);
300 DepNode
* depNode
= &_depDAG
[depMH
];
301 node
._dependents
.push_back(depNode
);
305 // check for cycles in DAG
306 for (auto& entry
: _depDAG
) {
307 DepNode
* node
= &entry
.second
;
309 chain
.prev
= nullptr;
311 std::unordered_set
<DepNode
*> visitedNodes
;
312 DepNode::verifyUnreachable(node
, chain
, visitedNodes
, node
->_dependents
);
315 // compute depth for each DAG node
316 for (auto& entry
: _depDAG
) {
317 entry
.second
.computeDepth();
320 // build sorted (bottom up) list of images
321 std::vector
<macho_header
<P
>*> sortedMachHeaders
;
322 sortedMachHeaders
.reserve(optimizers
.size());
323 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
324 if ( strcmp(op
->installName(), "dyld_shared_cache_branch_islands") != 0 )
325 sortedMachHeaders
.push_back(op
->machHeader());
327 _machHeaderToImageIndex
[op
->machHeader()] = kBranchIslandDylibIndex
;
329 std::sort(sortedMachHeaders
.begin(), sortedMachHeaders
.end(),
330 [&](macho_header
<P
>* lmh
, macho_header
<P
>* rmh
) -> bool {
331 if ( _depDAG
[lmh
]._depth
!= _depDAG
[rmh
]._depth
)
332 return (_depDAG
[lmh
]._depth
< _depDAG
[rmh
]._depth
);
337 // build zeroed array of extra infos
338 dyldCacheImageInfoExtra
<E
> emptyExtra
;
339 emptyExtra
.set_exportsTrieAddr(0);
340 emptyExtra
.set_weakBindingsAddr(0);
341 emptyExtra
.set_exportsTrieSize(0);
342 emptyExtra
.set_weakBindingsSize(0);
343 emptyExtra
.set_dependentsStartArrayIndex(0);
344 emptyExtra
.set_reExportsStartArrayIndex(0);
345 _extraInfo
.insert(_extraInfo
.begin(), sortedMachHeaders
.size(), emptyExtra
);
347 //for ( macho_header<P>* mh : sortedMachHeaders ) {
348 // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
351 // build dependency table
352 _dependencyArray
.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
353 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
354 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
355 unsigned index
= _machHeaderToImageIndex
[mh
];
356 auto depPaths
= op
->getAllDependents();
357 if ( depPaths
.empty() ) {
358 _extraInfo
[index
].set_dependentsStartArrayIndex(0);
361 _extraInfo
[index
].set_dependentsStartArrayIndex((uint32_t)_dependencyArray
.size());
362 auto downPaths
= op
->getDownwardDependents();
363 for (const char* depPath
: depPaths
) {
364 macho_header
<P
>* depMH
= _dylibPathToMachHeader
[depPath
];
365 uint16_t depIndex
= _machHeaderToImageIndex
[depMH
];
366 if ( std::find(downPaths
.begin(), downPaths
.end(), depPath
) == downPaths
.end())
368 _dependencyArray
.push_back(depIndex
);
370 _dependencyArray
.push_back(0xFFFF); // mark end of list
374 // build re-exports table
375 _reExportArray
.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
376 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
377 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
378 unsigned index
= _machHeaderToImageIndex
[mh
];
379 auto reExPaths
= op
->getReExportPaths();
380 if ( reExPaths
.empty() ) {
381 _extraInfo
[index
].set_reExportsStartArrayIndex(0);
384 _extraInfo
[index
].set_reExportsStartArrayIndex((uint32_t)_reExportArray
.size());
385 for (const char* reExPath
: reExPaths
) {
386 macho_header
<P
>* reExMH
= _dylibPathToMachHeader
[reExPath
];
387 uint32_t reExIndex
= _machHeaderToImageIndex
[reExMH
];
388 _reExportArray
.push_back(reExIndex
);
390 _reExportArray
.push_back(0xFFFF); // mark end of list
394 // build ordered list of initializers
395 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
396 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
397 unsigned index
= _machHeaderToImageIndex
[mh
];
398 _bottomUpArray
.push_back(index
);
399 for (uint64_t initializer
: op
->initializerAddresses()) {
400 //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
401 dyldCacheAcceleratorInitializer
<E
> entry
;
402 entry
.set_functionOffset((uint32_t)(initializer
-cacheStartAddress
));
403 entry
.set_imageIndex(_machHeaderToImageIndex
[mh
]);
404 _initializers
.push_back(entry
);
408 // build ordered list of DOF sections
409 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
410 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
412 unsigned imageIndex
= _machHeaderToImageIndex
[mh
];
413 for (auto& sect
: op
->dofSections()) {
414 //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
415 dyldCacheAcceleratorDOFEntry
<E
> entry
;
416 entry
.set_sectionAddress(sect
->addr());
417 entry
.set_sectionSize((uint32_t)sect
->size());
418 entry
.set_imageIndex(imageIndex
);
419 _dofSections
.push_back(entry
);
423 // register exports trie and weak binding info in each dylib with image extra info
424 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
425 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
426 unsigned index
= _machHeaderToImageIndex
[mh
];
427 _extraInfo
[index
].set_exportsTrieAddr(op
->exportsTrieLinkEditOffset() + linkeditStartAddr
);
428 _extraInfo
[index
].set_exportsTrieSize(op
->exportsTrieLinkEditSize());
429 _extraInfo
[index
].set_weakBindingsAddr(op
->weakBindingLinkEditOffset() + linkeditStartAddr
);
430 _extraInfo
[index
].set_weakBindingsSize(op
->weakBindingLinkEditSize());
433 // record location of __DATA/__dyld section in libdyld.dylib
434 macho_header
<P
>* libdyldMH
= _dylibPathToMachHeader
["/usr/lib/system/libdyld.dylib"];
435 LinkeditOptimizer
<P
>* libdyldOp
= _machHeaderToOptimizer
[libdyldMH
];
436 uint64_t dyldSectionAddr
= libdyldOp
->dyldSectionAddress();
438 // build range table for fast address->image lookups
439 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
440 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
441 unsigned imageIndex
= _machHeaderToImageIndex
[mh
];
442 for ( const macho_segment_command
<P
>* segCmd
: op
->segCmds() ) {
443 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
445 dyldCacheAcceleratorRangeEntry
<E
> entry
;
446 entry
.set_startAddress(segCmd
->vmaddr());
447 entry
.set_size((uint32_t)segCmd
->vmsize());
448 entry
.set_imageIndex(imageIndex
);
449 _rangeTable
.push_back(entry
);
452 std::sort(_rangeTable
.begin(), _rangeTable
.end(),
453 [&](const dyldCacheAcceleratorRangeEntry
<E
>& lRange
, const dyldCacheAcceleratorRangeEntry
<E
>& rRange
) -> bool {
454 return (lRange
.startAddress() < rRange
.startAddress());
457 // build trie that maps install names to image index
458 std::vector
<DylibIndexTrie::Entry
> dylibEntrys
;
459 for (auto &x
: _dylibPathToMachHeader
) {
460 const std::string
& path
= x
.first
;
461 unsigned index
= _machHeaderToImageIndex
[x
.second
];
462 dylibEntrys
.push_back(DylibIndexTrie::Entry(path
, DylibIndex(index
)));
464 DylibIndexTrie
dylibsTrie(dylibEntrys
);
465 dylibsTrie
.emit(_trieBytes
);
466 while ( (_trieBytes
.size() % 4) != 0 )
467 _trieBytes
.push_back(0);
470 dyldCacheAcceleratorInfo
<E
>& h
= _acceleratorInfoHeader
;
472 h
.set_imageExtrasCount((uint32_t)_extraInfo
.size());
473 h
.set_imagesExtrasOffset(ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info
), dyld_cache_image_info_extra
));
474 h
.set_bottomUpListOffset(h
.imagesExtrasOffset() + h
.imageExtrasCount()*sizeof(dyld_cache_image_info_extra
));
475 h
.set_dylibTrieOffset(h
.bottomUpListOffset() + h
.imageExtrasCount()*sizeof(uint16_t));
476 h
.set_dylibTrieSize((uint32_t)_trieBytes
.size());
477 h
.set_initializersOffset(ALIGN_AS_TYPE(h
.dylibTrieOffset() + h
.dylibTrieSize(), dyld_cache_accelerator_initializer
));
478 h
.set_initializersCount((uint32_t)_initializers
.size());
479 h
.set_dofSectionsOffset(ALIGN_AS_TYPE(h
.initializersOffset() + h
.initializersCount()*sizeof(dyld_cache_accelerator_initializer
), dyld_cache_accelerator_initializer
));
480 h
.set_dofSectionsCount((uint32_t)_dofSections
.size());
481 h
.set_reExportListOffset(ALIGN_AS_TYPE(h
.dofSectionsOffset() + h
.dofSectionsCount()*sizeof(dyld_cache_accelerator_dof
), dyld_cache_accelerator_dof
));
482 h
.set_reExportCount((uint32_t)_reExportArray
.size());
483 h
.set_depListOffset(ALIGN_AS_TYPE(h
.reExportListOffset() + h
.reExportCount()*sizeof(uint16_t), uint16_t));
484 h
.set_depListCount((uint32_t)_dependencyArray
.size());
485 h
.set_rangeTableOffset(ALIGN_AS_TYPE(h
.depListOffset() + h
.depListCount()*sizeof(uint16_t), dyld_cache_range_entry
));
486 h
.set_rangeTableCount((uint32_t)_rangeTable
.size());
487 h
.set_dyldSectionAddr(dyldSectionAddr
);
491 template <typename P
>
492 void AcceleratorTables
<P
>::DepNode::computeDepth()
497 for (DepNode
* node
: _dependents
) {
498 node
->computeDepth();
499 if ( node
->_depth
>= _depth
)
500 _depth
= node
->_depth
+ 1;
504 template <typename P
>
505 uint32_t AcceleratorTables
<P
>::totalSize() const
507 return (uint32_t)align(_acceleratorInfoHeader
.rangeTableOffset() + _acceleratorInfoHeader
.rangeTableCount()*sizeof(dyld_cache_range_entry
), 14);
510 template <typename P
>
511 void AcceleratorTables
<P
>::copyTo(uint8_t* buffer
)
513 memcpy(buffer
, &_acceleratorInfoHeader
, sizeof(dyld_cache_accelerator_info
));
514 memcpy(&buffer
[_acceleratorInfoHeader
.imagesExtrasOffset()], &_extraInfo
[0], _extraInfo
.size()*sizeof(dyld_cache_image_info_extra
));
515 memcpy(&buffer
[_acceleratorInfoHeader
.bottomUpListOffset()], &_bottomUpArray
[0], _bottomUpArray
.size()*sizeof(uint16_t));
516 memcpy(&buffer
[_acceleratorInfoHeader
.initializersOffset()], &_initializers
[0], _initializers
.size()*sizeof(dyld_cache_accelerator_initializer
));
517 memcpy(&buffer
[_acceleratorInfoHeader
.reExportListOffset()], &_reExportArray
[0], _reExportArray
.size()*sizeof(uint16_t));
518 memcpy(&buffer
[_acceleratorInfoHeader
.dofSectionsOffset()], &_dofSections
[0], _dofSections
.size()*sizeof(dyld_cache_accelerator_dof
));
519 memcpy(&buffer
[_acceleratorInfoHeader
.depListOffset()], &_dependencyArray
[0], _dependencyArray
.size()*sizeof(uint16_t));
520 memcpy(&buffer
[_acceleratorInfoHeader
.rangeTableOffset()], &_rangeTable
[0], _rangeTable
.size()*sizeof(dyld_cache_range_entry
));
521 memcpy(&buffer
[_acceleratorInfoHeader
.dylibTrieOffset()], &_trieBytes
[0], _trieBytes
.size());
526 template <typename P
>
527 LinkeditOptimizer
<P
>::LinkeditOptimizer(void* cacheBuffer
, macho_header
<P
>* mh
)
528 : _mh(mh
), _cacheBuffer(cacheBuffer
)
530 _linkeditBias
= (uint8_t*)cacheBuffer
;
531 const unsigned origLoadCommandsSize
= mh
->sizeofcmds();
532 unsigned bytesRemaining
= origLoadCommandsSize
;
533 unsigned removedCount
= 0;
534 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)mh
+ sizeof(macho_header
<P
>));
535 const uint32_t cmdCount
= mh
->ncmds();
536 const macho_load_command
<P
>* cmd
= cmds
;
537 const macho_dylib_command
<P
>* dylibCmd
;
538 const macho_routines_command
<P
>* routinesCmd
;
539 macho_segment_command
<P
>* segCmd
;
540 for (uint32_t i
= 0; i
< cmdCount
; ++i
) {
542 switch (cmd
->cmd()) {
544 _installName
= ((macho_dylib_command
<P
>*)cmd
)->name();
547 _symTabCmd
= (macho_symtab_command
<P
>*)cmd
;
550 _dynSymTabCmd
= (macho_dysymtab_command
<P
>*)cmd
;
553 case LC_DYLD_INFO_ONLY
:
554 _dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
555 _exportInfoSize
= _dyldInfo
->export_size();
557 case LC_FUNCTION_STARTS
:
558 _functionStartsCmd
= (macho_linkedit_data_command
<P
>*)cmd
;
560 case LC_DATA_IN_CODE
:
561 _dataInCodeCmd
= (macho_linkedit_data_command
<P
>*)cmd
;
565 routinesCmd
= (macho_routines_command
<P
>*)cmd
;
566 _initializerAddresses
.push_back(routinesCmd
->init_address());
568 case LC_REEXPORT_DYLIB
:
570 case LC_LOAD_WEAK_DYLIB
:
571 case LC_LOAD_UPWARD_DYLIB
:
572 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
573 _allDependentPaths
.push_back(dylibCmd
->name());
574 if ( cmd
->cmd() != LC_LOAD_UPWARD_DYLIB
)
575 _downDependentPaths
.push_back(dylibCmd
->name());
576 if ( cmd
->cmd() == LC_REEXPORT_DYLIB
)
577 _reExportPaths
.push_back(dylibCmd
->name());
579 case macho_segment_command
<P
>::CMD
:
580 segCmd
= (macho_segment_command
<P
>*)cmd
;
581 _segCmds
.push_back(segCmd
);
582 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) {
583 _linkeditSize
= (uint32_t)segCmd
->vmsize();
584 _linkeditCacheOffset
= (uint32_t)segCmd
->fileoff();
585 _linkeditAddr
= segCmd
->vmaddr();
587 else if ( segCmd
->nsects() > 0 ) {
588 macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((uint8_t*)segCmd
+ sizeof(macho_segment_command
<P
>));
589 macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
590 for (macho_section
<P
>* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
591 const uint8_t type
= sect
->flags() & SECTION_TYPE
;
592 if ( type
== S_MOD_INIT_FUNC_POINTERS
) {
593 const pint_t
* inits
= (pint_t
*)((char*)cacheBuffer
+ sect
->offset());
594 const size_t count
= sect
->size() / sizeof(pint_t
);
595 for (size_t j
=0; j
< count
; ++j
) {
596 uint64_t func
= P::getP(inits
[j
]);
597 _initializerAddresses
.push_back(func
);
600 else if ( type
== S_DTRACE_DOF
) {
601 _dofSections
.push_back(sect
);
603 else if ( (strcmp(sect
->sectname(), "__dyld") == 0) && (strncmp(sect
->segname(), "__DATA", 6) == 0) ) {
604 _dyldSectionAddr
= sect
->addr();
609 case LC_SEGMENT_SPLIT_INFO
:
613 uint32_t cmdSize
= cmd
->cmdsize();
614 macho_load_command
<P
>* nextCmd
= (macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmdSize
);
616 ::memmove((void*)cmd
, (void*)nextCmd
, bytesRemaining
);
620 bytesRemaining
-= cmdSize
;
624 // zero out stuff removed
625 ::bzero((void*)cmd
, bytesRemaining
);
627 mh
->set_ncmds(cmdCount
- removedCount
);
628 mh
->set_sizeofcmds(origLoadCommandsSize
- bytesRemaining
);
632 static void dumpLoadCommands(const uint8_t* mheader)
634 const mach_header* const mh = (mach_header*)mheader;
635 const uint32_t cmd_count = mh->ncmds;
636 bool is64 = (mh->magic == MH_MAGIC_64);
637 const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)));
638 const load_command* cmd = cmds;
639 const segment_command* segCmd;
640 const segment_command_64* seg64Cmd;
641 const symtab_command* symTab;
642 const linkedit_data_command* leData;
643 const uint8_t* linkEditBias = NULL;
644 for (uint32_t i = 0; i < cmd_count; ++i) {
647 segCmd = (const segment_command*)cmd;
648 printf("LC_SEGMENT\n");
649 printf(" segname = %s\n", segCmd->segname);
650 printf(" vmaddr = 0x%08X\n", segCmd->vmaddr);
651 printf(" vmsize = 0x%08X\n", segCmd->vmsize);
652 printf(" fileoff = 0x%08X\n", segCmd->fileoff);
653 printf(" filesize = 0x%08X\n", segCmd->filesize);
654 if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
655 linkEditBias = mheader - segCmd->fileoff;
659 seg64Cmd = (const segment_command_64*)cmd;
660 printf("LC_SEGMENT_64\n");
661 printf(" segname = %s\n", seg64Cmd->segname);
662 printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr);
663 printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize);
664 printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff);
665 printf(" filesize = 0x%09llX\n", seg64Cmd->filesize);
666 if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) {
667 linkEditBias = mheader - seg64Cmd->fileoff;
671 symTab = (const symtab_command*)cmd;
672 printf("LC_SYMTAB\n");
673 printf(" symoff = 0x%08X\n", symTab->symoff);
674 printf(" nsyms = 0x%08X\n", symTab->nsyms);
675 printf(" stroff = 0x%08X\n", symTab->stroff);
676 printf(" strsize = 0x%08X\n", symTab->strsize);
678 const char* strPool = (char*)&linkEditBias[symTab->stroff];
679 const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]);
680 printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]);
681 printf(" sym[0].n_type = 0x%02X\n", sym0->n_type);
682 printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect);
683 printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc);
684 printf(" sym[0].n_value = 0x%llX\n", sym0->n_value);
685 const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]);
686 printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]);
687 printf(" sym[1].n_type = 0x%02X\n", sym1->n_type);
688 printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect);
689 printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc);
690 printf(" sym[1].n_value = 0x%llX\n", sym1->n_value);
693 case LC_FUNCTION_STARTS:
694 leData = (const linkedit_data_command*)cmd;
695 printf("LC_FUNCTION_STARTS\n");
696 printf(" dataoff = 0x%08X\n", leData->dataoff);
697 printf(" datasize = 0x%08X\n", leData->datasize);
699 //printf("0x%08X\n", cmd->cmd);
702 cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize);
707 template <typename P
>
708 void LinkeditOptimizer
<P
>::updateLoadCommands(uint32_t mergedLinkeditStartOffset
, uint64_t mergedLinkeditAddr
, uint64_t newLinkeditSize
,
709 uint32_t sharedSymbolTableStartOffset
, uint32_t sharedSymbolTableCount
,
710 uint32_t sharedSymbolStringsOffset
, uint32_t sharedSymbolStringsSize
)
712 // update __LINKEDIT segment in all dylibs to overlap the same shared region
713 for (macho_segment_command
<P
>* segCmd
: _segCmds
) {
714 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) {
715 segCmd
->set_vmaddr(mergedLinkeditAddr
);
716 segCmd
->set_vmsize(newLinkeditSize
);
717 segCmd
->set_fileoff(mergedLinkeditStartOffset
);
718 segCmd
->set_filesize(newLinkeditSize
);
720 else if ( strcmp(segCmd
->segname(), "__TEXT") == 0 ) {
721 // HACK until lldb fixed in: <rdar://problem/20357466> DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache
722 segCmd
->set_fileoff(0);
727 // update symbol table to point to shared symbol table
728 _symTabCmd
->set_symoff(mergedLinkeditStartOffset
+ sharedSymbolTableStartOffset
+ _newLocalSymbolsStartIndex
*sizeof(macho_nlist
<P
>));
729 _symTabCmd
->set_nsyms(_newLocalSymbolCount
+_newExportedSymbolCount
+_newImportedSymbolCount
);
730 _symTabCmd
->set_stroff(mergedLinkeditStartOffset
+ sharedSymbolStringsOffset
);
731 _symTabCmd
->set_strsize(sharedSymbolStringsSize
);
733 // update dynamic symbol table to have proper offsets into shared symbol table
734 _dynSymTabCmd
->set_ilocalsym(0);
735 _dynSymTabCmd
->set_nlocalsym(_newLocalSymbolCount
);
736 _dynSymTabCmd
->set_iextdefsym(_newExportedSymbolsStartIndex
-_newLocalSymbolsStartIndex
);
737 _dynSymTabCmd
->set_nextdefsym(_newExportedSymbolCount
);
738 _dynSymTabCmd
->set_iundefsym(_newImportedSymbolsStartIndex
-_newLocalSymbolsStartIndex
);
739 _dynSymTabCmd
->set_nundefsym(_newImportedSymbolCount
);
740 _dynSymTabCmd
->set_tocoff(0);
741 _dynSymTabCmd
->set_ntoc(0);
742 _dynSymTabCmd
->set_modtaboff(0);
743 _dynSymTabCmd
->set_nmodtab(0);
744 _dynSymTabCmd
->set_indirectsymoff(mergedLinkeditStartOffset
+ _newIndirectSymbolTableOffset
);
745 _dynSymTabCmd
->set_extreloff(0);
746 _dynSymTabCmd
->set_locreloff(0);
747 _dynSymTabCmd
->set_nlocrel(0);
750 if ( _dyldInfo
!= nullptr ) {
751 _dyldInfo
->set_rebase_off(0);
752 _dyldInfo
->set_rebase_size(0);
753 _dyldInfo
->set_bind_off(_dyldInfo
->bind_size() ? mergedLinkeditStartOffset
+ _newBindingInfoOffset
: 0);
754 _dyldInfo
->set_weak_bind_off(_dyldInfo
->weak_bind_size() ? mergedLinkeditStartOffset
+ _newWeakBindingInfoOffset
: 0 );
755 _dyldInfo
->set_lazy_bind_off(_dyldInfo
->lazy_bind_size() ? mergedLinkeditStartOffset
+ _newLazyBindingInfoOffset
: 0 );
756 _dyldInfo
->set_export_off(mergedLinkeditStartOffset
+ _newExportInfoOffset
);
759 // update function-starts
760 if ( _functionStartsCmd
!= nullptr )
761 _functionStartsCmd
->set_dataoff(mergedLinkeditStartOffset
+_newFunctionStartsOffset
);
763 // update data-in-code
764 if ( _dataInCodeCmd
!= nullptr )
765 _dataInCodeCmd
->set_dataoff(mergedLinkeditStartOffset
+_newDataInCodeOffset
);
768 template <typename P
>
769 void LinkeditOptimizer
<P
>::copyWeakBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
771 if ( _dyldInfo
== nullptr )
773 unsigned size
= _dyldInfo
->weak_bind_size();
775 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->weak_bind_off()], size
);
776 _newWeakBindingInfoOffset
= offset
;
777 _newWeakBindingSize
= size
;
783 template <typename P
>
784 void LinkeditOptimizer
<P
>::copyLazyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
786 if ( _dyldInfo
== nullptr )
788 unsigned size
= _dyldInfo
->lazy_bind_size();
790 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->lazy_bind_off()], size
);
791 _newLazyBindingInfoOffset
= offset
;
796 template <typename P
>
797 void LinkeditOptimizer
<P
>::copyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
799 if ( _dyldInfo
== nullptr )
801 unsigned size
= _dyldInfo
->bind_size();
803 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->bind_off()], size
);
804 _newBindingInfoOffset
= offset
;
809 template <typename P
>
810 void LinkeditOptimizer
<P
>::copyExportInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
812 if ( _dyldInfo
== nullptr )
814 unsigned size
= _dyldInfo
->export_size();
816 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->export_off()], size
);
817 _newExportInfoOffset
= offset
;
823 template <typename P
>
824 void LinkeditOptimizer
<P
>::copyFunctionStarts(uint8_t* newLinkEditContent
, uint32_t& offset
)
826 if ( _functionStartsCmd
== nullptr )
828 unsigned size
= _functionStartsCmd
->datasize();
829 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_functionStartsCmd
->dataoff()], size
);
830 _newFunctionStartsOffset
= offset
;
834 template <typename P
>
835 void LinkeditOptimizer
<P
>::copyDataInCode(uint8_t* newLinkEditContent
, uint32_t& offset
)
837 if ( _dataInCodeCmd
== nullptr )
839 unsigned size
= _dataInCodeCmd
->datasize();
840 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dataInCodeCmd
->dataoff()], size
);
841 _newDataInCodeOffset
= offset
;
846 template <typename P
>
847 void LinkeditOptimizer
<P
>::copyLocalSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
,
848 bool redact
, std::vector
<LocalSymbolInfo
>& localSymbolInfos
,
849 std::vector
<macho_nlist
<P
>>& unmappedLocalSymbols
, SortedStringPool
<P
>& localSymbolsStringPool
)
851 LocalSymbolInfo localInfo
;
852 localInfo
.dylibOffset
= (uint32_t)(((uint8_t*)_mh
) - (uint8_t*)_cacheBuffer
);
853 localInfo
.nlistStartIndex
= (uint32_t)unmappedLocalSymbols
.size();
854 localInfo
.nlistCount
= 0;
855 _newLocalSymbolsStartIndex
= symbolIndex
;
856 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
857 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
858 const macho_nlist
<P
>* const firstExport
= &symbolTable
[_dynSymTabCmd
->ilocalsym()];
859 const macho_nlist
<P
>* const lastExport
= &symbolTable
[_dynSymTabCmd
->ilocalsym()+_dynSymTabCmd
->nlocalsym()];
860 for (const macho_nlist
<P
>* entry
= firstExport
; entry
< lastExport
; ++entry
) {
861 if ( (entry
->n_type() & N_TYPE
) != N_SECT
)
863 if ( (entry
->n_type() & N_STAB
) != 0)
865 const char* name
= &strings
[entry
->n_strx()];
866 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
867 *newSymbolEntry
= *entry
;
869 // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
870 if ( entry
->n_sect() == 1 ) {
871 stringPool
.add(symbolIndex
, "<redacted>");
873 offset
+= sizeof(macho_nlist
<P
>);
875 // copy local symbol to unmmapped locals area
876 localSymbolsStringPool
.add((uint32_t)unmappedLocalSymbols
.size(), name
);
877 unmappedLocalSymbols
.push_back(*entry
);
878 unmappedLocalSymbols
.back().set_n_strx(0);
881 stringPool
.add(symbolIndex
, name
);
883 offset
+= sizeof(macho_nlist
<P
>);
886 _newLocalSymbolCount
= symbolIndex
- _newLocalSymbolsStartIndex
;
887 localInfo
.nlistCount
= (uint32_t)unmappedLocalSymbols
.size() - localInfo
.nlistStartIndex
;
888 localSymbolInfos
.push_back(localInfo
);
892 template <typename P
>
893 void LinkeditOptimizer
<P
>::copyExportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
)
895 _newExportedSymbolsStartIndex
= symbolIndex
;
896 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
897 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
898 const macho_nlist
<P
>* const firstExport
= &symbolTable
[_dynSymTabCmd
->iextdefsym()];
899 const macho_nlist
<P
>* const lastExport
= &symbolTable
[_dynSymTabCmd
->iextdefsym()+_dynSymTabCmd
->nextdefsym()];
900 uint32_t oldSymbolIndex
= _dynSymTabCmd
->iextdefsym();
901 for (const macho_nlist
<P
>* entry
= firstExport
; entry
< lastExport
; ++entry
, ++oldSymbolIndex
) {
902 if ( (entry
->n_type() & N_TYPE
) != N_SECT
)
904 const char* name
= &strings
[entry
->n_strx()];
905 if ( strncmp(name
, ".objc_", 6) == 0 )
907 if ( strncmp(name
, "$ld$", 4) == 0 )
909 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
910 *newSymbolEntry
= *entry
;
911 newSymbolEntry
->set_n_strx(0);
912 stringPool
.add(symbolIndex
, name
);
913 _oldToNewSymbolIndexes
[oldSymbolIndex
] = symbolIndex
- _newLocalSymbolsStartIndex
;
915 offset
+= sizeof(macho_nlist
<P
>);
917 _newExportedSymbolCount
= symbolIndex
- _newExportedSymbolsStartIndex
;
920 template <typename P
>
921 void LinkeditOptimizer
<P
>::copyImportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
)
923 _newImportedSymbolsStartIndex
= symbolIndex
;
924 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
925 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
926 const macho_nlist
<P
>* const firstImport
= &symbolTable
[_dynSymTabCmd
->iundefsym()];
927 const macho_nlist
<P
>* const lastImport
= &symbolTable
[_dynSymTabCmd
->iundefsym()+_dynSymTabCmd
->nundefsym()];
928 uint32_t oldSymbolIndex
= _dynSymTabCmd
->iundefsym();
929 for (const macho_nlist
<P
>* entry
= firstImport
; entry
< lastImport
; ++entry
, ++oldSymbolIndex
) {
930 if ( (entry
->n_type() & N_TYPE
) != N_UNDF
)
932 const char* name
= &strings
[entry
->n_strx()];
933 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
934 *newSymbolEntry
= *entry
;
935 newSymbolEntry
->set_n_strx(0);
936 stringPool
.add(symbolIndex
, name
);
937 _oldToNewSymbolIndexes
[oldSymbolIndex
] = symbolIndex
- _newLocalSymbolsStartIndex
;
939 offset
+= sizeof(macho_nlist
<P
>);
941 _newImportedSymbolCount
= symbolIndex
- _newImportedSymbolsStartIndex
;
944 template <typename P
>
945 void LinkeditOptimizer
<P
>::copyIndirectSymbolTable(uint8_t* newLinkEditContent
, uint32_t& offset
)
947 _newIndirectSymbolTableOffset
= offset
;
948 const uint32_t* const indirectTable
= (uint32_t*)&_linkeditBias
[_dynSymTabCmd
->indirectsymoff()];
949 uint32_t* newIndirectTable
= (uint32_t*)&newLinkEditContent
[offset
];
950 for (int i
=0; i
< _dynSymTabCmd
->nindirectsyms(); ++i
) {
951 uint32_t symbolIndex
= E::get32(indirectTable
[i
]);
952 if ( (symbolIndex
== INDIRECT_SYMBOL_ABS
) || (symbolIndex
== INDIRECT_SYMBOL_LOCAL
) )
953 E::set32(newIndirectTable
[i
], symbolIndex
);
955 E::set32(newIndirectTable
[i
], _oldToNewSymbolIndexes
[symbolIndex
]);
956 offset
+= sizeof(uint32_t);
960 template <typename P
>
961 uint64_t mergeLinkedits(SharedCache
& cache
, bool dontMapLocalSymbols
, bool addAcceleratorTables
, std::vector
<LinkeditOptimizer
<P
>*>& optimizers
)
963 // allocate space for new linkedit data
964 uint32_t linkeditStartOffset
= 0xFFFFFFFF;
965 uint32_t linkeditEndOffset
= 0;
966 uint64_t linkeditStartAddr
= 0;
967 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
968 uint32_t leOffset
= op
->linkeditOffset();
969 if ( leOffset
< linkeditStartOffset
) {
970 linkeditStartOffset
= leOffset
;
971 linkeditStartAddr
= op
->linkeditAddr();
973 uint32_t leEndOffset
= op
->linkeditOffset() + op
->linkeditSize();
974 if ( leEndOffset
> linkeditEndOffset
)
975 linkeditEndOffset
= leEndOffset
;
977 uint64_t totalUnoptLinkeditsSize
= linkeditEndOffset
- linkeditStartOffset
;
978 uint8_t* newLinkEdit
= (uint8_t*)calloc(totalUnoptLinkeditsSize
, 1);
979 SortedStringPool
<P
> stringPool
;
982 verboseLog("Merged LINKEDIT:");
984 // copy weak binding info
985 uint32_t startWeakBindInfosOffset
= offset
;
986 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
987 op
->copyWeakBindingInfo(newLinkEdit
, offset
);
989 verboseLog(" weak bindings size: %5uKB", (offset
-startWeakBindInfosOffset
)/1024);
992 uint32_t startExportInfosOffset
= offset
;
993 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
994 op
->copyExportInfo(newLinkEdit
, offset
);
996 verboseLog(" exports info size: %5uKB", (offset
-startExportInfosOffset
)/1024);
998 // in theory, an optimized cache can drop the binding info
1000 // copy binding info
1001 uint32_t startBindingsInfosOffset
= offset
;
1002 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1003 op
->copyBindingInfo(newLinkEdit
, offset
);
1005 verboseLog(" bindings size: %5uKB", (offset
-startBindingsInfosOffset
)/1024);
1007 // copy lazy binding info
1008 uint32_t startLazyBindingsInfosOffset
= offset
;
1009 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1010 op
->copyLazyBindingInfo(newLinkEdit
, offset
);
1012 verboseLog(" lazy bindings size: %5uKB", (offset
-startLazyBindingsInfosOffset
)/1024);
1015 // copy symbol table entries
1016 std::vector
<macho_nlist
<P
>> unmappedLocalSymbols
;
1017 if ( dontMapLocalSymbols
)
1018 unmappedLocalSymbols
.reserve(0x01000000);
1019 std::vector
<LocalSymbolInfo
> localSymbolInfos
;
1020 localSymbolInfos
.reserve(optimizers
.size());
1021 SortedStringPool
<P
> localSymbolsStringPool
;
1022 uint32_t symbolIndex
= 0;
1023 const uint32_t sharedSymbolTableStartOffset
= offset
;
1024 uint32_t sharedSymbolTableExportsCount
= 0;
1025 uint32_t sharedSymbolTableImportsCount
= 0;
1026 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1027 op
->copyLocalSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
, dontMapLocalSymbols
,
1028 localSymbolInfos
, unmappedLocalSymbols
, localSymbolsStringPool
);
1029 uint32_t x
= symbolIndex
;
1030 op
->copyExportedSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
);
1031 sharedSymbolTableExportsCount
+= (symbolIndex
-x
);
1032 uint32_t y
= symbolIndex
;
1033 op
->copyImportedSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
);
1034 sharedSymbolTableImportsCount
+= (symbolIndex
-y
);
1036 uint32_t sharedSymbolTableCount
= symbolIndex
;
1037 const uint32_t sharedSymbolTableEndOffset
= offset
;
1039 // copy function starts
1040 uint32_t startFunctionStartsOffset
= offset
;
1041 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1042 op
->copyFunctionStarts(newLinkEdit
, offset
);
1044 verboseLog(" function starts size: %5uKB", (offset
-startFunctionStartsOffset
)/1024);
1046 // copy data-in-code info
1047 uint32_t startDataInCodeOffset
= offset
;
1048 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1049 op
->copyDataInCode(newLinkEdit
, offset
);
1051 verboseLog(" data in code size: %5uB", offset
-startDataInCodeOffset
);
1053 // copy indirect symbol tables
1054 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1055 op
->copyIndirectSymbolTable(newLinkEdit
, offset
);
1057 // if indirect table has odd number of entries, end will not be 8-byte aligned
1058 if ( (offset
% sizeof(typename
P::uint_t
)) != 0 )
1062 uint32_t sharedSymbolStringsOffset
= offset
;
1063 uint32_t sharedSymbolStringsSize
= stringPool
.copyPoolAndUpdateOffsets((char*)&newLinkEdit
[sharedSymbolStringsOffset
], (macho_nlist
<P
>*)&newLinkEdit
[sharedSymbolTableStartOffset
]);
1064 offset
+= sharedSymbolStringsSize
;
1065 uint32_t newLinkeditUnalignedSize
= offset
;
1066 uint64_t newLinkeditEnd
= align(linkeditStartOffset
+newLinkeditUnalignedSize
, 14);
1067 verboseLog(" symbol table size: %5uKB (%d exports, %d imports)", (sharedSymbolTableEndOffset
-sharedSymbolTableStartOffset
)/1024, sharedSymbolTableExportsCount
, sharedSymbolTableImportsCount
);
1068 verboseLog(" symbol string pool size: %5uKB", sharedSymbolStringsSize
/1024);
1070 // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
1071 verboseLog("LINKEDITS optimized from %uMB to %uMB", (uint32_t)totalUnoptLinkeditsSize
/(1024*1024), (uint32_t)newLinkeditUnalignedSize
/(1024*1024));
1072 ::memcpy((char*)cache
.buffer().get()+linkeditStartOffset
, newLinkEdit
, newLinkeditUnalignedSize
);
1073 ::bzero((char*)cache
.buffer().get()+linkeditStartOffset
+newLinkeditUnalignedSize
, totalUnoptLinkeditsSize
-newLinkeditUnalignedSize
);
1074 ::free(newLinkEdit
);
1076 // If making cache for customers, add extra accelerator tables for dyld
1077 if ( addAcceleratorTables
) {
1078 AcceleratorTables
<P
> tables(cache
.buffer().get(), linkeditStartAddr
, optimizers
);
1079 uint32_t tablesSize
= tables
.totalSize();
1080 if ( tablesSize
< (totalUnoptLinkeditsSize
-newLinkeditUnalignedSize
) ) {
1081 tables
.copyTo((uint8_t*)cache
.buffer().get()+newLinkeditEnd
);
1082 newLinkeditEnd
+= tablesSize
;
1083 uint64_t accelInfoAddr
= align(linkeditStartAddr
+ newLinkeditUnalignedSize
, 14);
1084 cache
.setAcceleratorInfoRange(accelInfoAddr
, tablesSize
);
1087 warning("not enough room to add dyld accelerator tables");
1091 // update mapping to reduce linkedit size
1092 cache
.setLinkeditsMappingEndFileOffset(newLinkeditEnd
);
1094 // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
1095 uint64_t newFileSize
= newLinkeditEnd
;
1096 if ( dontMapLocalSymbols
) {
1097 typedef typename
P::E E
;
1098 uint32_t localSymbolsOffset
= (uint32_t)align(newFileSize
, 14);
1099 uint32_t spaceAtEnd
= linkeditEndOffset
- (uint32_t)newLinkeditEnd
;
1100 dyldCacheLocalSymbolsInfo
<E
>* infoHeader
= (dyldCacheLocalSymbolsInfo
<E
>*)((uint8_t*)cache
.buffer().get()+localSymbolsOffset
);
1101 const uint32_t entriesOffset
= sizeof(dyldCacheLocalSymbolsInfo
<E
>);
1102 const uint32_t entriesCount
= (uint32_t)localSymbolInfos
.size();
1103 const uint32_t nlistOffset
= (uint32_t)align(entriesOffset
+ entriesCount
* sizeof(dyldCacheLocalSymbolEntry
<E
>), 4); // 16-byte align start
1104 const uint32_t nlistCount
= (uint32_t)unmappedLocalSymbols
.size();
1105 const uint32_t stringsOffset
= nlistOffset
+ nlistCount
* sizeof(macho_nlist
<P
>);
1106 // copy info for each dylib
1107 dyldCacheLocalSymbolEntry
<E
>* entries
= (dyldCacheLocalSymbolEntry
<E
>*)(((uint8_t*)infoHeader
)+entriesOffset
);
1108 for (int i
=0; i
< entriesCount
; ++i
) {
1109 entries
[i
].set_dylibOffset(localSymbolInfos
[i
].dylibOffset
);
1110 entries
[i
].set_nlistStartIndex(localSymbolInfos
[i
].nlistStartIndex
);
1111 entries
[i
].set_nlistCount(localSymbolInfos
[i
].nlistCount
);
1114 macho_nlist
<P
>* newLocalsSymbolTable
= (macho_nlist
<P
>*)(((uint8_t*)infoHeader
)+nlistOffset
);
1115 ::memcpy(newLocalsSymbolTable
, &unmappedLocalSymbols
[0], nlistCount
*sizeof(macho_nlist
<P
>));
1117 const uint32_t stringsSize
= localSymbolsStringPool
.copyPoolAndUpdateOffsets(((char*)infoHeader
)+stringsOffset
, newLocalsSymbolTable
);
1118 const uint32_t localsRegionSize
= (uint32_t)align(stringsOffset
+stringsSize
, 14);
1119 if ( localsRegionSize
> spaceAtEnd
)
1120 terminate("not enough room to store local symbols");
1121 // fill in local symbols info
1122 infoHeader
->set_nlistOffset(nlistOffset
);
1123 infoHeader
->set_nlistCount(nlistCount
);
1124 infoHeader
->set_stringsOffset(stringsOffset
);
1125 infoHeader
->set_stringsSize(stringsSize
);
1126 infoHeader
->set_entriesOffset(entriesOffset
);
1127 infoHeader
->set_entriesCount(entriesCount
);
1128 // update cache size
1129 newFileSize
+= localsRegionSize
;
1130 verboseLog("Unmapped local symbol info: %uMB", localsRegionSize
/(1024*1024));
1131 cache
.setUnmappedLocalsRange(localSymbolsOffset
, localsRegionSize
);
1134 // update all load commands to new merged layout
1135 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1136 op
->updateLoadCommands(linkeditStartOffset
, linkeditStartAddr
, newLinkeditEnd
-linkeditStartOffset
,
1137 sharedSymbolTableStartOffset
, sharedSymbolTableCount
,
1138 sharedSymbolStringsOffset
, sharedSymbolStringsSize
);
1144 } // anonymous namespace
1146 template <typename P
>
1147 void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols
, bool addAcceleratorTables
, const std::vector
<uint64_t>& branchPoolOffsets
)
1149 // construct a LinkeditOptimizer for each image
1150 std::vector
<LinkeditOptimizer
<P
>*> optimizers
;
1151 forEachImage([&](const void* mh
, const char*, time_t, ino_t
, const std::vector
<MachOProxy::Segment
>&) {
1152 optimizers
.push_back(new LinkeditOptimizer
<P
>(_buffer
.get(), (macho_header
<P
>*)mh
));
1154 // add optimizer for each branch pool
1155 for (uint64_t poolOffset
: branchPoolOffsets
) {
1156 macho_header
<P
>* mh
= (macho_header
<P
>*)((char*)_buffer
.get() + poolOffset
);
1157 optimizers
.push_back(new LinkeditOptimizer
<P
>(_buffer
.get(), mh
));
1160 // merge linkedit info
1161 _fileSize
= mergeLinkedits(*this, dontMapLocalSymbols
, addAcceleratorTables
, optimizers
);
1163 // delete optimizers
1164 for (LinkeditOptimizer
<P
>* op
: optimizers
)
1168 void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols
, bool addAcceleratorTables
, const std::vector
<uint64_t>& branchPoolOffsets
)
1170 switch ( _arch
.arch
) {
1173 optimizeLinkedit
<Pointer32
<LittleEndian
>>(dontMapLocalSymbols
, addAcceleratorTables
, branchPoolOffsets
);
1175 case CPU_TYPE_X86_64
:
1176 case CPU_TYPE_ARM64
:
1177 optimizeLinkedit
<Pointer64
<LittleEndian
>>(dontMapLocalSymbols
, addAcceleratorTables
, branchPoolOffsets
);
1180 terminate("unsupported arch 0x%08X", _arch
.arch
);