132-13
[darwin-xtools.git] / dyld / launch-cache / MachORebaser.hpp
blob9dcfc6babd0d4113e7aab01902a879b009cee23a
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
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.
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #ifndef __MACHO_REBASER__
26 #define __MACHO_REBASER__
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <mach/mach.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <mach-o/loader.h>
39 #include <mach-o/fat.h>
40 #include <mach-o/reloc.h>
41 #include <mach-o/ppc/reloc.h>
42 #include <mach-o/x86_64/reloc.h>
43 #include <mach-o/arm/reloc.h>
44 #include <vector>
45 #include <set>
47 #include "MachOFileAbstraction.hpp"
48 #include "Architectures.hpp"
49 #include "MachOLayout.hpp"
50 #include "MachOTrie.hpp"
54 class AbstractRebaser
56 public:
57 virtual cpu_type_t getArchitecture() const = 0;
58 virtual uint64_t getBaseAddress() const = 0;
59 virtual uint64_t getVMSize() const = 0;
60 virtual void rebase() = 0;
64 template <typename A>
65 class Rebaser : public AbstractRebaser
67 public:
68 Rebaser(const MachOLayoutAbstraction&);
69 virtual ~Rebaser() {}
71 virtual cpu_type_t getArchitecture() const;
72 virtual uint64_t getBaseAddress() const;
73 virtual uint64_t getVMSize() const;
74 virtual void rebase();
76 protected:
77 typedef typename A::P P;
78 typedef typename A::P::E E;
79 typedef typename A::P::uint_t pint_t;
81 pint_t* mappedAddressForNewAddress(pint_t vmaddress);
82 pint_t getSlideForNewAddress(pint_t newAddress);
84 private:
85 void calculateRelocBase();
86 void adjustLoadCommands();
87 void adjustSymbolTable();
88 void adjustDATA();
89 void adjustCode();
90 void applyRebaseInfo();
91 void adjustExportInfo();
92 void doRebase(int segIndex, uint64_t segOffset, uint8_t type);
93 void adjustSegmentLoadCommand(macho_segment_command<P>* seg);
94 pint_t getSlideForVMAddress(pint_t vmaddress);
95 pint_t* mappedAddressForVMAddress(pint_t vmaddress);
96 pint_t* mappedAddressForRelocAddress(pint_t r_address);
97 void adjustRelocBaseAddresses();
98 const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta);
99 void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta);
100 void doLocalRelocation(const macho_relocation_info<P>* reloc);
101 bool unequalSlides() const;
103 protected:
104 const macho_header<P>* fHeader;
105 uint8_t* fLinkEditBase; // add file offset to this to get linkedit content
106 const MachOLayoutAbstraction& fLayout;
107 private:
108 pint_t fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to
109 const macho_symtab_command<P>* fSymbolTable;
110 const macho_dysymtab_command<P>* fDynamicSymbolTable;
111 const macho_dyld_info_command<P>* fDyldInfo;
112 bool fSplittingSegments;
113 bool fOrignalVMRelocBaseAddressValid;
118 template <typename A>
119 Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
120 : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL),
121 fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), fOrignalVMRelocBaseAddressValid(false)
123 fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
124 switch ( fHeader->filetype() ) {
125 case MH_DYLIB:
126 case MH_BUNDLE:
127 break;
128 default:
129 throw "file is not a dylib or bundle";
132 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
133 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
134 const MachOLayoutAbstraction::Segment& seg = *it;
135 if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) {
136 fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset();
137 break;
140 if ( fLinkEditBase == NULL )
141 throw "no __LINKEDIT segment";
143 // get symbol table info
144 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
145 const uint32_t cmd_count = fHeader->ncmds();
146 const macho_load_command<P>* cmd = cmds;
147 for (uint32_t i = 0; i < cmd_count; ++i) {
148 switch (cmd->cmd()) {
149 case LC_SYMTAB:
150 fSymbolTable = (macho_symtab_command<P>*)cmd;
151 break;
152 case LC_DYSYMTAB:
153 fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd;
154 break;
155 case LC_DYLD_INFO:
156 case LC_DYLD_INFO_ONLY:
157 fDyldInfo = (macho_dyld_info_command<P>*)cmd;
158 break;
160 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
163 calculateRelocBase();
165 fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides();
168 template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
169 template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
170 template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
171 template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
173 template <typename A>
174 bool Rebaser<A>::unequalSlides() const
176 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
177 uint64_t slide = segments[0].newAddress() - segments[0].address();
178 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
179 const MachOLayoutAbstraction::Segment& seg = *it;
180 if ( (seg.newAddress() - seg.address()) != slide )
181 return true;
183 return false;
186 template <typename A>
187 uint64_t Rebaser<A>::getBaseAddress() const
189 return fLayout.getSegments()[0].address();
192 template <typename A>
193 uint64_t Rebaser<A>::getVMSize() const
195 uint64_t highestVMAddress = 0;
196 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
197 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
198 const MachOLayoutAbstraction::Segment& seg = *it;
199 if ( seg.address() > highestVMAddress )
200 highestVMAddress = seg.address();
202 return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096));
207 template <typename A>
208 void Rebaser<A>::rebase()
210 // update writable segments that have internal pointers
211 if ( fDyldInfo != NULL )
212 this->applyRebaseInfo();
213 else
214 this->adjustDATA();
216 // if splitting segments, update code-to-data references
217 this->adjustCode();
219 // change address on relocs now that segments are split
220 this->adjustRelocBaseAddresses();
222 // update load commands
223 this->adjustLoadCommands();
225 // update symbol table
226 this->adjustSymbolTable();
228 // update export info
229 if ( fDyldInfo != NULL )
230 this->adjustExportInfo();
233 template <>
234 void Rebaser<x86>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
236 // __IMPORT segments are not-writable in shared cache
237 if ( strcmp(seg->segname(), "__IMPORT") == 0 )
238 seg->set_initprot(VM_PROT_READ|VM_PROT_EXECUTE);
241 template <typename A>
242 void Rebaser<A>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
247 template <typename A>
248 void Rebaser<A>::adjustLoadCommands()
250 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
251 const uint32_t cmd_count = fHeader->ncmds();
252 const macho_load_command<P>* cmd = cmds;
253 for (uint32_t i = 0; i < cmd_count; ++i) {
254 switch ( cmd->cmd() ) {
255 case LC_ID_DYLIB:
256 if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
257 // clear timestamp so that any prebound clients are invalidated
258 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
259 dylib->set_timestamp(1);
261 break;
262 case LC_LOAD_DYLIB:
263 case LC_LOAD_WEAK_DYLIB:
264 case LC_REEXPORT_DYLIB:
265 if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
266 // clear expected timestamps so that this image will load with invalid prebinding
267 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
268 dylib->set_timestamp(2);
270 break;
271 case macho_routines_command<P>::CMD:
272 // update -init command
274 struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
275 routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address()));
277 break;
278 case macho_segment_command<P>::CMD:
279 // update segment commands
281 macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
282 this->adjustSegmentLoadCommand(seg);
283 pint_t slide = this->getSlideForVMAddress(seg->vmaddr());
284 seg->set_vmaddr(seg->vmaddr() + slide);
285 macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
286 macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
287 for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
288 sect->set_addr(sect->addr() + slide);
291 break;
293 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
299 template <typename A>
300 typename A::P::uint_t Rebaser<A>::getSlideForVMAddress(pint_t vmaddress)
302 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
303 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
304 const MachOLayoutAbstraction::Segment& seg = *it;
305 if ( (seg.address() <= vmaddress) && (seg.size() != 0) && ((vmaddress < (seg.address()+seg.size())) || (seg.address() == vmaddress)) ) {
306 return seg.newAddress() - seg.address();
309 throwf("vm address 0x%08llX not found", (uint64_t)vmaddress);
313 template <typename A>
314 typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(pint_t vmaddress)
316 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
317 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
318 const MachOLayoutAbstraction::Segment& seg = *it;
319 if ( (seg.address() <= vmaddress) && (vmaddress < (seg.address()+seg.size())) ) {
320 return (pint_t*)((vmaddress - seg.address()) + (uint8_t*)seg.mappedAddress());
323 throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddress);
326 template <typename A>
327 typename A::P::uint_t* Rebaser<A>::mappedAddressForNewAddress(pint_t vmaddress)
329 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
330 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
331 const MachOLayoutAbstraction::Segment& seg = *it;
332 if ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) {
333 return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress());
336 throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress);
339 template <typename A>
340 typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress)
342 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
343 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
344 const MachOLayoutAbstraction::Segment& seg = *it;
345 if ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) {
346 return seg.newAddress() - seg.address();
349 throwf("new address 0x%08llX not found", (uint64_t)newAddress);
352 template <typename A>
353 typename A::P::uint_t* Rebaser<A>::mappedAddressForRelocAddress(pint_t r_address)
355 if ( fOrignalVMRelocBaseAddressValid )
356 return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress);
357 else
358 throw "can't apply relocation. Relocation base not known";
362 template <typename A>
363 void Rebaser<A>::adjustSymbolTable()
365 macho_nlist<P>* symbolTable = (macho_nlist<P>*)(&fLinkEditBase[fSymbolTable->symoff()]);
367 // walk all exports and slide their n_value
368 macho_nlist<P>* lastExport = &symbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
369 for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->iextdefsym()]; entry < lastExport; ++entry) {
370 if ( (entry->n_type() & N_TYPE) == N_SECT )
371 entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
374 // walk all local symbols and slide their n_value (don't adjust any stabs)
375 macho_nlist<P>* lastLocal = &symbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
376 for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->ilocalsym()]; entry < lastLocal; ++entry) {
377 if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
378 entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
382 template <typename A>
383 void Rebaser<A>::adjustExportInfo()
385 // if no export info, nothing to adjust
386 if ( fDyldInfo->export_size() == 0 )
387 return;
389 // since export info addresses are offsets from mach_header, everything in __TEXT is fine
390 // only __DATA addresses need to be updated
391 const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off()];
392 const uint8_t* end = &start[fDyldInfo->export_size()];
393 std::vector<mach_o::trie::Entry> originalExports;
394 try {
395 parseTrie(start, end, originalExports);
397 catch (const char* msg) {
398 throwf("%s in %s", msg, fLayout.getFilePath());
401 std::vector<mach_o::trie::Entry> newExports;
402 newExports.reserve(originalExports.size());
403 pint_t baseAddress = this->getBaseAddress();
404 pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress);
405 for (std::vector<mach_o::trie::Entry>::iterator it=originalExports.begin(); it != originalExports.end(); ++it) {
406 // remove symbols used by the static linker only
407 if ( (strncmp(it->name, "$ld$", 4) == 0)
408 || (strncmp(it->name, ".objc_class_name",16) == 0)
409 || (strncmp(it->name, ".objc_category_name",19) == 0) ) {
410 //fprintf(stderr, "ignoring symbol %s\n", it->name);
411 continue;
413 // adjust symbols in slid segments
414 //uint32_t oldOffset = it->address;
415 it->address += (this->getSlideForVMAddress(it->address + baseAddress) - baseAddressSlide);
416 //fprintf(stderr, "orig=0x%08X, new=0x%08llX, sym=%s\n", oldOffset, it->address, it->name);
417 newExports.push_back(*it);
420 // rebuild export trie
421 std::vector<uint8_t> newExportTrieBytes;
422 newExportTrieBytes.reserve(fDyldInfo->export_size());
423 mach_o::trie::makeTrie(newExports, newExportTrieBytes);
424 // align
425 while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 )
426 newExportTrieBytes.push_back(0);
428 // copy into place, zero pad
429 uint32_t newExportsSize = newExportTrieBytes.size();
430 if ( newExportsSize > fDyldInfo->export_size() ) {
431 // it is possible that the new export trie is larger than the old one
432 // for those cases will malloc a block on the side and set up
433 // export_off to point to it.
434 uint8_t* sideTrie = new uint8_t[newExportsSize];
435 memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
436 //fprintf(stderr, "set_export_off()=%ld, fLinkEditBase=%p, sideTrie=%p\n", (long)(sideTrie - fLinkEditBase), fLinkEditBase, sideTrie);
437 // warning, export_off is only 32-bits so if the trie grows it must be allocated with 32-bits of fLinkeditBase
438 int64_t offset = sideTrie - fLinkEditBase;
439 int32_t offset32 = (int32_t)offset;
440 if ( offset != offset32 )
441 throw "internal error, new trie allocated to far from fLinkeditBase";
442 ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(offset32);
443 ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
445 else {
446 uint8_t* trie = (uint8_t*)&fLinkEditBase[fDyldInfo->export_off()];
447 memcpy(trie, &newExportTrieBytes[0], newExportsSize);
448 bzero(trie+newExportsSize, fDyldInfo->export_size() - newExportsSize);
449 ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
455 template <typename A>
456 void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta)
458 //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta);
459 uint32_t* p;
460 uint32_t instruction;
461 uint32_t value;
462 uint64_t value64;
463 switch (kind) {
464 case 1: // 32-bit pointer
465 p = (uint32_t*)mappedAddressForVMAddress(address);
466 value = A::P::E::get32(*p);
467 value += codeToDataDelta;
468 A::P::E::set32(*p, value);
469 break;
470 case 2: // 64-bit pointer
471 p = (uint32_t*)mappedAddressForVMAddress(address);
472 value64 = A::P::E::get64(*(uint64_t*)p);
473 value64 += codeToDataDelta;
474 A::P::E::set64(*(uint64_t*)p, value64);
475 break;
476 case 3: // used only for ppc, an instruction that sets the hi16 of a register
477 // adjust low 16 bits of instruction which contain hi16 of distance to something in DATA
478 if ( (codeToDataDelta & 0xFFFF) != 0 )
479 throwf("codeToDataDelta=0x%0llX is not a multiple of 64K", codeToDataDelta);
480 p = (uint32_t*)mappedAddressForVMAddress(address);
481 instruction = BigEndian::get32(*p);
483 uint16_t originalLo16 = instruction & 0x0000FFFF;
484 uint16_t delta64Ks = codeToDataDelta >> 16;
485 instruction = (instruction & 0xFFFF0000) | ((originalLo16+delta64Ks) & 0x0000FFFF);
487 BigEndian::set32(*p, instruction);
488 break;
489 case 4: // only used for i386, a reference to something in the IMPORT segment
490 p = (uint32_t*)mappedAddressForVMAddress(address);
491 value = A::P::E::get32(*p);
492 value += codeToImportDelta;
493 A::P::E::set32(*p, value);
494 break;
495 default:
496 throwf("invalid kind=%d in split seg info", kind);
500 template <typename A>
501 const uint8_t* Rebaser<A>::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta)
503 uint64_t address = 0;
504 uint64_t delta = 0;
505 uint32_t shift = 0;
506 bool more = true;
507 do {
508 uint8_t byte = *p++;
509 delta |= ((byte & 0x7F) << shift);
510 shift += 7;
511 if ( byte < 0x80 ) {
512 if ( delta != 0 ) {
513 address += delta;
514 doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta);
515 delta = 0;
516 shift = 0;
518 else {
519 more = false;
522 } while (more);
523 return p;
526 template <typename A>
527 void Rebaser<A>::adjustCode()
529 if ( fSplittingSegments ) {
530 // get uleb128 compressed runs of code addresses to update
531 const uint8_t* infoStart = NULL;
532 const uint8_t* infoEnd = NULL;
533 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
534 const uint32_t cmd_count = fHeader->ncmds();
535 const macho_load_command<P>* cmd = cmds;
536 for (uint32_t i = 0; i < cmd_count; ++i) {
537 switch (cmd->cmd()) {
538 case LC_SEGMENT_SPLIT_INFO:
540 const macho_linkedit_data_command<P>* segInfo = (macho_linkedit_data_command<P>*)cmd;
541 infoStart = &fLinkEditBase[segInfo->dataoff()];
542 infoEnd = &infoStart[segInfo->datasize()];
544 break;
546 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
548 // calculate how much we need to slide writable segments
549 const uint64_t orgBaseAddress = this->getBaseAddress();
550 int64_t codeToDataDelta = 0;
551 int64_t codeToImportDelta = 0;
552 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
553 const MachOLayoutAbstraction::Segment& codeSeg = segments[0];
554 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
555 const MachOLayoutAbstraction::Segment& dataSeg = *it;
556 if ( strcmp(dataSeg.name(), "__IMPORT") == 0 )
557 codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
558 else if ( dataSeg.writable() )
559 codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
561 // decompress and call doCodeUpdate() on each address
562 for(const uint8_t* p = infoStart; *p != 0;) {
563 uint8_t kind = *p++;
564 p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta);
569 template <typename A>
570 void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type)
572 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
573 if ( segIndex > segments.size() )
574 throw "bad segment index in rebase info";
575 const MachOLayoutAbstraction::Segment& seg = segments[segIndex];
576 uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segOffset;
577 pint_t* mappedAddrP = (pint_t*)mappedAddr;
578 uint32_t* mappedAddr32 = (uint32_t*)mappedAddr;
579 pint_t valueP;
580 pint_t valuePnew;
581 uint32_t value32;
582 int32_t svalue32;
583 int32_t svalue32new;
584 switch ( type ) {
585 case REBASE_TYPE_POINTER:
586 valueP= P::getP(*mappedAddrP);
587 P::setP(*mappedAddrP, valueP + this->getSlideForVMAddress(valueP));
588 break;
590 case REBASE_TYPE_TEXT_ABSOLUTE32:
591 value32 = E::get32(*mappedAddr32);
592 E::set32(*mappedAddr32, value32 + this->getSlideForVMAddress(value32));
593 break;
595 case REBASE_TYPE_TEXT_PCREL32:
596 svalue32 = E::get32(*mappedAddr32);
597 valueP = seg.address() + segOffset + 4 + svalue32;
598 valuePnew = valueP + this->getSlideForVMAddress(valueP);
599 svalue32new = seg.address() + segOffset + 4 - valuePnew;
600 E::set32(*mappedAddr32, svalue32new);
601 break;
603 default:
604 throw "bad rebase type";
609 template <typename A>
610 void Rebaser<A>::applyRebaseInfo()
612 const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()];
613 const uint8_t* end = &p[fDyldInfo->rebase_size()];
615 uint8_t type = 0;
616 int segIndex;
617 uint64_t segOffset = 0;
618 uint32_t count;
619 uint32_t skip;
620 bool done = false;
621 while ( !done && (p < end) ) {
622 uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
623 uint8_t opcode = *p & REBASE_OPCODE_MASK;
624 ++p;
625 switch (opcode) {
626 case REBASE_OPCODE_DONE:
627 done = true;
628 break;
629 case REBASE_OPCODE_SET_TYPE_IMM:
630 type = immediate;
631 break;
632 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
633 segIndex = immediate;
634 segOffset = read_uleb128(p, end);
635 break;
636 case REBASE_OPCODE_ADD_ADDR_ULEB:
637 segOffset += read_uleb128(p, end);
638 break;
639 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
640 segOffset += immediate*sizeof(pint_t);
641 break;
642 case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
643 for (int i=0; i < immediate; ++i) {
644 doRebase(segIndex, segOffset, type);
645 segOffset += sizeof(pint_t);
647 break;
648 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
649 count = read_uleb128(p, end);
650 for (uint32_t i=0; i < count; ++i) {
651 doRebase(segIndex, segOffset, type);
652 segOffset += sizeof(pint_t);
654 break;
655 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
656 doRebase(segIndex, segOffset, type);
657 segOffset += read_uleb128(p, end) + sizeof(pint_t);
658 break;
659 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
660 count = read_uleb128(p, end);
661 skip = read_uleb128(p, end);
662 for (uint32_t i=0; i < count; ++i) {
663 doRebase(segIndex, segOffset, type);
664 segOffset += skip + sizeof(pint_t);
666 break;
667 default:
668 throwf("bad rebase opcode %d", *p);
673 template <typename A>
674 void Rebaser<A>::adjustDATA()
676 // walk all local relocations and slide every pointer
677 const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]);
678 const macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()];
679 for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
680 this->doLocalRelocation(reloc);
683 // walk non-lazy-pointers and slide the ones that are LOCAL
684 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
685 const uint32_t cmd_count = fHeader->ncmds();
686 const macho_load_command<P>* cmd = cmds;
687 for (uint32_t i = 0; i < cmd_count; ++i) {
688 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
689 const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
690 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
691 const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
692 const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[fDynamicSymbolTable->indirectsymoff()]);
693 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
694 if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
695 const uint32_t indirectTableOffset = sect->reserved1();
696 uint32_t pointerCount = sect->size() / sizeof(pint_t);
697 pint_t* nonLazyPointerAddr = this->mappedAddressForVMAddress(sect->addr());
698 for (uint32_t j=0; j < pointerCount; ++j, ++nonLazyPointerAddr) {
699 if ( E::get32(indirectTable[indirectTableOffset + j]) == INDIRECT_SYMBOL_LOCAL ) {
700 pint_t value = A::P::getP(*nonLazyPointerAddr);
701 P::setP(*nonLazyPointerAddr, value + this->getSlideForVMAddress(value));
707 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
712 template <typename A>
713 void Rebaser<A>::adjustRelocBaseAddresses()
715 // split seg file need reloc base to be first writable segment
716 if ( fSplittingSegments && ((fHeader->flags() & MH_SPLIT_SEGS) == 0) ) {
718 // get amount to adjust reloc address
719 int32_t relocAddressAdjust = 0;
720 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
721 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
722 const MachOLayoutAbstraction::Segment& seg = *it;
723 if ( seg.writable() ) {
724 relocAddressAdjust = seg.address() - segments[0].address();
725 break;
729 // walk all local relocations and adjust every address
730 macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]);
731 macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()];
732 for (macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
733 reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
736 // walk all external relocations and adjust every address
737 macho_relocation_info<P>* const externRelocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->extreloff()]);
738 macho_relocation_info<P>* const externRelocsEnd = &externRelocsStart[fDynamicSymbolTable->nextrel()];
739 for (macho_relocation_info<P>* reloc=externRelocsStart; reloc < externRelocsEnd; ++reloc) {
740 reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
745 template <>
746 void Rebaser<x86_64>::adjustRelocBaseAddresses()
748 // x86_64 already have reloc base of first writable segment
752 template <>
753 void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc)
755 if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) {
756 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
757 pint_t value = P::getP(*addr);
758 P::setP(*addr, value + this->getSlideForVMAddress(value));
760 else {
761 throw "invalid relocation type";
765 template <>
766 void Rebaser<ppc>::doLocalRelocation(const macho_relocation_info<P>* reloc)
768 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
769 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
770 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
771 pint_t value = P::getP(*addr);
772 P::setP(*addr, value + this->getSlideForVMAddress(value));
775 else {
776 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
777 if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) {
778 sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) );
780 else {
781 throw "cannot rebase final linked image with scattered relocations";
786 template <>
787 void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc)
789 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
790 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
791 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
792 pint_t value = P::getP(*addr);
793 P::setP(*addr, value + this->getSlideForVMAddress(value));
796 else {
797 macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
798 if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) {
799 sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) );
801 else {
802 throw "cannot rebase final linked image with scattered relocations";
807 template <typename A>
808 void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc)
810 if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
811 if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
812 pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
813 pint_t value = P::getP(*addr);
814 P::setP(*addr, value + this->getSlideForVMAddress(value));
817 else {
818 throw "cannot rebase final linked image with scattered relocations";
823 template <typename A>
824 void Rebaser<A>::calculateRelocBase()
826 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
827 if ( fHeader->flags() & MH_SPLIT_SEGS ) {
828 // reloc addresses are from the start of the first writable segment
829 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
830 const MachOLayoutAbstraction::Segment& seg = *it;
831 if ( seg.writable() ) {
832 // found first writable segment
833 fOrignalVMRelocBaseAddress = seg.address();
834 fOrignalVMRelocBaseAddressValid = true;
838 else {
839 // reloc addresses are from the start of the mapped file (base address)
840 fOrignalVMRelocBaseAddress = segments[0].address();
841 fOrignalVMRelocBaseAddressValid = true;
846 template <>
847 void Rebaser<x86_64>::calculateRelocBase()
849 // reloc addresses are always based from the start of the first writable segment
850 const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
851 for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
852 const MachOLayoutAbstraction::Segment& seg = *it;
853 if ( seg.writable() ) {
854 // found first writable segment
855 fOrignalVMRelocBaseAddress = seg.address();
856 fOrignalVMRelocBaseAddressValid = true;
862 #if 0
863 class MultiArchRebaser
865 public:
866 MultiArchRebaser::MultiArchRebaser(const char* path, bool writable=false)
867 : fMappingAddress(0), fFileSize(0)
869 // map in whole file
870 int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0);
871 if ( fd == -1 )
872 throwf("can't open file, errno=%d", errno);
873 struct stat stat_buf;
874 if ( fstat(fd, &stat_buf) == -1)
875 throwf("can't stat open file %s, errno=%d", path, errno);
876 if ( stat_buf.st_size < 20 )
877 throwf("file too small %s", path);
878 const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
879 const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE);
880 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0);
881 if ( p == (uint8_t*)(-1) )
882 throwf("can't map file %s, errno=%d", path, errno);
883 ::close(fd);
885 // if fat file, process each architecture
886 const fat_header* fh = (fat_header*)p;
887 const mach_header* mh = (mach_header*)p;
888 if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
889 // Fat header is always big-endian
890 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
891 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
892 uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset);
893 try {
894 switch ( OSSwapBigToHostInt32(archs[i].cputype) ) {
895 case CPU_TYPE_POWERPC:
896 fRebasers.push_back(new Rebaser<ppc>(&p[fileOffset]));
897 break;
898 case CPU_TYPE_I386:
899 fRebasers.push_back(new Rebaser<x86>(&p[fileOffset]));
900 break;
901 case CPU_TYPE_X86_64:
902 fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset]));
903 break;
904 case CPU_TYPE_ARM:
905 fRebasers.push_back(new Rebaser<arm>(&p[fileOffset]));
906 break;
907 default:
908 throw "unknown file format";
911 catch (const char* msg) {
912 fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
916 else {
917 try {
918 if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
919 fRebasers.push_back(new Rebaser<ppc>(mh));
921 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
922 fRebasers.push_back(new Rebaser<x86>(mh));
924 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
925 fRebasers.push_back(new Rebaser<x86_64>(mh));
927 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
928 fRebasers.push_back(new Rebaser<arm>(mh));
930 else {
931 throw "unknown file format";
934 catch (const char* msg) {
935 fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
939 fMappingAddress = p;
940 fFileSize = stat_buf.st_size;
944 ~MultiArchRebaser() {::munmap(fMappingAddress, fFileSize); }
946 const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; }
947 void commit() { ::msync(fMappingAddress, fFileSize, MS_ASYNC); }
949 private:
950 std::vector<AbstractRebaser*> fRebasers;
951 void* fMappingAddress;
952 uint64_t fFileSize;
954 #endif
957 #endif // __MACHO_REBASER__