]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/OptimizerLinkedit.cpp
dyld-519.2.1.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / OptimizerLinkedit.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26 #include <dirent.h>
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
31 #include <assert.h>
32
33 #include <fstream>
34 #include <string>
35 #include <algorithm>
36 #include <unordered_map>
37 #include <unordered_set>
38
39 #include "MachOFileAbstraction.hpp"
40 #include "Trie.hpp"
41 #include "DyldSharedCache.h"
42 #include "CacheBuilder.h"
43
44
45 #define ALIGN_AS_TYPE(value, type) \
46 ((value + alignof(type) - 1) & (-alignof(type)))
47
48 namespace {
49
50 template <typename P>
51 class SortedStringPool
52 {
53 public:
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);
57 }
58
59 // copy sorted strings to buffer and update all symbol's string offsets
60 uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist<P>* symbolTable) {
61 // make sorted list of strings
62 std::vector<std::string> allStrings;
63 allStrings.reserve(_map.size());
64 for (auto& entry : _map) {
65 allStrings.push_back(entry.first);
66 }
67 std::sort(allStrings.begin(), allStrings.end());
68 // walk sorted list of strings
69 dstStringPool[0] = '\0'; // tradition for start of pool to be empty string
70 uint32_t poolOffset = 1;
71 for (const std::string& symName : allStrings) {
72 // append string to pool
73 strcpy(&dstStringPool[poolOffset], symName.c_str());
74 // set each string offset of each symbol using it
75 for (uint32_t symbolIndex : _map[symName]) {
76 symbolTable[symbolIndex].set_n_strx(poolOffset);
77 }
78 poolOffset += symName.size() + 1;
79 }
80 // return size of pool
81 return poolOffset;
82 }
83
84 size_t size() {
85 size_t size = 1;
86 for (auto& entry : _map) {
87 size += (entry.first.size() + 1);
88 }
89 return size;
90 }
91
92
93 private:
94 std::unordered_map<std::string, std::vector<uint32_t>> _map;
95 };
96
97
98
99
100 struct LocalSymbolInfo
101 {
102 uint32_t dylibOffset;
103 uint32_t nlistStartIndex;
104 uint32_t nlistCount;
105 };
106
107
108 template <typename P>
109 class LinkeditOptimizer {
110 public:
111 LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag);
112
113 uint32_t linkeditSize() { return _linkeditSize; }
114 uint32_t linkeditOffset() { return _linkeditCacheOffset; }
115 uint64_t linkeditAddr() { return _linkeditAddr; }
116 const char* installName() { return _installName; }
117 void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
118 void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
119 void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
120 void copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset);
121 void copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
122 void copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
123 void copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
124 bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
125 std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool);
126 void copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset);
127 void copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset);
128 void copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset);
129 void updateLoadCommands(uint32_t linkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
130 uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
131 uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize);
132
133 macho_header<P>* machHeader() { return _mh; }
134 const std::vector<const char*> getDownwardDependents() { return _downDependentPaths; }
135 const std::vector<const char*> getAllDependents() { return _allDependentPaths; }
136 const std::vector<const char*> getReExportPaths() { return _reExportPaths; }
137 const std::vector<uint64_t> initializerAddresses() { return _initializerAddresses; }
138 const std::vector<macho_section<P>*> dofSections() { return _dofSections; }
139 uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset; }
140 uint32_t exportsTrieLinkEditSize() { return _exportInfoSize; }
141 uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset; }
142 uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize; }
143 uint64_t dyldSectionAddress() { return _dyldSectionAddr; }
144 const std::vector<macho_segment_command<P>*>& segCmds() { return _segCmds; }
145
146
147 private:
148
149 typedef typename P::uint_t pint_t;
150 typedef typename P::E E;
151
152 macho_header<P>* _mh;
153 void* _cacheBuffer;
154 Diagnostics& _diagnostics;
155 uint32_t _linkeditSize = 0;
156 uint32_t _linkeditCacheOffset = 0;
157 uint64_t _linkeditAddr = 0;
158 const uint8_t* _linkeditBias = nullptr;
159 const char* _installName = nullptr;
160 macho_symtab_command<P>* _symTabCmd = nullptr;
161 macho_dysymtab_command<P>* _dynSymTabCmd = nullptr;
162 macho_dyld_info_command<P>* _dyldInfo = nullptr;
163 macho_linkedit_data_command<P>* _functionStartsCmd = nullptr;
164 macho_linkedit_data_command<P>* _dataInCodeCmd = nullptr;
165 std::vector<macho_segment_command<P>*> _segCmds;
166 std::unordered_map<uint32_t,uint32_t> _oldToNewSymbolIndexes;
167 std::vector<const char*> _reExportPaths;
168 std::vector<const char*> _downDependentPaths;
169 std::vector<const char*> _allDependentPaths;
170 std::vector<uint64_t> _initializerAddresses;
171 std::vector<macho_section<P>*> _dofSections;
172 uint32_t _newWeakBindingInfoOffset = 0;
173 uint32_t _newLazyBindingInfoOffset = 0;
174 uint32_t _newBindingInfoOffset = 0;
175 uint32_t _newExportInfoOffset = 0;
176 uint32_t _exportInfoSize = 0;
177 uint32_t _newWeakBindingSize = 0;
178 uint32_t _newExportedSymbolsStartIndex = 0;
179 uint32_t _newExportedSymbolCount = 0;
180 uint32_t _newImportedSymbolsStartIndex = 0;
181 uint32_t _newImportedSymbolCount = 0;
182 uint32_t _newLocalSymbolsStartIndex = 0;
183 uint32_t _newLocalSymbolCount = 0;
184 uint32_t _newFunctionStartsOffset = 0;
185 uint32_t _newDataInCodeOffset = 0;
186 uint32_t _newIndirectSymbolTableOffset = 0;
187 uint64_t _dyldSectionAddr = 0;
188 };
189
190
191
192 template <typename P>
193 class AcceleratorTables {
194 public:
195 AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
196
197 uint32_t totalSize() const;
198 void copyTo(uint8_t* buffer);
199
200 private:
201 typedef typename P::E E;
202
203 struct NodeChain;
204
205 struct DepNode {
206 std::vector<DepNode*> _dependents;
207 unsigned _depth;
208 const char* _installName;
209
210 DepNode() : _depth(0), _installName(nullptr) { }
211 void computeDepth();
212 static void verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
213 };
214
215 struct NodeChain {
216 NodeChain* prev;
217 DepNode* node;
218 };
219
220 std::unordered_map<macho_header<P>*, DepNode> _depDAG;
221 std::vector<dyld_cache_image_info_extra> _extraInfo;
222 std::vector<uint8_t> _trieBytes;
223 std::vector<uint16_t> _reExportArray;
224 std::vector<uint16_t> _dependencyArray;
225 std::vector<uint16_t> _bottomUpArray;
226 std::vector<dyld_cache_accelerator_initializer> _initializers;
227 std::vector<dyld_cache_accelerator_dof> _dofSections;
228 std::vector<dyld_cache_range_entry> _rangeTable;
229 std::unordered_map<macho_header<P>*, uint32_t> _machHeaderToImageIndex;
230 std::unordered_map<std::string, macho_header<P>*> _dylibPathToMachHeader;
231 std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*> _machHeaderToOptimizer;
232 dyld_cache_accelerator_info _acceleratorInfoHeader;
233 };
234
235
236 template <typename P>
237 void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
238 std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
239 for (DepNode* node : from) {
240 bool foundCycle = (node == target);
241 for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
242 if ( c->node == target ) {
243 foundCycle = true;
244 break;
245 }
246 }
247 if ( foundCycle ) {
248 NodeChain* chp = &chain;
249 std::string msg = std::string("found cycle for ") + target->_installName;
250 while (chp != nullptr) {
251 msg = msg + "\n " + chp->node->_installName;
252 chp = chp->prev;
253 }
254 diag.warning("%s", msg.c_str());
255 return;
256 }
257
258 if ( visitedNodes.count(node) )
259 continue;
260 visitedNodes.insert(node);
261 NodeChain nextChain;
262 nextChain.prev = &chain;
263 nextChain.node = node;
264 verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
265 }
266 }
267
268 const uint16_t kBranchIslandDylibIndex = 0x7FFF;
269
270 template <typename P>
271 AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
272 {
273 // build table mapping tables to map between mach_header, index, and optimizer
274 for ( LinkeditOptimizer<P>* op : optimizers ) {
275 _machHeaderToOptimizer[op->machHeader()] = op;
276 }
277 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
278 uint64_t cacheStartAddress = mappings[0].address;
279 const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
280 for (unsigned i=0; i < cache->header.imagesCount; ++i) {
281 uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
282 macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
283 const char* path = (char*)cache + 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;
288 }
289
290
291 // build DAG of image dependencies
292 for (LinkeditOptimizer<P>* op : optimizers) {
293 _depDAG[op->machHeader()]._installName = op->installName();
294 }
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);
302 }
303 }
304
305 // check for cycles in DAG
306 for (auto& entry : _depDAG) {
307 DepNode* node = &entry.second;
308 NodeChain chain;
309 chain.prev = nullptr;
310 chain.node = node;
311 std::unordered_set<DepNode*> visitedNodes;
312 DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents);
313 }
314
315 // compute depth for each DAG node
316 for (auto& entry : _depDAG) {
317 entry.second.computeDepth();
318 }
319
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());
326 else
327 _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
328 }
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);
333 else
334 return (lmh < rmh);
335 });
336
337 // build zeroed array of extra infos
338 dyld_cache_image_info_extra emptyExtra;
339 emptyExtra.exportsTrieAddr = 0;
340 emptyExtra.weakBindingsAddr = 0;
341 emptyExtra.exportsTrieSize = 0;
342 emptyExtra.weakBindingsSize = 0;
343 emptyExtra.dependentsStartArrayIndex = 0;
344 emptyExtra.reExportsStartArrayIndex = 0;
345 _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
346
347 //for ( macho_header<P>* mh : sortedMachHeaders ) {
348 // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
349 //}
350
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].dependentsStartArrayIndex = 0;
359 }
360 else {
361 _extraInfo[index].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())
367 depIndex |= 0x8000;
368 _dependencyArray.push_back(depIndex);
369 }
370 _dependencyArray.push_back(0xFFFF); // mark end of list
371 }
372 }
373
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].reExportsStartArrayIndex = 0;
382 }
383 else {
384 _extraInfo[index].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);
389 }
390 _reExportArray.push_back(0xFFFF); // mark end of list
391 }
392 }
393
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 dyld_cache_accelerator_initializer entry;
402 entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
403 entry.imageIndex = _machHeaderToImageIndex[mh];
404 _initializers.push_back(entry);
405 }
406 }
407
408 // build ordered list of DOF sections
409 for (macho_header<P>* mh : sortedMachHeaders) {
410 LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
411 assert(op != NULL);
412 unsigned imageIndex = _machHeaderToImageIndex[mh];
413 for (auto& sect : op->dofSections()) {
414 //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
415 dyld_cache_accelerator_dof entry;
416 entry.sectionAddress = sect->addr();
417 entry.sectionSize = (uint32_t)sect->size();
418 entry.imageIndex = imageIndex;
419 _dofSections.push_back(entry);
420 }
421 }
422
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].exportsTrieAddr = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
428 _extraInfo[index].exportsTrieSize = op->exportsTrieLinkEditSize();
429 _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
430 _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
431 }
432
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();
437
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 )
444 continue;
445 dyld_cache_range_entry entry;
446 entry.startAddress = segCmd->vmaddr();
447 entry.size = (uint32_t)segCmd->vmsize();
448 entry.imageIndex = imageIndex;
449 _rangeTable.push_back(entry);
450 }
451 }
452 std::sort(_rangeTable.begin(), _rangeTable.end(),
453 [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
454 return (lRange.startAddress < rRange.startAddress);
455 });
456
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)));
463 }
464 DylibIndexTrie dylibsTrie(dylibEntrys);
465 dylibsTrie.emit(_trieBytes);
466 while ( (_trieBytes.size() % 4) != 0 )
467 _trieBytes.push_back(0);
468
469 // fill out header
470 _acceleratorInfoHeader.version = 1;
471 _acceleratorInfoHeader.imageExtrasCount = (uint32_t)_extraInfo.size();
472 _acceleratorInfoHeader.imagesExtrasOffset = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
473 _acceleratorInfoHeader.bottomUpListOffset = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
474 _acceleratorInfoHeader.dylibTrieOffset = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
475 _acceleratorInfoHeader.dylibTrieSize = (uint32_t)_trieBytes.size();
476 _acceleratorInfoHeader.initializersOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
477 _acceleratorInfoHeader.initializersCount = (uint32_t)_initializers.size();
478 _acceleratorInfoHeader.dofSectionsOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
479 _acceleratorInfoHeader.dofSectionsCount = (uint32_t)_dofSections.size();
480 _acceleratorInfoHeader.reExportListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
481 _acceleratorInfoHeader.reExportCount = (uint32_t)_reExportArray.size();
482 _acceleratorInfoHeader.depListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
483 _acceleratorInfoHeader.depListCount = (uint32_t)_dependencyArray.size();
484 _acceleratorInfoHeader.rangeTableOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
485 _acceleratorInfoHeader.rangeTableCount = (uint32_t)_rangeTable.size();
486 _acceleratorInfoHeader.dyldSectionAddr = dyldSectionAddr;
487 }
488
489
490 template <typename P>
491 void AcceleratorTables<P>::DepNode::computeDepth()
492 {
493 if ( _depth != 0 )
494 return;
495 _depth = 1;
496 for (DepNode* node : _dependents) {
497 node->computeDepth();
498 if ( node->_depth >= _depth )
499 _depth = node->_depth + 1;
500 }
501 }
502
503 template <typename P>
504 uint32_t AcceleratorTables<P>::totalSize() const
505 {
506 return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
507 }
508
509 template <typename P>
510 void AcceleratorTables<P>::copyTo(uint8_t* buffer)
511 {
512 memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
513 memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
514 memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t));
515 memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
516 memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t));
517 memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
518 memcpy(&buffer[_acceleratorInfoHeader.depListOffset], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
519 memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry));
520 memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset], &_trieBytes[0], _trieBytes.size());
521 }
522
523
524
525 template <typename P>
526 LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
527 : _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
528 {
529 _linkeditBias = (uint8_t*)cacheBuffer;
530 const unsigned origLoadCommandsSize = mh->sizeofcmds();
531 unsigned bytesRemaining = origLoadCommandsSize;
532 unsigned removedCount = 0;
533 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
534 const uint32_t cmdCount = mh->ncmds();
535 const macho_load_command<P>* cmd = cmds;
536 const macho_dylib_command<P>* dylibCmd;
537 const macho_routines_command<P>* routinesCmd;
538 macho_segment_command<P>* segCmd;
539 for (uint32_t i = 0; i < cmdCount; ++i) {
540 bool remove = false;
541 switch (cmd->cmd()) {
542 case LC_ID_DYLIB:
543 _installName = ((macho_dylib_command<P>*)cmd)->name();
544 break;
545 case LC_SYMTAB:
546 _symTabCmd = (macho_symtab_command<P>*)cmd;
547 break;
548 case LC_DYSYMTAB:
549 _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
550 break;
551 case LC_DYLD_INFO:
552 case LC_DYLD_INFO_ONLY:
553 _dyldInfo = (macho_dyld_info_command<P>*)cmd;
554 _exportInfoSize = _dyldInfo->export_size();
555 break;
556 case LC_FUNCTION_STARTS:
557 _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
558 break;
559 case LC_DATA_IN_CODE:
560 _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
561 break;
562 case LC_ROUTINES:
563 case LC_ROUTINES_64:
564 routinesCmd = (macho_routines_command<P>*)cmd;
565 _initializerAddresses.push_back(routinesCmd->init_address());
566 break;
567 case LC_REEXPORT_DYLIB:
568 case LC_LOAD_DYLIB:
569 case LC_LOAD_WEAK_DYLIB:
570 case LC_LOAD_UPWARD_DYLIB:
571 dylibCmd = (macho_dylib_command<P>*)cmd;
572 _allDependentPaths.push_back(dylibCmd->name());
573 if ( cmd->cmd() != LC_LOAD_UPWARD_DYLIB )
574 _downDependentPaths.push_back(dylibCmd->name());
575 if ( cmd->cmd() == LC_REEXPORT_DYLIB )
576 _reExportPaths.push_back(dylibCmd->name());
577 break;
578 case macho_segment_command<P>::CMD:
579 segCmd = (macho_segment_command<P>*)cmd;
580 _segCmds.push_back(segCmd);
581 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
582 _linkeditSize = (uint32_t)segCmd->vmsize();
583 _linkeditCacheOffset = (uint32_t)segCmd->fileoff();
584 _linkeditAddr = segCmd->vmaddr();
585 }
586 else if ( segCmd->nsects() > 0 ) {
587 macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
588 macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
589 for (macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
590 const uint8_t type = sect->flags() & SECTION_TYPE;
591 if ( type == S_MOD_INIT_FUNC_POINTERS ) {
592 const pint_t* inits = (pint_t*)((char*)cacheBuffer + sect->offset());
593 const size_t count = sect->size() / sizeof(pint_t);
594 for (size_t j=0; j < count; ++j) {
595 uint64_t func = P::getP(inits[j]);
596 _initializerAddresses.push_back(func);
597 }
598 }
599 else if ( type == S_DTRACE_DOF ) {
600 _dofSections.push_back(sect);
601 }
602 else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) {
603 _dyldSectionAddr = sect->addr();
604 }
605 }
606 }
607 break;
608 case LC_SEGMENT_SPLIT_INFO:
609 remove = true;
610 break;
611 }
612 uint32_t cmdSize = cmd->cmdsize();
613 macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
614 if ( remove ) {
615 ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
616 ++removedCount;
617 }
618 else {
619 bytesRemaining -= cmdSize;
620 cmd = nextCmd;
621 }
622 }
623 // zero out stuff removed
624 ::bzero((void*)cmd, bytesRemaining);
625 // update header
626 mh->set_ncmds(cmdCount - removedCount);
627 mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
628 }
629
630 /*
631 static void dumpLoadCommands(const uint8_t* mheader)
632 {
633 const mach_header* const mh = (mach_header*)mheader;
634 const uint32_t cmd_count = mh->ncmds;
635 bool is64 = (mh->magic == MH_MAGIC_64);
636 const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)));
637 const load_command* cmd = cmds;
638 const segment_command* segCmd;
639 const segment_command_64* seg64Cmd;
640 const symtab_command* symTab;
641 const linkedit_data_command* leData;
642 const uint8_t* linkEditBias = NULL;
643 for (uint32_t i = 0; i < cmd_count; ++i) {
644 switch (cmd->cmd) {
645 case LC_SEGMENT:
646 segCmd = (const segment_command*)cmd;
647 printf("LC_SEGMENT\n");
648 printf(" segname = %s\n", segCmd->segname);
649 printf(" vmaddr = 0x%08X\n", segCmd->vmaddr);
650 printf(" vmsize = 0x%08X\n", segCmd->vmsize);
651 printf(" fileoff = 0x%08X\n", segCmd->fileoff);
652 printf(" filesize = 0x%08X\n", segCmd->filesize);
653 if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
654 linkEditBias = mheader - segCmd->fileoff;
655 }
656 break;
657 case LC_SEGMENT_64:
658 seg64Cmd = (const segment_command_64*)cmd;
659 printf("LC_SEGMENT_64\n");
660 printf(" segname = %s\n", seg64Cmd->segname);
661 printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr);
662 printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize);
663 printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff);
664 printf(" filesize = 0x%09llX\n", seg64Cmd->filesize);
665 if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) {
666 linkEditBias = mheader - seg64Cmd->fileoff;
667 }
668 break;
669 case LC_SYMTAB:
670 symTab = (const symtab_command*)cmd;
671 printf("LC_SYMTAB\n");
672 printf(" symoff = 0x%08X\n", symTab->symoff);
673 printf(" nsyms = 0x%08X\n", symTab->nsyms);
674 printf(" stroff = 0x%08X\n", symTab->stroff);
675 printf(" strsize = 0x%08X\n", symTab->strsize);
676 {
677 const char* strPool = (char*)&linkEditBias[symTab->stroff];
678 const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]);
679 printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]);
680 printf(" sym[0].n_type = 0x%02X\n", sym0->n_type);
681 printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect);
682 printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc);
683 printf(" sym[0].n_value = 0x%llX\n", sym0->n_value);
684 const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]);
685 printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]);
686 printf(" sym[1].n_type = 0x%02X\n", sym1->n_type);
687 printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect);
688 printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc);
689 printf(" sym[1].n_value = 0x%llX\n", sym1->n_value);
690 }
691 break;
692 case LC_FUNCTION_STARTS:
693 leData = (const linkedit_data_command*)cmd;
694 printf("LC_FUNCTION_STARTS\n");
695 printf(" dataoff = 0x%08X\n", leData->dataoff);
696 printf(" datasize = 0x%08X\n", leData->datasize);
697 default:
698 //printf("0x%08X\n", cmd->cmd);
699 break;
700 }
701 cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize);
702 }
703 }
704 */
705
706 template <typename P>
707 void LinkeditOptimizer<P>::updateLoadCommands(uint32_t mergedLinkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
708 uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
709 uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize)
710 {
711 // update __LINKEDIT segment in all dylibs to overlap the same shared region
712 for (macho_segment_command<P>* segCmd : _segCmds) {
713 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
714 segCmd->set_vmaddr(mergedLinkeditAddr);
715 segCmd->set_vmsize(newLinkeditSize);
716 segCmd->set_fileoff(mergedLinkeditStartOffset);
717 segCmd->set_filesize(newLinkeditSize);
718 }
719 else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
720 // HACK until lldb fixed in: <rdar://problem/20357466> DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache
721 //segCmd->set_fileoff(0);
722
723 }
724 }
725
726 // update symbol table to point to shared symbol table
727 _symTabCmd->set_symoff(mergedLinkeditStartOffset + sharedSymbolTableStartOffset + _newLocalSymbolsStartIndex*sizeof(macho_nlist<P>));
728 _symTabCmd->set_nsyms(_newLocalSymbolCount+_newExportedSymbolCount+_newImportedSymbolCount);
729 _symTabCmd->set_stroff(mergedLinkeditStartOffset + sharedSymbolStringsOffset);
730 _symTabCmd->set_strsize(sharedSymbolStringsSize);
731
732 // update dynamic symbol table to have proper offsets into shared symbol table
733 _dynSymTabCmd->set_ilocalsym(0);
734 _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
735 _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
736 _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
737 _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
738 _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
739 _dynSymTabCmd->set_tocoff(0);
740 _dynSymTabCmd->set_ntoc(0);
741 _dynSymTabCmd->set_modtaboff(0);
742 _dynSymTabCmd->set_nmodtab(0);
743 _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
744 _dynSymTabCmd->set_extreloff(0);
745 _dynSymTabCmd->set_locreloff(0);
746 _dynSymTabCmd->set_nlocrel(0);
747
748 // update dyld info
749 if ( _dyldInfo != nullptr ) {
750 _dyldInfo->set_rebase_off(0);
751 _dyldInfo->set_rebase_size(0);
752 _dyldInfo->set_bind_off(_dyldInfo->bind_size() ? mergedLinkeditStartOffset + _newBindingInfoOffset : 0);
753 _dyldInfo->set_weak_bind_off(_dyldInfo->weak_bind_size() ? mergedLinkeditStartOffset + _newWeakBindingInfoOffset : 0 );
754 _dyldInfo->set_lazy_bind_off(_dyldInfo->lazy_bind_size() ? mergedLinkeditStartOffset + _newLazyBindingInfoOffset : 0 );
755 _dyldInfo->set_export_off(mergedLinkeditStartOffset + _newExportInfoOffset);
756 }
757
758 // update function-starts
759 if ( _functionStartsCmd != nullptr )
760 _functionStartsCmd->set_dataoff(mergedLinkeditStartOffset+_newFunctionStartsOffset);
761
762 // update data-in-code
763 if ( _dataInCodeCmd != nullptr )
764 _dataInCodeCmd->set_dataoff(mergedLinkeditStartOffset+_newDataInCodeOffset);
765 }
766
767 template <typename P>
768 void LinkeditOptimizer<P>::copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
769 {
770 if ( _dyldInfo == nullptr )
771 return;
772 unsigned size = _dyldInfo->weak_bind_size();
773 if ( size != 0 ) {
774 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->weak_bind_off()], size);
775 _newWeakBindingInfoOffset = offset;
776 _newWeakBindingSize = size;
777 offset += size;
778 }
779 }
780
781
782 template <typename P>
783 void LinkeditOptimizer<P>::copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
784 {
785 if ( _dyldInfo == nullptr )
786 return;
787 unsigned size = _dyldInfo->lazy_bind_size();
788 if ( size != 0 ) {
789 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->lazy_bind_off()], size);
790 _newLazyBindingInfoOffset = offset;
791 offset += size;
792 }
793 }
794
795 template <typename P>
796 void LinkeditOptimizer<P>::copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
797 {
798 if ( _dyldInfo == nullptr )
799 return;
800 unsigned size = _dyldInfo->bind_size();
801 if ( size != 0 ) {
802 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->bind_off()], size);
803 _newBindingInfoOffset = offset;
804 offset += size;
805 }
806 }
807
808 template <typename P>
809 void LinkeditOptimizer<P>::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset)
810 {
811 if ( _dyldInfo == nullptr )
812 return;
813 unsigned size = _dyldInfo->export_size();
814 if ( size != 0 ) {
815 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size);
816 _newExportInfoOffset = offset;
817 offset += size;
818 }
819 }
820
821
822 template <typename P>
823 void LinkeditOptimizer<P>::copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset)
824 {
825 if ( _functionStartsCmd == nullptr )
826 return;
827 unsigned size = _functionStartsCmd->datasize();
828 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_functionStartsCmd->dataoff()], size);
829 _newFunctionStartsOffset = offset;
830 offset += size;
831 }
832
833 template <typename P>
834 void LinkeditOptimizer<P>::copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset)
835 {
836 if ( _dataInCodeCmd == nullptr )
837 return;
838 unsigned size = _dataInCodeCmd->datasize();
839 ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dataInCodeCmd->dataoff()], size);
840 _newDataInCodeOffset = offset;
841 offset += size;
842 }
843
844
845 template <typename P>
846 void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
847 bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
848 std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool)
849 {
850 LocalSymbolInfo localInfo;
851 localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer);
852 localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size();
853 localInfo.nlistCount = 0;
854 _newLocalSymbolsStartIndex = symbolIndex;
855 const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
856 const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
857 const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
858 const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
859 for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry) {
860 if ( (entry->n_type() & N_TYPE) != N_SECT)
861 continue;
862 if ( (entry->n_type() & N_STAB) != 0)
863 continue;
864 const char* name = &strings[entry->n_strx()];
865 macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
866 *newSymbolEntry = *entry;
867 if ( redact ) {
868 // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
869 if ( entry->n_sect() == 1 ) {
870 stringPool.add(symbolIndex, "<redacted>");
871 ++symbolIndex;
872 offset += sizeof(macho_nlist<P>);
873 }
874 // copy local symbol to unmmapped locals area
875 localSymbolsStringPool.add((uint32_t)unmappedLocalSymbols.size(), name);
876 unmappedLocalSymbols.push_back(*entry);
877 unmappedLocalSymbols.back().set_n_strx(0);
878 }
879 else {
880 stringPool.add(symbolIndex, name);
881 ++symbolIndex;
882 offset += sizeof(macho_nlist<P>);
883 }
884 }
885 _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
886 localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
887 localSymbolInfos.push_back(localInfo);
888 }
889
890
891 template <typename P>
892 void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
893 {
894 _newExportedSymbolsStartIndex = symbolIndex;
895 const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
896 const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
897 const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
898 const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
899 uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym();
900 for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry, ++oldSymbolIndex) {
901 if ( (entry->n_type() & N_TYPE) != N_SECT)
902 continue;
903 const char* name = &strings[entry->n_strx()];
904 if ( strncmp(name, ".objc_", 6) == 0 )
905 continue;
906 if ( strncmp(name, "$ld$", 4) == 0 )
907 continue;
908 macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
909 *newSymbolEntry = *entry;
910 newSymbolEntry->set_n_strx(0);
911 stringPool.add(symbolIndex, name);
912 _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
913 ++symbolIndex;
914 offset += sizeof(macho_nlist<P>);
915 }
916 _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex;
917 }
918
919 template <typename P>
920 void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
921 {
922 _newImportedSymbolsStartIndex = symbolIndex;
923 const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
924 const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
925 const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
926 const macho_nlist<P>* const lastImport = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()];
927 uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym();
928 for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) {
929 if ( (entry->n_type() & N_TYPE) != N_UNDF)
930 continue;
931 const char* name = &strings[entry->n_strx()];
932 macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
933 *newSymbolEntry = *entry;
934 newSymbolEntry->set_n_strx(0);
935 stringPool.add(symbolIndex, name);
936 _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
937 ++symbolIndex;
938 offset += sizeof(macho_nlist<P>);
939 }
940 _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex;
941 }
942
943 template <typename P>
944 void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset)
945 {
946 _newIndirectSymbolTableOffset = offset;
947 const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
948 uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset];
949 for (int i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) {
950 uint32_t symbolIndex = E::get32(indirectTable[i]);
951 if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) )
952 E::set32(newIndirectTable[i], symbolIndex);
953 else
954 E::set32(newIndirectTable[i], _oldToNewSymbolIndexes[symbolIndex]);
955 offset += sizeof(uint32_t);
956 }
957 }
958
959 template <typename P>
960 uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers, Diagnostics& diagnostics, dyld_cache_local_symbols_info** localsInfo)
961 {
962 // allocate space for new linkedit data
963 uint32_t linkeditStartOffset = 0xFFFFFFFF;
964 uint32_t linkeditEndOffset = 0;
965 uint64_t linkeditStartAddr = 0;
966 for (LinkeditOptimizer<P>* op : optimizers) {
967 uint32_t leOffset = op->linkeditOffset();
968 if ( leOffset < linkeditStartOffset ) {
969 linkeditStartOffset = leOffset;
970 linkeditStartAddr = op->linkeditAddr();
971 }
972 uint32_t leEndOffset = op->linkeditOffset() + op->linkeditSize();
973 if ( leEndOffset > linkeditEndOffset )
974 linkeditEndOffset = leEndOffset;
975 }
976 uint64_t totalUnoptLinkeditsSize = linkeditEndOffset - linkeditStartOffset;
977 uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
978 SortedStringPool<P> stringPool;
979 uint32_t offset = 0;
980
981 diagnostics.verbose("Merged LINKEDIT:\n");
982
983 // copy weak binding info
984 uint32_t startWeakBindInfosOffset = offset;
985 for (LinkeditOptimizer<P>* op : optimizers) {
986 op->copyWeakBindingInfo(newLinkEdit, offset);
987 }
988 diagnostics.verbose(" weak bindings size: %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
989
990 // copy export info
991 uint32_t startExportInfosOffset = offset;
992 for (LinkeditOptimizer<P>* op : optimizers) {
993 op->copyExportInfo(newLinkEdit, offset);
994 }
995 diagnostics.verbose(" exports info size: %5uKB\n", (uint32_t)(offset-startExportInfosOffset)/1024);
996
997 // in theory, an optimized cache can drop the binding info
998 if ( true ) {
999 // copy binding info
1000 uint32_t startBindingsInfosOffset = offset;
1001 for (LinkeditOptimizer<P>* op : optimizers) {
1002 op->copyBindingInfo(newLinkEdit, offset);
1003 }
1004 diagnostics.verbose(" bindings size: %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
1005
1006 // copy lazy binding info
1007 uint32_t startLazyBindingsInfosOffset = offset;
1008 for (LinkeditOptimizer<P>* op : optimizers) {
1009 op->copyLazyBindingInfo(newLinkEdit, offset);
1010 }
1011 diagnostics.verbose(" lazy bindings size: %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
1012 }
1013
1014 // copy symbol table entries
1015 std::vector<macho_nlist<P>> unmappedLocalSymbols;
1016 if ( dontMapLocalSymbols )
1017 unmappedLocalSymbols.reserve(0x01000000);
1018 std::vector<LocalSymbolInfo> localSymbolInfos;
1019 localSymbolInfos.reserve(optimizers.size());
1020 SortedStringPool<P> localSymbolsStringPool;
1021 uint32_t symbolIndex = 0;
1022 const uint32_t sharedSymbolTableStartOffset = offset;
1023 uint32_t sharedSymbolTableExportsCount = 0;
1024 uint32_t sharedSymbolTableImportsCount = 0;
1025 for (LinkeditOptimizer<P>* op : optimizers) {
1026 op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
1027 localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
1028 uint32_t x = symbolIndex;
1029 op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
1030 sharedSymbolTableExportsCount += (symbolIndex-x);
1031 uint32_t y = symbolIndex;
1032 op->copyImportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
1033 sharedSymbolTableImportsCount += (symbolIndex-y);
1034 }
1035 uint32_t sharedSymbolTableCount = symbolIndex;
1036 const uint32_t sharedSymbolTableEndOffset = offset;
1037
1038 // copy function starts
1039 uint32_t startFunctionStartsOffset = offset;
1040 for (LinkeditOptimizer<P>* op : optimizers) {
1041 op->copyFunctionStarts(newLinkEdit, offset);
1042 }
1043 diagnostics.verbose(" function starts size: %5uKB\n", (offset-startFunctionStartsOffset)/1024);
1044
1045 // copy data-in-code info
1046 uint32_t startDataInCodeOffset = offset;
1047 for (LinkeditOptimizer<P>* op : optimizers) {
1048 op->copyDataInCode(newLinkEdit, offset);
1049 }
1050 diagnostics.verbose(" data in code size: %5uKB\n", (offset-startDataInCodeOffset)/1024);
1051
1052 // copy indirect symbol tables
1053 for (LinkeditOptimizer<P>* op : optimizers) {
1054 op->copyIndirectSymbolTable(newLinkEdit, offset);
1055 }
1056 // if indirect table has odd number of entries, end will not be 8-byte aligned
1057 if ( (offset % sizeof(typename P::uint_t)) != 0 )
1058 offset += 4;
1059
1060 // copy string pool
1061 uint32_t sharedSymbolStringsOffset = offset;
1062 uint32_t sharedSymbolStringsSize = stringPool.copyPoolAndUpdateOffsets((char*)&newLinkEdit[sharedSymbolStringsOffset], (macho_nlist<P>*)&newLinkEdit[sharedSymbolTableStartOffset]);
1063 offset += sharedSymbolStringsSize;
1064 uint32_t newLinkeditUnalignedSize = offset;
1065 uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14);
1066 diagnostics.verbose(" symbol table size: %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
1067 diagnostics.verbose(" symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
1068
1069 // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
1070 diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
1071 ::memcpy((char*)cache + linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
1072 ::bzero((char*)cache + linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
1073 ::free(newLinkEdit);
1074
1075 // If making cache for customers, add extra accelerator tables for dyld
1076 if ( addAcceleratorTables ) {
1077 AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
1078 uint32_t tablesSize = tables.totalSize();
1079 if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
1080 tables.copyTo((uint8_t*)cache+newLinkeditEnd);
1081 newLinkeditEnd += tablesSize;
1082 uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
1083 cache->header.accelerateInfoAddr = accelInfoAddr;
1084 cache->header.accelerateInfoSize = tablesSize;
1085 diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
1086 }
1087 else {
1088 diagnostics.warning("not enough room to add dyld accelerator tables");
1089 }
1090 }
1091
1092 // update mapping to reduce linkedit size
1093 dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
1094 mappings[2].size = newLinkeditEnd - mappings[2].fileOffset;
1095
1096 // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
1097 uint64_t newFileSize = newLinkeditEnd;
1098 if ( dontMapLocalSymbols ) {
1099 typedef typename P::E E;
1100 const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
1101 const uint32_t entriesCount = (uint32_t)localSymbolInfos.size();
1102 const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyld_cache_local_symbols_info), 4); // 16-byte align start
1103 const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size();
1104 const uint32_t stringsSize = (uint32_t)localSymbolsStringPool.size();
1105 const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
1106 // allocate buffer for local symbols
1107 const size_t localsBufferSize = align(stringsOffset + stringsSize, 14);
1108 dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)malloc(localsBufferSize);
1109 // fill in header info
1110 infoHeader->nlistOffset = nlistOffset;
1111 infoHeader->nlistCount = nlistCount;
1112 infoHeader->stringsOffset = stringsOffset;
1113 infoHeader->stringsSize = stringsSize;
1114 infoHeader->entriesOffset = entriesOffset;
1115 infoHeader->entriesCount = entriesCount;
1116 // copy info for each dylib
1117 dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset);
1118 for (int i=0; i < entriesCount; ++i) {
1119 entries[i].dylibOffset = localSymbolInfos[i].dylibOffset;
1120 entries[i].nlistStartIndex = localSymbolInfos[i].nlistStartIndex;
1121 entries[i].nlistCount = localSymbolInfos[i].nlistCount;
1122 }
1123 // copy nlists
1124 macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
1125 ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
1126 // copy string pool
1127 localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
1128 // return buffer of local symbols, caller to free() it
1129 *localsInfo = infoHeader;
1130 }
1131
1132 // update all load commands to new merged layout
1133 for (LinkeditOptimizer<P>* op : optimizers) {
1134 op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
1135 sharedSymbolTableStartOffset, sharedSymbolTableCount,
1136 sharedSymbolStringsOffset, sharedSymbolStringsSize);
1137 }
1138
1139 return newFileSize;
1140 }
1141
1142 } // anonymous namespace
1143
1144 template <typename P>
1145 uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
1146 {
1147 // construct a LinkeditOptimizer for each image
1148 __block std::vector<LinkeditOptimizer<P>*> optimizers;
1149 cache->forEachImage(^(const mach_header* mh, const char*) {
1150 optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, diag));
1151 });
1152 #if 0
1153 // add optimizer for each branch pool
1154 for (uint64_t poolOffset : branchPoolOffsets) {
1155 macho_header<P>* mh = (macho_header<P>*)((char*)cache + poolOffset);
1156 optimizers.push_back(new LinkeditOptimizer<P>(cache, mh, diag));
1157 }
1158 #endif
1159 // merge linkedit info
1160 uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
1161
1162 // delete optimizers
1163 for (LinkeditOptimizer<P>* op : optimizers)
1164 delete op;
1165
1166 return newFileSize;
1167 }
1168
1169 uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
1170 {
1171 if ( is64) {
1172 return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
1173 }
1174 else {
1175 return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
1176 }
1177 }
1178
1179
1180