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