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@
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
36 #include <unordered_map>
37 #include <unordered_set>
39 #include "MachOFileAbstraction.hpp"
41 #include "DyldSharedCache.h"
42 #include "CacheBuilder.h"
43 #include "MachOLoaded.h"
45 #define ALIGN_AS_TYPE(value, type) \
46 ((value + alignof(type) - 1) & (-alignof(type)))
51 class SortedStringPool
54 // add a string and symbol table entry index to be updated later
55 void add(uint32_t symbolIndex
, const char* symbolName
) {
56 _map
[symbolName
].push_back(symbolIndex
);
59 // copy sorted strings to buffer and update all symbol's string offsets
60 uint32_t copyPoolAndUpdateOffsets(char* dstStringPool
, macho_nlist
<P
>* symbolTable
) {
61 // walk sorted list of strings
62 dstStringPool
[0] = '\0'; // tradition for start of pool to be empty string
63 uint32_t poolOffset
= 1;
64 for (auto& entry
: _map
) {
65 const std::string
& symName
= entry
.first
;
66 // append string to pool
67 strcpy(&dstStringPool
[poolOffset
], symName
.c_str());
68 // set each string offset of each symbol using it
69 for (uint32_t symbolIndex
: entry
.second
) {
70 symbolTable
[symbolIndex
].set_n_strx(poolOffset
);
72 poolOffset
+= symName
.size() + 1;
74 // return size of pool
80 for (auto& entry
: _map
) {
81 size
+= (entry
.first
.size() + 1);
88 std::map
<std::string
, std::vector
<uint32_t>> _map
;
92 } // anonymous namespace
95 struct LocalSymbolInfo
98 uint32_t nlistStartIndex
;
103 template <typename P
>
104 class LinkeditOptimizer
{
106 LinkeditOptimizer(void* cacheBuffer
, macho_header
<P
>* mh
, Diagnostics
& diag
);
108 uint32_t linkeditSize() { return _linkeditSize
; }
109 uint64_t linkeditAddr() { return _linkeditAddr
; }
110 const char* installName() { return _installName
; }
111 void copyWeakBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
112 void copyLazyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
113 void copyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
114 void copyExportInfo(uint8_t* newLinkEditContent
, uint32_t& offset
);
115 void copyExportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
);
116 void copyImportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
);
117 void copyLocalSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
,
118 bool redact
, std::vector
<LocalSymbolInfo
>& localSymbolInfos
,
119 std::vector
<macho_nlist
<P
>>& unmappedLocalSymbols
, SortedStringPool
<P
>& localSymbolsStringPool
);
120 void copyFunctionStarts(uint8_t* newLinkEditContent
, uint32_t& offset
);
121 void copyDataInCode(uint8_t* newLinkEditContent
, uint32_t& offset
);
122 void copyIndirectSymbolTable(uint8_t* newLinkEditContent
, uint32_t& offset
);
123 void updateLoadCommands(uint32_t linkeditStartOffset
, uint64_t mergedLinkeditAddr
, uint64_t newLinkeditSize
,
124 uint32_t sharedSymbolTableStartOffset
, uint32_t sharedSymbolTableCount
,
125 uint32_t sharedSymbolStringsOffset
, uint32_t sharedSymbolStringsSize
);
127 macho_header
<P
>* machHeader() { return _mh
; }
128 const std::vector
<const char*> getDownwardDependents() { return _downDependentPaths
; }
129 const std::vector
<const char*> getAllDependents() { return _allDependentPaths
; }
130 const std::vector
<const char*> getReExportPaths() { return _reExportPaths
; }
131 const std::vector
<uint64_t> initializerAddresses() { return _initializerAddresses
; }
132 const std::vector
<macho_section
<P
>*> dofSections() { return _dofSections
; }
133 uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset
; }
134 uint32_t exportsTrieLinkEditSize() { return _exportInfoSize
; }
135 uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset
; }
136 uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize
; }
137 uint64_t dyldSectionAddress() { return _dyldSectionAddr
; }
138 const std::vector
<macho_segment_command
<P
>*>& segCmds() { return _segCmds
; }
141 static void optimizeLinkedit(CacheBuilder
& builder
);
142 static void mergeLinkedits(CacheBuilder
& builder
, std::vector
<LinkeditOptimizer
<P
>*>& optimizers
);
146 typedef typename
P::uint_t pint_t
;
147 typedef typename
P::E E
;
149 macho_header
<P
>* _mh
;
151 Diagnostics
& _diagnostics
;
152 uint32_t _linkeditSize
= 0;
153 uint64_t _linkeditAddr
= 0;
154 const uint8_t* _linkeditBias
= nullptr;
155 const char* _installName
= nullptr;
156 macho_symtab_command
<P
>* _symTabCmd
= nullptr;
157 macho_dysymtab_command
<P
>* _dynSymTabCmd
= nullptr;
158 macho_dyld_info_command
<P
>* _dyldInfo
= nullptr;
159 macho_linkedit_data_command
<P
>* _functionStartsCmd
= nullptr;
160 macho_linkedit_data_command
<P
>* _dataInCodeCmd
= nullptr;
161 std::vector
<macho_segment_command
<P
>*> _segCmds
;
162 std::unordered_map
<uint32_t,uint32_t> _oldToNewSymbolIndexes
;
163 std::vector
<const char*> _reExportPaths
;
164 std::vector
<const char*> _downDependentPaths
;
165 std::vector
<const char*> _allDependentPaths
;
166 std::vector
<uint64_t> _initializerAddresses
;
167 std::vector
<macho_section
<P
>*> _dofSections
;
168 uint32_t _newWeakBindingInfoOffset
= 0;
169 uint32_t _newLazyBindingInfoOffset
= 0;
170 uint32_t _newBindingInfoOffset
= 0;
171 uint32_t _newExportInfoOffset
= 0;
172 uint32_t _exportInfoSize
= 0;
173 uint32_t _newWeakBindingSize
= 0;
174 uint32_t _newExportedSymbolsStartIndex
= 0;
175 uint32_t _newExportedSymbolCount
= 0;
176 uint32_t _newImportedSymbolsStartIndex
= 0;
177 uint32_t _newImportedSymbolCount
= 0;
178 uint32_t _newLocalSymbolsStartIndex
= 0;
179 uint32_t _newLocalSymbolCount
= 0;
180 uint32_t _newFunctionStartsOffset
= 0;
181 uint32_t _newDataInCodeOffset
= 0;
182 uint32_t _newIndirectSymbolTableOffset
= 0;
183 uint64_t _dyldSectionAddr
= 0;
188 template <typename P
>
189 class AcceleratorTables
{
191 AcceleratorTables(DyldSharedCache
* cache
, uint64_t linkeditStartAddr
, Diagnostics
& diag
, const std::vector
<LinkeditOptimizer
<P
>*>& optimizers
);
193 uint32_t totalSize() const;
194 void copyTo(uint8_t* buffer
);
197 typedef typename
P::E E
;
202 std::vector
<DepNode
*> _dependents
;
204 const char* _installName
;
206 DepNode() : _depth(0), _installName(nullptr) { }
208 static void verifyUnreachable(DepNode
* target
, NodeChain
& chain
, Diagnostics
& diag
, std::unordered_set
<DepNode
*>& visitedNodes
, const std::vector
<DepNode
*>& from
);
216 std::unordered_map
<macho_header
<P
>*, DepNode
> _depDAG
;
217 std::vector
<dyld_cache_image_info_extra
> _extraInfo
;
218 std::vector
<uint8_t> _trieBytes
;
219 std::vector
<uint16_t> _reExportArray
;
220 std::vector
<uint16_t> _dependencyArray
;
221 std::vector
<uint16_t> _bottomUpArray
;
222 std::vector
<dyld_cache_accelerator_initializer
> _initializers
;
223 std::vector
<dyld_cache_accelerator_dof
> _dofSections
;
224 std::vector
<dyld_cache_range_entry
> _rangeTable
;
225 std::unordered_map
<macho_header
<P
>*, uint32_t> _machHeaderToImageIndex
;
226 std::unordered_map
<std::string
, macho_header
<P
>*> _dylibPathToMachHeader
;
227 std::unordered_map
<macho_header
<P
>*, LinkeditOptimizer
<P
>*> _machHeaderToOptimizer
;
228 dyld_cache_accelerator_info _acceleratorInfoHeader
;
232 template <typename P
>
233 void AcceleratorTables
<P
>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables
<P
>::DepNode
* target
, struct AcceleratorTables
<P
>::NodeChain
& chain
, Diagnostics
& diag
,
234 std::unordered_set
<DepNode
*>& visitedNodes
, const std::vector
<AcceleratorTables
<P
>::DepNode
*>& from
) {
235 for (DepNode
* node
: from
) {
236 bool foundCycle
= (node
== target
);
237 for (NodeChain
* c
= &chain
; c
->prev
!= nullptr; c
= c
->prev
) {
238 if ( c
->node
== target
) {
244 NodeChain
* chp
= &chain
;
245 std::string msg
= std::string("found cycle for ") + target
->_installName
;
246 while (chp
!= nullptr) {
247 msg
= msg
+ "\n " + chp
->node
->_installName
;
250 diag
.warning("%s", msg
.c_str());
254 if ( visitedNodes
.count(node
) )
256 visitedNodes
.insert(node
);
258 nextChain
.prev
= &chain
;
259 nextChain
.node
= node
;
260 verifyUnreachable(target
, nextChain
, diag
, visitedNodes
, node
->_dependents
);
264 const uint16_t kBranchIslandDylibIndex
= 0x7FFF;
266 template <typename P
>
267 AcceleratorTables
<P
>::AcceleratorTables(DyldSharedCache
* cache
, uint64_t linkeditStartAddr
, Diagnostics
& diag
, const std::vector
<LinkeditOptimizer
<P
>*>& optimizers
)
269 // build table mapping tables to map between mach_header, index, and optimizer
270 for ( LinkeditOptimizer
<P
>* op
: optimizers
) {
271 _machHeaderToOptimizer
[op
->machHeader()] = op
;
273 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((uint8_t*)cache
+ cache
->header
.mappingOffset
);
274 uint64_t cacheStartAddress
= mappings
[0].address
;
275 const dyld_cache_image_info
* images
= (dyld_cache_image_info
*)((uint8_t*)cache
+ cache
->header
.imagesOffset
);
276 for (unsigned i
=0; i
< cache
->header
.imagesCount
; ++i
) {
277 uint64_t segCacheFileOffset
= images
[i
].address
- cacheStartAddress
;
278 macho_header
<P
>* mhMapped
= (macho_header
<P
>*)((uint8_t*)cache
+segCacheFileOffset
);
279 const char* path
= (char*)cache
+ images
[i
].pathFileOffset
;
280 _dylibPathToMachHeader
[path
] = mhMapped
;
281 // don't add alias entries (path offset in pool near start of cache) to header->index map
282 if ( images
[i
].pathFileOffset
> segCacheFileOffset
)
283 _machHeaderToImageIndex
[mhMapped
] = i
;
287 // build DAG of image dependencies
288 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
289 _depDAG
[op
->machHeader()]._installName
= op
->installName();
291 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
292 DepNode
& node
= _depDAG
[op
->machHeader()];
293 for (const char* depPath
: op
->getDownwardDependents()) {
294 macho_header
<P
>* depMH
= _dylibPathToMachHeader
[depPath
];
295 if ( depMH
!= nullptr ) {
296 DepNode
* depNode
= &_depDAG
[depMH
];
297 node
._dependents
.push_back(depNode
);
302 // check for cycles in DAG
303 for (auto& entry
: _depDAG
) {
304 DepNode
* node
= &entry
.second
;
306 chain
.prev
= nullptr;
308 std::unordered_set
<DepNode
*> visitedNodes
;
309 DepNode::verifyUnreachable(node
, chain
, diag
, visitedNodes
, node
->_dependents
);
312 // compute depth for each DAG node
313 for (auto& entry
: _depDAG
) {
314 entry
.second
.computeDepth();
317 // build sorted (bottom up) list of images
318 std::vector
<macho_header
<P
>*> sortedMachHeaders
;
319 sortedMachHeaders
.reserve(optimizers
.size());
320 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
321 if ( strcmp(op
->installName(), "dyld_shared_cache_branch_islands") != 0 )
322 sortedMachHeaders
.push_back(op
->machHeader());
324 _machHeaderToImageIndex
[op
->machHeader()] = kBranchIslandDylibIndex
;
326 std::sort(sortedMachHeaders
.begin(), sortedMachHeaders
.end(),
327 [&](macho_header
<P
>* lmh
, macho_header
<P
>* rmh
) -> bool {
328 if ( _depDAG
[lmh
]._depth
!= _depDAG
[rmh
]._depth
)
329 return (_depDAG
[lmh
]._depth
< _depDAG
[rmh
]._depth
);
334 // build zeroed array of extra infos
335 dyld_cache_image_info_extra emptyExtra
;
336 emptyExtra
.exportsTrieAddr
= 0;
337 emptyExtra
.weakBindingsAddr
= 0;
338 emptyExtra
.exportsTrieSize
= 0;
339 emptyExtra
.weakBindingsSize
= 0;
340 emptyExtra
.dependentsStartArrayIndex
= 0;
341 emptyExtra
.reExportsStartArrayIndex
= 0;
342 _extraInfo
.insert(_extraInfo
.begin(), sortedMachHeaders
.size(), emptyExtra
);
344 //for ( macho_header<P>* mh : sortedMachHeaders ) {
345 // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
348 // build dependency table
349 _dependencyArray
.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
350 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
351 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
352 unsigned index
= _machHeaderToImageIndex
[mh
];
353 auto depPaths
= op
->getAllDependents();
354 if ( depPaths
.empty() ) {
355 _extraInfo
[index
].dependentsStartArrayIndex
= 0;
358 _extraInfo
[index
].dependentsStartArrayIndex
= (uint32_t)_dependencyArray
.size();
359 auto downPaths
= op
->getDownwardDependents();
360 for (const char* depPath
: depPaths
) {
361 macho_header
<P
>* depMH
= _dylibPathToMachHeader
[depPath
];
362 uint16_t depIndex
= _machHeaderToImageIndex
[depMH
];
363 if ( std::find(downPaths
.begin(), downPaths
.end(), depPath
) == downPaths
.end())
365 _dependencyArray
.push_back(depIndex
);
367 _dependencyArray
.push_back(0xFFFF); // mark end of list
371 // build re-exports table
372 _reExportArray
.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
373 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
374 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
375 unsigned index
= _machHeaderToImageIndex
[mh
];
376 auto reExPaths
= op
->getReExportPaths();
377 if ( reExPaths
.empty() ) {
378 _extraInfo
[index
].reExportsStartArrayIndex
= 0;
381 _extraInfo
[index
].reExportsStartArrayIndex
= (uint32_t)_reExportArray
.size();
382 for (const char* reExPath
: reExPaths
) {
383 macho_header
<P
>* reExMH
= _dylibPathToMachHeader
[reExPath
];
384 uint32_t reExIndex
= _machHeaderToImageIndex
[reExMH
];
385 _reExportArray
.push_back(reExIndex
);
387 _reExportArray
.push_back(0xFFFF); // mark end of list
391 // build ordered list of initializers
392 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
393 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
394 unsigned index
= _machHeaderToImageIndex
[mh
];
395 _bottomUpArray
.push_back(index
);
396 for (uint64_t initializer
: op
->initializerAddresses()) {
397 //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
398 dyld_cache_accelerator_initializer entry
;
399 entry
.functionOffset
= (uint32_t)(initializer
-cacheStartAddress
);
400 entry
.imageIndex
= _machHeaderToImageIndex
[mh
];
401 _initializers
.push_back(entry
);
405 // build ordered list of DOF sections
406 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
407 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
409 unsigned imageIndex
= _machHeaderToImageIndex
[mh
];
410 for (auto& sect
: op
->dofSections()) {
411 //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
412 dyld_cache_accelerator_dof entry
;
413 entry
.sectionAddress
= sect
->addr();
414 entry
.sectionSize
= (uint32_t)sect
->size();
415 entry
.imageIndex
= imageIndex
;
416 _dofSections
.push_back(entry
);
421 // register exports trie and weak binding info in each dylib with image extra info
422 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
423 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
424 unsigned index
= _machHeaderToImageIndex
[mh
];
425 _extraInfo
[index
].exportsTrieAddr
= op
->exportsTrieLinkEditOffset() + linkeditStartAddr
;
426 _extraInfo
[index
].exportsTrieSize
= op
->exportsTrieLinkEditSize();
427 _extraInfo
[index
].weakBindingsAddr
= op
->weakBindingLinkEditOffset() + linkeditStartAddr
;
428 _extraInfo
[index
].weakBindingsSize
= op
->weakBindingLinkEditSize();
431 // record location of __DATA/__dyld section in libdyld.dylib
432 macho_header
<P
>* libdyldMH
= _dylibPathToMachHeader
["/usr/lib/system/libdyld.dylib"];
433 LinkeditOptimizer
<P
>* libdyldOp
= _machHeaderToOptimizer
[libdyldMH
];
434 uint64_t dyldSectionAddr
= libdyldOp
->dyldSectionAddress();
436 // build range table for fast address->image lookups
437 for (macho_header
<P
>* mh
: sortedMachHeaders
) {
438 LinkeditOptimizer
<P
>* op
= _machHeaderToOptimizer
[mh
];
439 unsigned imageIndex
= _machHeaderToImageIndex
[mh
];
440 for (const macho_segment_command
<P
>* segCmd
: op
->segCmds()) {
441 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
443 dyld_cache_range_entry entry
;
444 entry
.startAddress
= segCmd
->vmaddr();
445 entry
.size
= (uint32_t)segCmd
->vmsize();
446 entry
.imageIndex
= imageIndex
;
447 _rangeTable
.push_back(entry
);
450 std::sort(_rangeTable
.begin(), _rangeTable
.end(),
451 [&](const dyld_cache_range_entry
& lRange
, const dyld_cache_range_entry
& rRange
) -> bool {
452 return (lRange
.startAddress
< rRange
.startAddress
);
455 // build trie that maps install names to image index
456 std::vector
<DylibIndexTrie::Entry
> dylibEntrys
;
457 for (auto &x
: _dylibPathToMachHeader
) {
458 const std::string
& path
= x
.first
;
459 unsigned index
= _machHeaderToImageIndex
[x
.second
];
460 dylibEntrys
.push_back(DylibIndexTrie::Entry(path
, DylibIndex(index
)));
462 DylibIndexTrie
dylibsTrie(dylibEntrys
);
463 dylibsTrie
.emit(_trieBytes
);
464 while ( (_trieBytes
.size() % 4) != 0 )
465 _trieBytes
.push_back(0);
468 _acceleratorInfoHeader
.version
= 1;
469 _acceleratorInfoHeader
.imageExtrasCount
= (uint32_t)_extraInfo
.size();
470 _acceleratorInfoHeader
.imagesExtrasOffset
= ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info
), dyld_cache_image_info_extra
);
471 _acceleratorInfoHeader
.bottomUpListOffset
= _acceleratorInfoHeader
.imagesExtrasOffset
+ _acceleratorInfoHeader
.imageExtrasCount
*sizeof(dyld_cache_image_info_extra
);
472 _acceleratorInfoHeader
.dylibTrieOffset
= _acceleratorInfoHeader
.bottomUpListOffset
+ _acceleratorInfoHeader
.imageExtrasCount
*sizeof(uint16_t);
473 _acceleratorInfoHeader
.dylibTrieSize
= (uint32_t)_trieBytes
.size();
474 _acceleratorInfoHeader
.initializersOffset
= ALIGN_AS_TYPE(_acceleratorInfoHeader
.dylibTrieOffset
+ _acceleratorInfoHeader
.dylibTrieSize
, dyld_cache_accelerator_initializer
);
475 _acceleratorInfoHeader
.initializersCount
= (uint32_t)_initializers
.size();
476 _acceleratorInfoHeader
.dofSectionsOffset
= ALIGN_AS_TYPE(_acceleratorInfoHeader
.initializersOffset
+ _acceleratorInfoHeader
.initializersCount
*sizeof(dyld_cache_accelerator_initializer
), dyld_cache_accelerator_initializer
);
477 _acceleratorInfoHeader
.dofSectionsCount
= (uint32_t)_dofSections
.size();
478 _acceleratorInfoHeader
.reExportListOffset
= ALIGN_AS_TYPE(_acceleratorInfoHeader
.dofSectionsOffset
+ _acceleratorInfoHeader
.dofSectionsCount
*sizeof(dyld_cache_accelerator_dof
), dyld_cache_accelerator_dof
);
479 _acceleratorInfoHeader
.reExportCount
= (uint32_t)_reExportArray
.size();
480 _acceleratorInfoHeader
.depListOffset
= ALIGN_AS_TYPE(_acceleratorInfoHeader
.reExportListOffset
+ _acceleratorInfoHeader
.reExportCount
*sizeof(uint16_t), uint16_t);
481 _acceleratorInfoHeader
.depListCount
= (uint32_t)_dependencyArray
.size();
482 _acceleratorInfoHeader
.rangeTableOffset
= ALIGN_AS_TYPE(_acceleratorInfoHeader
.depListOffset
+ _acceleratorInfoHeader
.depListCount
*sizeof(uint16_t), dyld_cache_range_entry
);
483 _acceleratorInfoHeader
.rangeTableCount
= (uint32_t)_rangeTable
.size();
484 _acceleratorInfoHeader
.dyldSectionAddr
= dyldSectionAddr
;
488 template <typename P
>
489 void AcceleratorTables
<P
>::DepNode::computeDepth()
494 for (DepNode
* node
: _dependents
) {
495 node
->computeDepth();
496 if ( node
->_depth
>= _depth
)
497 _depth
= node
->_depth
+ 1;
501 template <typename P
>
502 uint32_t AcceleratorTables
<P
>::totalSize() const
504 return (uint32_t)align(_acceleratorInfoHeader
.rangeTableOffset
+ _acceleratorInfoHeader
.rangeTableCount
*sizeof(dyld_cache_range_entry
), 14);
507 template <typename P
>
508 void AcceleratorTables
<P
>::copyTo(uint8_t* buffer
)
510 memcpy(buffer
, &_acceleratorInfoHeader
, sizeof(dyld_cache_accelerator_info
));
511 memcpy(&buffer
[_acceleratorInfoHeader
.imagesExtrasOffset
], &_extraInfo
[0], _extraInfo
.size()*sizeof(dyld_cache_image_info_extra
));
512 memcpy(&buffer
[_acceleratorInfoHeader
.bottomUpListOffset
], &_bottomUpArray
[0], _bottomUpArray
.size()*sizeof(uint16_t));
513 memcpy(&buffer
[_acceleratorInfoHeader
.initializersOffset
], &_initializers
[0], _initializers
.size()*sizeof(dyld_cache_accelerator_initializer
));
514 memcpy(&buffer
[_acceleratorInfoHeader
.reExportListOffset
], &_reExportArray
[0], _reExportArray
.size()*sizeof(uint16_t));
515 memcpy(&buffer
[_acceleratorInfoHeader
.dofSectionsOffset
], &_dofSections
[0], _dofSections
.size()*sizeof(dyld_cache_accelerator_dof
));
516 memcpy(&buffer
[_acceleratorInfoHeader
.depListOffset
], &_dependencyArray
[0], _dependencyArray
.size()*sizeof(uint16_t));
517 memcpy(&buffer
[_acceleratorInfoHeader
.rangeTableOffset
], &_rangeTable
[0], _rangeTable
.size()*sizeof(dyld_cache_range_entry
));
518 memcpy(&buffer
[_acceleratorInfoHeader
.dylibTrieOffset
], &_trieBytes
[0], _trieBytes
.size());
523 template <typename P
>
524 LinkeditOptimizer
<P
>::LinkeditOptimizer(void* cacheBuffer
, macho_header
<P
>* mh
, Diagnostics
& diag
)
525 : _mh(mh
), _cacheBuffer(cacheBuffer
), _diagnostics(diag
)
527 const unsigned origLoadCommandsSize
= mh
->sizeofcmds();
528 unsigned bytesRemaining
= origLoadCommandsSize
;
529 unsigned removedCount
= 0;
530 uint64_t textSegAddr
= 0;
532 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)mh
+ sizeof(macho_header
<P
>));
533 const uint32_t cmdCount
= mh
->ncmds();
534 const macho_load_command
<P
>* cmd
= cmds
;
535 const macho_dylib_command
<P
>* dylibCmd
;
536 const macho_routines_command
<P
>* routinesCmd
;
537 macho_segment_command
<P
>* segCmd
;
538 for (uint32_t i
= 0; i
< cmdCount
; ++i
) {
540 switch (cmd
->cmd()) {
542 _installName
= ((macho_dylib_command
<P
>*)cmd
)->name();
545 _symTabCmd
= (macho_symtab_command
<P
>*)cmd
;
548 _dynSymTabCmd
= (macho_dysymtab_command
<P
>*)cmd
;
551 case LC_DYLD_INFO_ONLY
:
552 _dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
553 _exportInfoSize
= _dyldInfo
->export_size();
555 case LC_FUNCTION_STARTS
:
556 _functionStartsCmd
= (macho_linkedit_data_command
<P
>*)cmd
;
558 case LC_DATA_IN_CODE
:
559 _dataInCodeCmd
= (macho_linkedit_data_command
<P
>*)cmd
;
563 routinesCmd
= (macho_routines_command
<P
>*)cmd
;
564 _initializerAddresses
.push_back(routinesCmd
->init_address());
566 case LC_REEXPORT_DYLIB
:
568 case LC_LOAD_WEAK_DYLIB
:
569 case LC_LOAD_UPWARD_DYLIB
:
570 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
571 _allDependentPaths
.push_back(dylibCmd
->name());
572 if ( cmd
->cmd() != LC_LOAD_UPWARD_DYLIB
)
573 _downDependentPaths
.push_back(dylibCmd
->name());
574 if ( cmd
->cmd() == LC_REEXPORT_DYLIB
)
575 _reExportPaths
.push_back(dylibCmd
->name());
577 case macho_segment_command
<P
>::CMD
:
578 segCmd
= (macho_segment_command
<P
>*)cmd
;
579 _segCmds
.push_back(segCmd
);
580 if ( strcmp(segCmd
->segname(), "__TEXT") == 0 ) {
581 textSegAddr
= segCmd
->vmaddr();
582 slide
= (uint64_t)mh
- textSegAddr
;
584 else if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) {
585 _linkeditAddr
= segCmd
->vmaddr();
586 _linkeditBias
= (uint8_t*)mh
+ (_linkeditAddr
- textSegAddr
) - segCmd
->fileoff();
587 _linkeditSize
= (uint32_t)segCmd
->vmsize();
589 else if ( segCmd
->nsects() > 0 ) {
590 macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((uint8_t*)segCmd
+ sizeof(macho_segment_command
<P
>));
591 macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
592 for (macho_section
<P
>* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
593 const uint8_t type
= sect
->flags() & SECTION_TYPE
;
594 if ( type
== S_MOD_INIT_FUNC_POINTERS
) {
595 const pint_t
* inits
= (pint_t
*)(sect
->addr()+slide
);
596 const size_t count
= sect
->size() / sizeof(pint_t
);
597 for (size_t j
=0; j
< count
; ++j
) {
598 uint64_t func
= P::getP(inits
[j
]);
599 _initializerAddresses
.push_back(func
);
602 else if ( type
== S_DTRACE_DOF
) {
603 _dofSections
.push_back(sect
);
605 else if ( (strcmp(sect
->sectname(), "__dyld") == 0) && (strncmp(sect
->segname(), "__DATA", 6) == 0) ) {
606 _dyldSectionAddr
= sect
->addr();
611 case LC_SEGMENT_SPLIT_INFO
:
615 uint32_t cmdSize
= cmd
->cmdsize();
616 macho_load_command
<P
>* nextCmd
= (macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmdSize
);
618 ::memmove((void*)cmd
, (void*)nextCmd
, bytesRemaining
);
622 bytesRemaining
-= cmdSize
;
626 // zero out stuff removed
627 ::bzero((void*)cmd
, bytesRemaining
);
629 mh
->set_ncmds(cmdCount
- removedCount
);
630 mh
->set_sizeofcmds(origLoadCommandsSize
- bytesRemaining
);
634 static void dumpLoadCommands(const uint8_t* mheader)
636 const mach_header* const mh = (mach_header*)mheader;
637 const uint32_t cmd_count = mh->ncmds;
638 bool is64 = (mh->magic == MH_MAGIC_64);
639 const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)));
640 const load_command* cmd = cmds;
641 const segment_command* segCmd;
642 const segment_command_64* seg64Cmd;
643 const symtab_command* symTab;
644 const linkedit_data_command* leData;
645 const uint8_t* linkEditBias = NULL;
646 for (uint32_t i = 0; i < cmd_count; ++i) {
649 segCmd = (const segment_command*)cmd;
650 printf("LC_SEGMENT\n");
651 printf(" segname = %s\n", segCmd->segname);
652 printf(" vmaddr = 0x%08X\n", segCmd->vmaddr);
653 printf(" vmsize = 0x%08X\n", segCmd->vmsize);
654 printf(" fileoff = 0x%08X\n", segCmd->fileoff);
655 printf(" filesize = 0x%08X\n", segCmd->filesize);
656 if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
657 linkEditBias = mheader - segCmd->fileoff;
661 seg64Cmd = (const segment_command_64*)cmd;
662 printf("LC_SEGMENT_64\n");
663 printf(" segname = %s\n", seg64Cmd->segname);
664 printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr);
665 printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize);
666 printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff);
667 printf(" filesize = 0x%09llX\n", seg64Cmd->filesize);
668 if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) {
669 linkEditBias = mheader - seg64Cmd->fileoff;
673 symTab = (const symtab_command*)cmd;
674 printf("LC_SYMTAB\n");
675 printf(" symoff = 0x%08X\n", symTab->symoff);
676 printf(" nsyms = 0x%08X\n", symTab->nsyms);
677 printf(" stroff = 0x%08X\n", symTab->stroff);
678 printf(" strsize = 0x%08X\n", symTab->strsize);
680 const char* strPool = (char*)&linkEditBias[symTab->stroff];
681 const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]);
682 printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]);
683 printf(" sym[0].n_type = 0x%02X\n", sym0->n_type);
684 printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect);
685 printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc);
686 printf(" sym[0].n_value = 0x%llX\n", sym0->n_value);
687 const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]);
688 printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]);
689 printf(" sym[1].n_type = 0x%02X\n", sym1->n_type);
690 printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect);
691 printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc);
692 printf(" sym[1].n_value = 0x%llX\n", sym1->n_value);
695 case LC_FUNCTION_STARTS:
696 leData = (const linkedit_data_command*)cmd;
697 printf("LC_FUNCTION_STARTS\n");
698 printf(" dataoff = 0x%08X\n", leData->dataoff);
699 printf(" datasize = 0x%08X\n", leData->datasize);
701 //printf("0x%08X\n", cmd->cmd);
704 cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize);
709 template <typename P
>
710 void LinkeditOptimizer
<P
>::updateLoadCommands(uint32_t mergedLinkeditStartOffset
, uint64_t mergedLinkeditAddr
, uint64_t newLinkeditSize
,
711 uint32_t sharedSymbolTableStartOffset
, uint32_t sharedSymbolTableCount
,
712 uint32_t sharedSymbolStringsOffset
, uint32_t sharedSymbolStringsSize
)
714 // update __LINKEDIT segment in all dylibs to overlap the same shared region
715 for (macho_segment_command
<P
>* segCmd
: _segCmds
) {
716 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) {
717 segCmd
->set_vmaddr(mergedLinkeditAddr
);
718 segCmd
->set_vmsize(newLinkeditSize
);
719 segCmd
->set_fileoff(mergedLinkeditStartOffset
);
720 segCmd
->set_filesize(newLinkeditSize
);
722 else if ( strcmp(segCmd
->segname(), "__TEXT") == 0 ) {
723 // HACK until lldb fixed in: <rdar://problem/20357466> DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache
724 //segCmd->set_fileoff(0);
729 // update symbol table to point to shared symbol table
730 _symTabCmd
->set_symoff(mergedLinkeditStartOffset
+ sharedSymbolTableStartOffset
+ _newLocalSymbolsStartIndex
*sizeof(macho_nlist
<P
>));
731 _symTabCmd
->set_nsyms(_newLocalSymbolCount
+_newExportedSymbolCount
+_newImportedSymbolCount
);
732 _symTabCmd
->set_stroff(mergedLinkeditStartOffset
+ sharedSymbolStringsOffset
);
733 _symTabCmd
->set_strsize(sharedSymbolStringsSize
);
735 // update dynamic symbol table to have proper offsets into shared symbol table
736 _dynSymTabCmd
->set_ilocalsym(0);
737 _dynSymTabCmd
->set_nlocalsym(_newLocalSymbolCount
);
738 _dynSymTabCmd
->set_iextdefsym(_newExportedSymbolsStartIndex
-_newLocalSymbolsStartIndex
);
739 _dynSymTabCmd
->set_nextdefsym(_newExportedSymbolCount
);
740 _dynSymTabCmd
->set_iundefsym(_newImportedSymbolsStartIndex
-_newLocalSymbolsStartIndex
);
741 _dynSymTabCmd
->set_nundefsym(_newImportedSymbolCount
);
742 _dynSymTabCmd
->set_tocoff(0);
743 _dynSymTabCmd
->set_ntoc(0);
744 _dynSymTabCmd
->set_modtaboff(0);
745 _dynSymTabCmd
->set_nmodtab(0);
746 _dynSymTabCmd
->set_indirectsymoff(mergedLinkeditStartOffset
+ _newIndirectSymbolTableOffset
);
747 _dynSymTabCmd
->set_extreloff(0);
748 _dynSymTabCmd
->set_locreloff(0);
749 _dynSymTabCmd
->set_nlocrel(0);
752 if ( _dyldInfo
!= nullptr ) {
753 _dyldInfo
->set_rebase_off(0);
754 _dyldInfo
->set_rebase_size(0);
755 _dyldInfo
->set_bind_off(_dyldInfo
->bind_size() ? mergedLinkeditStartOffset
+ _newBindingInfoOffset
: 0);
756 _dyldInfo
->set_weak_bind_off(_dyldInfo
->weak_bind_size() ? mergedLinkeditStartOffset
+ _newWeakBindingInfoOffset
: 0 );
757 _dyldInfo
->set_lazy_bind_off(_dyldInfo
->lazy_bind_size() ? mergedLinkeditStartOffset
+ _newLazyBindingInfoOffset
: 0 );
758 _dyldInfo
->set_export_off(mergedLinkeditStartOffset
+ _newExportInfoOffset
);
761 // update function-starts
762 if ( _functionStartsCmd
!= nullptr )
763 _functionStartsCmd
->set_dataoff(mergedLinkeditStartOffset
+_newFunctionStartsOffset
);
765 // update data-in-code
766 if ( _dataInCodeCmd
!= nullptr )
767 _dataInCodeCmd
->set_dataoff(mergedLinkeditStartOffset
+_newDataInCodeOffset
);
770 template <typename P
>
771 void LinkeditOptimizer
<P
>::copyWeakBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
773 if ( _dyldInfo
== nullptr )
775 unsigned size
= _dyldInfo
->weak_bind_size();
777 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->weak_bind_off()], size
);
778 _newWeakBindingInfoOffset
= offset
;
779 _newWeakBindingSize
= size
;
785 template <typename P
>
786 void LinkeditOptimizer
<P
>::copyLazyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
788 if ( _dyldInfo
== nullptr )
790 unsigned size
= _dyldInfo
->lazy_bind_size();
792 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->lazy_bind_off()], size
);
793 _newLazyBindingInfoOffset
= offset
;
798 template <typename P
>
799 void LinkeditOptimizer
<P
>::copyBindingInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
801 if ( _dyldInfo
== nullptr )
803 unsigned size
= _dyldInfo
->bind_size();
805 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->bind_off()], size
);
806 _newBindingInfoOffset
= offset
;
811 template <typename P
>
812 void LinkeditOptimizer
<P
>::copyExportInfo(uint8_t* newLinkEditContent
, uint32_t& offset
)
814 if ( _dyldInfo
== nullptr )
816 unsigned size
= _dyldInfo
->export_size();
818 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dyldInfo
->export_off()], size
);
819 _newExportInfoOffset
= offset
;
825 template <typename P
>
826 void LinkeditOptimizer
<P
>::copyFunctionStarts(uint8_t* newLinkEditContent
, uint32_t& offset
)
828 if ( _functionStartsCmd
== nullptr )
830 unsigned size
= _functionStartsCmd
->datasize();
831 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_functionStartsCmd
->dataoff()], size
);
832 _newFunctionStartsOffset
= offset
;
836 template <typename P
>
837 void LinkeditOptimizer
<P
>::copyDataInCode(uint8_t* newLinkEditContent
, uint32_t& offset
)
839 if ( _dataInCodeCmd
== nullptr )
841 unsigned size
= _dataInCodeCmd
->datasize();
842 ::memcpy(&newLinkEditContent
[offset
], &_linkeditBias
[_dataInCodeCmd
->dataoff()], size
);
843 _newDataInCodeOffset
= offset
;
848 template <typename P
>
849 void LinkeditOptimizer
<P
>::copyLocalSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
,
850 bool redact
, std::vector
<LocalSymbolInfo
>& localSymbolInfos
,
851 std::vector
<macho_nlist
<P
>>& unmappedLocalSymbols
, SortedStringPool
<P
>& localSymbolsStringPool
)
853 LocalSymbolInfo localInfo
;
854 localInfo
.dylibOffset
= (uint32_t)(((uint8_t*)_mh
) - (uint8_t*)_cacheBuffer
);
855 localInfo
.nlistStartIndex
= (uint32_t)unmappedLocalSymbols
.size();
856 localInfo
.nlistCount
= 0;
857 _newLocalSymbolsStartIndex
= symbolIndex
;
858 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
859 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
860 const macho_nlist
<P
>* const firstExport
= &symbolTable
[_dynSymTabCmd
->ilocalsym()];
861 const macho_nlist
<P
>* const lastExport
= &symbolTable
[_dynSymTabCmd
->ilocalsym()+_dynSymTabCmd
->nlocalsym()];
862 for (const macho_nlist
<P
>* entry
= firstExport
; entry
< lastExport
; ++entry
) {
863 if ( (entry
->n_type() & N_TYPE
) != N_SECT
)
865 if ( (entry
->n_type() & N_STAB
) != 0)
867 const char* name
= &strings
[entry
->n_strx()];
868 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
869 *newSymbolEntry
= *entry
;
871 // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
872 if ( entry
->n_sect() == 1 ) {
873 stringPool
.add(symbolIndex
, "<redacted>");
875 offset
+= sizeof(macho_nlist
<P
>);
877 // copy local symbol to unmmapped locals area
878 localSymbolsStringPool
.add((uint32_t)unmappedLocalSymbols
.size(), name
);
879 unmappedLocalSymbols
.push_back(*entry
);
880 unmappedLocalSymbols
.back().set_n_strx(0);
883 stringPool
.add(symbolIndex
, name
);
885 offset
+= sizeof(macho_nlist
<P
>);
888 _newLocalSymbolCount
= symbolIndex
- _newLocalSymbolsStartIndex
;
889 localInfo
.nlistCount
= (uint32_t)unmappedLocalSymbols
.size() - localInfo
.nlistStartIndex
;
890 localSymbolInfos
.push_back(localInfo
);
894 template <typename P
>
895 void LinkeditOptimizer
<P
>::copyExportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
)
897 _newExportedSymbolsStartIndex
= symbolIndex
;
898 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
899 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
900 const macho_nlist
<P
>* const firstExport
= &symbolTable
[_dynSymTabCmd
->iextdefsym()];
901 const macho_nlist
<P
>* const lastExport
= &symbolTable
[_dynSymTabCmd
->iextdefsym()+_dynSymTabCmd
->nextdefsym()];
902 uint32_t oldSymbolIndex
= _dynSymTabCmd
->iextdefsym();
903 for (const macho_nlist
<P
>* entry
= firstExport
; entry
< lastExport
; ++entry
, ++oldSymbolIndex
) {
904 if ( (entry
->n_type() & N_TYPE
) != N_SECT
)
906 const char* name
= &strings
[entry
->n_strx()];
907 if ( strncmp(name
, ".objc_", 6) == 0 )
909 if ( strncmp(name
, "$ld$", 4) == 0 )
911 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
912 *newSymbolEntry
= *entry
;
913 newSymbolEntry
->set_n_strx(0);
914 stringPool
.add(symbolIndex
, name
);
915 _oldToNewSymbolIndexes
[oldSymbolIndex
] = symbolIndex
- _newLocalSymbolsStartIndex
;
917 offset
+= sizeof(macho_nlist
<P
>);
919 _newExportedSymbolCount
= symbolIndex
- _newExportedSymbolsStartIndex
;
922 template <typename P
>
923 void LinkeditOptimizer
<P
>::copyImportedSymbols(uint8_t* newLinkEditContent
, SortedStringPool
<P
>& stringPool
, uint32_t& offset
, uint32_t& symbolIndex
)
925 _newImportedSymbolsStartIndex
= symbolIndex
;
926 const char* strings
= (char*)&_linkeditBias
[_symTabCmd
->stroff()];
927 const macho_nlist
<P
>* const symbolTable
= (macho_nlist
<P
>*)(&_linkeditBias
[_symTabCmd
->symoff()]);
928 const macho_nlist
<P
>* const firstImport
= &symbolTable
[_dynSymTabCmd
->iundefsym()];
929 const macho_nlist
<P
>* const lastImport
= &symbolTable
[_dynSymTabCmd
->iundefsym()+_dynSymTabCmd
->nundefsym()];
930 uint32_t oldSymbolIndex
= _dynSymTabCmd
->iundefsym();
931 for (const macho_nlist
<P
>* entry
= firstImport
; entry
< lastImport
; ++entry
, ++oldSymbolIndex
) {
932 if ( (entry
->n_type() & N_TYPE
) != N_UNDF
)
934 const char* name
= &strings
[entry
->n_strx()];
935 macho_nlist
<P
>* newSymbolEntry
= (macho_nlist
<P
>*)&newLinkEditContent
[offset
];
936 *newSymbolEntry
= *entry
;
937 newSymbolEntry
->set_n_strx(0);
938 stringPool
.add(symbolIndex
, name
);
939 _oldToNewSymbolIndexes
[oldSymbolIndex
] = symbolIndex
- _newLocalSymbolsStartIndex
;
941 offset
+= sizeof(macho_nlist
<P
>);
943 _newImportedSymbolCount
= symbolIndex
- _newImportedSymbolsStartIndex
;
946 template <typename P
>
947 void LinkeditOptimizer
<P
>::copyIndirectSymbolTable(uint8_t* newLinkEditContent
, uint32_t& offset
)
949 _newIndirectSymbolTableOffset
= offset
;
950 const uint32_t* const indirectTable
= (uint32_t*)&_linkeditBias
[_dynSymTabCmd
->indirectsymoff()];
951 uint32_t* newIndirectTable
= (uint32_t*)&newLinkEditContent
[offset
];
952 for (uint32_t i
=0; i
< _dynSymTabCmd
->nindirectsyms(); ++i
) {
953 uint32_t symbolIndex
= E::get32(indirectTable
[i
]);
954 if ( (symbolIndex
== INDIRECT_SYMBOL_ABS
) || (symbolIndex
== INDIRECT_SYMBOL_LOCAL
) )
955 E::set32(newIndirectTable
[i
], symbolIndex
);
957 E::set32(newIndirectTable
[i
], _oldToNewSymbolIndexes
[symbolIndex
]);
958 offset
+= sizeof(uint32_t);
962 template <typename P
>
963 void LinkeditOptimizer
<P
>::mergeLinkedits(CacheBuilder
& builder
, std::vector
<LinkeditOptimizer
<P
>*>& optimizers
)
965 // allocate space for new linkedit data
966 uint64_t totalUnoptLinkeditsSize
= builder
._readOnlyRegion
.sizeInUse
- builder
._nonLinkEditReadOnlySize
;
967 uint8_t* newLinkEdit
= (uint8_t*)calloc(totalUnoptLinkeditsSize
, 1);
968 SortedStringPool
<P
> stringPool
;
971 builder
._diagnostics
.verbose("Merged LINKEDIT:\n");
973 // copy weak binding info
974 uint32_t startWeakBindInfosOffset
= offset
;
975 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
976 // Skip chained fixups as the in-place linked list isn't valid any more
977 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)op
->machHeader();
978 if (!mf
->hasChainedFixups())
979 op
->copyWeakBindingInfo(newLinkEdit
, offset
);
981 builder
._diagnostics
.verbose(" weak bindings size: %5uKB\n", (uint32_t)(offset
-startWeakBindInfosOffset
)/1024);
984 uint32_t startExportInfosOffset
= offset
;
985 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
986 op
->copyExportInfo(newLinkEdit
, offset
);
988 builder
._diagnostics
.verbose(" exports info size: %5uKB\n", (uint32_t)(offset
-startExportInfosOffset
)/1024);
990 // in theory, an optimized cache can drop the binding info
993 uint32_t startBindingsInfosOffset
= offset
;
994 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
995 // Skip chained fixups as the in-place linked list isn't valid any more
996 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)op
->machHeader();
997 if (!mf
->hasChainedFixups())
998 op
->copyBindingInfo(newLinkEdit
, offset
);
1000 builder
._diagnostics
.verbose(" bindings size: %5uKB\n", (uint32_t)(offset
-startBindingsInfosOffset
)/1024);
1002 // copy lazy binding info
1003 uint32_t startLazyBindingsInfosOffset
= offset
;
1004 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1005 // Skip chained fixups as the in-place linked list isn't valid any more
1006 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)op
->machHeader();
1007 if (!mf
->hasChainedFixups())
1008 op
->copyLazyBindingInfo(newLinkEdit
, offset
);
1010 builder
._diagnostics
.verbose(" lazy bindings size: %5uKB\n", (offset
-startLazyBindingsInfosOffset
)/1024);
1013 // copy symbol table entries
1014 std::vector
<macho_nlist
<P
>> unmappedLocalSymbols
;
1015 if ( builder
._options
.excludeLocalSymbols
)
1016 unmappedLocalSymbols
.reserve(0x01000000);
1017 std::vector
<LocalSymbolInfo
> localSymbolInfos
;
1018 localSymbolInfos
.reserve(optimizers
.size());
1019 SortedStringPool
<P
> localSymbolsStringPool
;
1020 uint32_t symbolIndex
= 0;
1021 const uint32_t sharedSymbolTableStartOffset
= offset
;
1022 uint32_t sharedSymbolTableExportsCount
= 0;
1023 uint32_t sharedSymbolTableImportsCount
= 0;
1024 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1025 op
->copyLocalSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
, builder
._options
.excludeLocalSymbols
,
1026 localSymbolInfos
, unmappedLocalSymbols
, localSymbolsStringPool
);
1027 uint32_t x
= symbolIndex
;
1028 op
->copyExportedSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
);
1029 sharedSymbolTableExportsCount
+= (symbolIndex
-x
);
1030 uint32_t y
= symbolIndex
;
1031 op
->copyImportedSymbols(newLinkEdit
, stringPool
, offset
, symbolIndex
);
1032 sharedSymbolTableImportsCount
+= (symbolIndex
-y
);
1034 uint32_t sharedSymbolTableCount
= symbolIndex
;
1035 const uint32_t sharedSymbolTableEndOffset
= offset
;
1037 // copy function starts
1038 uint32_t startFunctionStartsOffset
= offset
;
1039 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1040 op
->copyFunctionStarts(newLinkEdit
, offset
);
1042 builder
._diagnostics
.verbose(" function starts size: %5uKB\n", (offset
-startFunctionStartsOffset
)/1024);
1044 // copy data-in-code info
1045 uint32_t startDataInCodeOffset
= offset
;
1046 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1047 op
->copyDataInCode(newLinkEdit
, offset
);
1049 builder
._diagnostics
.verbose(" data in code size: %5uKB\n", (offset
-startDataInCodeOffset
)/1024);
1051 // copy indirect symbol tables
1052 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1053 op
->copyIndirectSymbolTable(newLinkEdit
, offset
);
1055 // if indirect table has odd number of entries, end will not be 8-byte aligned
1056 if ( (offset
% sizeof(typename
P::uint_t
)) != 0 )
1060 uint32_t sharedSymbolStringsOffset
= offset
;
1061 uint32_t sharedSymbolStringsSize
= stringPool
.copyPoolAndUpdateOffsets((char*)&newLinkEdit
[sharedSymbolStringsOffset
], (macho_nlist
<P
>*)&newLinkEdit
[sharedSymbolTableStartOffset
]);
1062 offset
+= sharedSymbolStringsSize
;
1063 uint32_t newLinkeditUnalignedSize
= offset
;
1064 uint64_t newLinkeditAlignedSize
= align(offset
, 14);
1065 builder
._diagnostics
.verbose(" symbol table size: %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset
-sharedSymbolTableStartOffset
)/1024, sharedSymbolTableExportsCount
, sharedSymbolTableImportsCount
);
1066 builder
._diagnostics
.verbose(" symbol string pool size: %5uKB\n", sharedSymbolStringsSize
/1024);
1067 builder
._sharedStringsPoolVmOffset
= (uint32_t)((builder
._readOnlyRegion
.unslidLoadAddress
- builder
._readExecuteRegion
.unslidLoadAddress
) + builder
._nonLinkEditReadOnlySize
+ sharedSymbolStringsOffset
);
1069 // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
1070 builder
._diagnostics
.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize
/(1024*1024), (uint32_t)newLinkeditUnalignedSize
/(1024*1024));
1071 ::memcpy(builder
._readOnlyRegion
.buffer
+builder
._nonLinkEditReadOnlySize
, newLinkEdit
, newLinkeditAlignedSize
);
1072 ::free(newLinkEdit
);
1073 builder
._readOnlyRegion
.sizeInUse
= builder
._nonLinkEditReadOnlySize
+ newLinkeditAlignedSize
;
1075 // If making cache for customers, add extra accelerator tables for dyld
1076 DyldSharedCache
* cacheHeader
= (DyldSharedCache
*)builder
._readExecuteRegion
.buffer
;
1077 if ( builder
._options
.optimizeStubs
) {
1078 uint64_t addrWhereAccTablesWillBe
= builder
._readOnlyRegion
.unslidLoadAddress
+builder
._readOnlyRegion
.sizeInUse
;
1079 uint64_t addrWhereMergedLinkWillStart
= builder
._readOnlyRegion
.unslidLoadAddress
+builder
._nonLinkEditReadOnlySize
;
1080 AcceleratorTables
<P
> tables(cacheHeader
, addrWhereMergedLinkWillStart
, builder
._diagnostics
, optimizers
);
1081 uint32_t tablesSize
= tables
.totalSize();
1082 if ( tablesSize
< (builder
._readOnlyRegion
.bufferSize
- builder
._readOnlyRegion
.sizeInUse
) ) {
1083 tables
.copyTo(builder
._readOnlyRegion
.buffer
+builder
._readOnlyRegion
.sizeInUse
);
1084 cacheHeader
->header
.accelerateInfoAddr
= addrWhereAccTablesWillBe
;
1085 cacheHeader
->header
.accelerateInfoSize
= tablesSize
;
1086 builder
._readOnlyRegion
.sizeInUse
+= align(tablesSize
, 14);
1087 builder
._diagnostics
.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize
/(1024*1024));
1090 builder
._diagnostics
.warning("not enough room to add dyld accelerator tables");
1094 // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
1095 if ( builder
._options
.excludeLocalSymbols
) {
1096 const uint32_t entriesOffset
= sizeof(dyld_cache_local_symbols_info
);
1097 const uint32_t entriesCount
= (uint32_t)localSymbolInfos
.size();
1098 const uint32_t nlistOffset
= (uint32_t)align(entriesOffset
+ entriesCount
* sizeof(dyld_cache_local_symbols_info
), 4); // 16-byte align start
1099 const uint32_t nlistCount
= (uint32_t)unmappedLocalSymbols
.size();
1100 const uint32_t stringsSize
= (uint32_t)localSymbolsStringPool
.size();
1101 const uint32_t stringsOffset
= nlistOffset
+ nlistCount
* sizeof(macho_nlist
<P
>);
1102 // allocate buffer for local symbols
1103 const size_t localsBufferSize
= align(stringsOffset
+ stringsSize
, 14);
1104 vm_address_t localsBuffer
;
1105 if ( ::vm_allocate(mach_task_self(), &localsBuffer
, localsBufferSize
, VM_FLAGS_ANYWHERE
) == 0 ) {
1106 dyld_cache_local_symbols_info
* infoHeader
= (dyld_cache_local_symbols_info
*)localsBuffer
;
1107 // fill in header info
1108 infoHeader
->nlistOffset
= nlistOffset
;
1109 infoHeader
->nlistCount
= nlistCount
;
1110 infoHeader
->stringsOffset
= stringsOffset
;
1111 infoHeader
->stringsSize
= stringsSize
;
1112 infoHeader
->entriesOffset
= entriesOffset
;
1113 infoHeader
->entriesCount
= entriesCount
;
1114 // copy info for each dylib
1115 dyld_cache_local_symbols_entry
* entries
= (dyld_cache_local_symbols_entry
*)(((uint8_t*)infoHeader
)+entriesOffset
);
1116 for (uint32_t i
=0; i
< entriesCount
; ++i
) {
1117 entries
[i
].dylibOffset
= localSymbolInfos
[i
].dylibOffset
;
1118 entries
[i
].nlistStartIndex
= localSymbolInfos
[i
].nlistStartIndex
;
1119 entries
[i
].nlistCount
= localSymbolInfos
[i
].nlistCount
;
1122 macho_nlist
<P
>* newLocalsSymbolTable
= (macho_nlist
<P
>*)(localsBuffer
+nlistOffset
);
1123 ::memcpy(newLocalsSymbolTable
, &unmappedLocalSymbols
[0], nlistCount
*sizeof(macho_nlist
<P
>));
1125 localSymbolsStringPool
.copyPoolAndUpdateOffsets(((char*)infoHeader
)+stringsOffset
, newLocalsSymbolTable
);
1126 // update cache header
1127 cacheHeader
->header
.localSymbolsSize
= localsBufferSize
;
1128 // return buffer of local symbols, caller to free() it
1129 builder
._localSymbolsRegion
.buffer
= (uint8_t*)localsBuffer
;
1130 builder
._localSymbolsRegion
.bufferSize
= localsBufferSize
;
1131 builder
._localSymbolsRegion
.sizeInUse
= localsBufferSize
;
1134 builder
._diagnostics
.warning("could not allocate local symbols");
1138 // update all load commands to new merged layout
1139 uint64_t linkeditsUnslidStartAddr
= builder
._readOnlyRegion
.unslidLoadAddress
+ builder
._nonLinkEditReadOnlySize
;
1140 uint32_t linkeditsCacheFileOffset
= (uint32_t)(builder
._readOnlyRegion
.cacheFileOffset
+ builder
._nonLinkEditReadOnlySize
);
1141 for (LinkeditOptimizer
<P
>* op
: optimizers
) {
1142 op
->updateLoadCommands(linkeditsCacheFileOffset
, linkeditsUnslidStartAddr
, newLinkeditUnalignedSize
,
1143 sharedSymbolTableStartOffset
, sharedSymbolTableCount
,
1144 sharedSymbolStringsOffset
, sharedSymbolStringsSize
);
1149 template <typename P
>
1150 void LinkeditOptimizer
<P
>::optimizeLinkedit(CacheBuilder
& builder
)
1152 DyldSharedCache
* cache
= (DyldSharedCache
*)builder
._readExecuteRegion
.buffer
;
1153 // construct a LinkeditOptimizer for each image
1154 __block
std::vector
<LinkeditOptimizer
<P
>*> optimizers
;
1155 cache
->forEachImage(^(const mach_header
* mh
, const char*) {
1156 optimizers
.push_back(new LinkeditOptimizer
<P
>(cache
, (macho_header
<P
>*)mh
, builder
._diagnostics
));
1159 // add optimizer for each branch pool
1160 for (uint64_t poolOffset
: branchPoolOffsets
) {
1161 macho_header
<P
>* mh
= (macho_header
<P
>*)((char*)cache
+ poolOffset
);
1162 optimizers
.push_back(new LinkeditOptimizer
<P
>(cache
, mh
, diag
));
1165 // merge linkedit info
1166 mergeLinkedits(builder
, optimizers
);
1168 // delete optimizers
1169 for (LinkeditOptimizer
<P
>* op
: optimizers
)
1173 void CacheBuilder::optimizeLinkedit(const std::vector
<uint64_t>& branchPoolOffsets
)
1175 if ( _archLayout
->is64
) {
1176 return LinkeditOptimizer
<Pointer64
<LittleEndian
>>::optimizeLinkedit(*this);
1179 return LinkeditOptimizer
<Pointer32
<LittleEndian
>>::optimizeLinkedit(*this);