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@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #ifndef __MACHO_REBASER__
26 #define __MACHO_REBASER__
28 #include <sys/types.h>
31 #include <mach/mach.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>
47 #include "MachOFileAbstraction.hpp"
48 #include "Architectures.hpp"
49 #include "MachOLayout.hpp"
50 #include "MachOTrie.hpp"
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;
65 class Rebaser
: public AbstractRebaser
68 Rebaser(const MachOLayoutAbstraction
&);
71 virtual cpu_type_t
getArchitecture() const;
72 virtual uint64_t getBaseAddress() const;
73 virtual uint64_t getVMSize() const;
74 virtual void rebase();
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
);
85 void calculateRelocBase();
86 void adjustLoadCommands();
87 void adjustSymbolTable();
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;
104 const macho_header
<P
>* fHeader
;
105 uint8_t* fLinkEditBase
; // add file offset to this to get linkedit content
106 const MachOLayoutAbstraction
& fLayout
;
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() ) {
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();
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()) {
150 fSymbolTable
= (macho_symtab_command
<P
>*)cmd
;
153 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
156 case LC_DYLD_INFO_ONLY
:
157 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
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
)
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();
216 // if splitting segments, update code-to-data references
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();
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() ) {
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);
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);
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()));
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
= §ionsStart
[seg
->nsects()];
287 for(macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
288 sect
->set_addr(sect
->addr() + slide
);
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
);
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 )
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
;
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);
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
);
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
);
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);
460 uint32_t instruction
;
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
);
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
);
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
);
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
);
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;
509 delta
|= ((byte
& 0x7F) << shift
);
514 doCodeUpdate(kind
, address
+orgBaseAddress
, codeToDataDelta
, codeToImportDelta
);
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()];
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;) {
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
;
585 case REBASE_TYPE_POINTER
:
586 valueP
= P::getP(*mappedAddrP
);
587 P::setP(*mappedAddrP
, valueP
+ this->getSlideForVMAddress(valueP
));
590 case REBASE_TYPE_TEXT_ABSOLUTE32
:
591 value32
= E::get32(*mappedAddr32
);
592 E::set32(*mappedAddr32
, value32
+ this->getSlideForVMAddress(value32
));
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
);
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()];
617 uint64_t segOffset
= 0;
621 while ( !done
&& (p
< end
) ) {
622 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
623 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
626 case REBASE_OPCODE_DONE
:
629 case REBASE_OPCODE_SET_TYPE_IMM
:
632 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
633 segIndex
= immediate
;
634 segOffset
= read_uleb128(p
, end
);
636 case REBASE_OPCODE_ADD_ADDR_ULEB
:
637 segOffset
+= read_uleb128(p
, end
);
639 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
640 segOffset
+= immediate
*sizeof(pint_t
);
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
);
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
);
655 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
656 doRebase(segIndex
, segOffset
, type
);
657 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
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
);
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
= §ionsStart
[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();
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
);
746 void Rebaser
<x86_64
>::adjustRelocBaseAddresses()
748 // x86_64 already have reloc base of first writable segment
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
));
761 throw "invalid relocation type";
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
));
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()) );
781 throw "cannot rebase final linked image with scattered relocations";
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
));
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()) );
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
));
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;
839 // reloc addresses are from the start of the mapped file (base address)
840 fOrignalVMRelocBaseAddress
= segments
[0].address();
841 fOrignalVMRelocBaseAddressValid
= true;
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;
863 class MultiArchRebaser
866 MultiArchRebaser::MultiArchRebaser(const char* path
, bool writable
=false)
867 : fMappingAddress(0), fFileSize(0)
870 int fd
= ::open(path
, (writable
? O_RDWR
: O_RDONLY
), 0);
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
);
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
);
894 switch ( OSSwapBigToHostInt32(archs
[i
].cputype
) ) {
895 case CPU_TYPE_POWERPC
:
896 fRebasers
.push_back(new Rebaser
<ppc
>(&p
[fileOffset
]));
899 fRebasers
.push_back(new Rebaser
<x86
>(&p
[fileOffset
]));
901 case CPU_TYPE_X86_64
:
902 fRebasers
.push_back(new Rebaser
<x86_64
>(&p
[fileOffset
]));
905 fRebasers
.push_back(new Rebaser
<arm
>(&p
[fileOffset
]));
908 throw "unknown file format";
911 catch (const char* msg
) {
912 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
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
));
931 throw "unknown file format";
934 catch (const char* msg
) {
935 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
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
); }
950 std::vector
<AbstractRebaser
*> fRebasers
;
951 void* fMappingAddress
;
957 #endif // __MACHO_REBASER__