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>
46 #include "MachOFileAbstraction.hpp"
47 #include "Architectures.hpp"
48 #include "MachOLayout.hpp"
55 virtual cpu_type_t
getArchitecture() const = 0;
56 virtual uint64_t getBaseAddress() const = 0;
57 virtual uint64_t getVMSize() const = 0;
58 virtual void rebase() = 0;
63 class Rebaser
: public AbstractRebaser
66 Rebaser(const MachOLayoutAbstraction
&);
69 virtual cpu_type_t
getArchitecture() const;
70 virtual uint64_t getBaseAddress() const;
71 virtual uint64_t getVMSize() const;
72 virtual void rebase();
75 typedef typename
A::P P
;
76 typedef typename
A::P::E E
;
77 typedef typename
A::P::uint_t pint_t
;
79 pint_t
* mappedAddressForNewAddress(pint_t vmaddress
);
80 pint_t
getSlideForNewAddress(pint_t newAddress
);
83 pint_t
calculateRelocBase();
84 void adjustLoadCommands();
85 void adjustSymbolTable();
88 void adjustSegmentLoadCommand(macho_segment_command
<P
>* seg
);
89 pint_t
getSlideForVMAddress(pint_t vmaddress
);
90 pint_t
* mappedAddressForVMAddress(pint_t vmaddress
);
91 pint_t
* mappedAddressForRelocAddress(pint_t r_address
);
92 void adjustRelocBaseAddresses();
93 const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p
, uint8_t kind
, uint64_t orgBaseAddress
, int64_t codeToDataDelta
, int64_t codeToImportDelta
);
94 void doCodeUpdate(uint8_t kind
, uint64_t address
, int64_t codeToDataDelta
, int64_t codeToImportDelta
);
95 void doLocalRelocation(const macho_relocation_info
<P
>* reloc
);
96 bool unequalSlides() const;
99 const macho_header
<P
>* fHeader
;
100 uint8_t* fLinkEditBase
; // add file offset to this to get linkedit content
101 const MachOLayoutAbstraction
& fLayout
;
103 pint_t fOrignalVMRelocBaseAddress
; // add reloc address to this to get original address reloc referred to
104 bool fSplittingSegments
;
109 template <typename A
>
110 Rebaser
<A
>::Rebaser(const MachOLayoutAbstraction
& layout
)
111 : fLayout(layout
), fOrignalVMRelocBaseAddress(NULL
), fLinkEditBase(NULL
), fSplittingSegments(false)
113 fHeader
= (const macho_header
<P
>*)fLayout
.getSegments()[0].mappedAddress();
114 switch ( fHeader
->filetype() ) {
119 throw "file is not a dylib or bundle";
122 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
123 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
124 const MachOLayoutAbstraction::Segment
& seg
= *it
;
125 if ( strcmp(seg
.name(), "__LINKEDIT") == 0 ) {
126 fLinkEditBase
= (uint8_t*)seg
.mappedAddress() - seg
.fileOffset();
130 if ( fLinkEditBase
== NULL
)
131 throw "no __LINKEDIT segment";
133 fOrignalVMRelocBaseAddress
= calculateRelocBase();
135 fSplittingSegments
= layout
.hasSplitSegInfo() && this->unequalSlides();
138 template <> cpu_type_t Rebaser
<ppc
>::getArchitecture() const { return CPU_TYPE_POWERPC
; }
139 template <> cpu_type_t Rebaser
<ppc64
>::getArchitecture() const { return CPU_TYPE_POWERPC64
; }
140 template <> cpu_type_t Rebaser
<x86
>::getArchitecture() const { return CPU_TYPE_I386
; }
141 template <> cpu_type_t Rebaser
<x86_64
>::getArchitecture() const { return CPU_TYPE_X86_64
; }
143 template <typename A
>
144 bool Rebaser
<A
>::unequalSlides() const
146 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
147 uint64_t slide
= segments
[0].newAddress() - segments
[0].address();
148 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
149 const MachOLayoutAbstraction::Segment
& seg
= *it
;
150 if ( (seg
.newAddress() - seg
.address()) != slide
)
156 template <typename A
>
157 uint64_t Rebaser
<A
>::getBaseAddress() const
159 return fLayout
.getSegments()[0].address();
162 template <typename A
>
163 uint64_t Rebaser
<A
>::getVMSize() const
165 uint64_t highestVMAddress
= 0;
166 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
167 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
168 const MachOLayoutAbstraction::Segment
& seg
= *it
;
169 if ( seg
.address() > highestVMAddress
)
170 highestVMAddress
= seg
.address();
172 return (((highestVMAddress
- getBaseAddress()) + 4095) & (-4096));
177 template <typename A
>
178 void Rebaser
<A
>::rebase()
180 // update writable segments that have internal pointers
183 // if splitting segments, update code-to-data references
186 // change address on relocs now that segments are split
187 this->adjustRelocBaseAddresses();
189 // update load commands
190 this->adjustLoadCommands();
192 // update symbol table
193 this->adjustSymbolTable();
197 void Rebaser
<x86
>::adjustSegmentLoadCommand(macho_segment_command
<P
>* seg
)
199 // __IMPORT segments are not-writable in shared cache
200 if ( strcmp(seg
->segname(), "__IMPORT") == 0 )
201 seg
->set_initprot(VM_PROT_READ
|VM_PROT_EXECUTE
);
204 template <typename A
>
205 void Rebaser
<A
>::adjustSegmentLoadCommand(macho_segment_command
<P
>* seg
)
210 template <typename A
>
211 void Rebaser
<A
>::adjustLoadCommands()
213 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
214 const uint32_t cmd_count
= fHeader
->ncmds();
215 const macho_load_command
<P
>* cmd
= cmds
;
216 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
217 switch ( cmd
->cmd() ) {
219 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
220 // clear timestamp so that any prebound clients are invalidated
221 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
222 dylib
->set_timestamp(1);
226 case LC_LOAD_WEAK_DYLIB
:
227 case LC_REEXPORT_DYLIB
:
228 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
229 // clear expected timestamps so that this image will load with invalid prebinding
230 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
231 dylib
->set_timestamp(2);
234 case macho_routines_command
<P
>::CMD
:
235 // update -init command
237 struct macho_routines_command
<P
>* routines
= (struct macho_routines_command
<P
>*)cmd
;
238 routines
->set_init_address(routines
->init_address() + this->getSlideForVMAddress(routines
->init_address()));
241 case macho_segment_command
<P
>::CMD
:
242 // update segment commands
244 macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
245 this->adjustSegmentLoadCommand(seg
);
246 pint_t slide
= this->getSlideForVMAddress(seg
->vmaddr());
247 seg
->set_vmaddr(seg
->vmaddr() + slide
);
248 macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
249 macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
250 for(macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
251 sect
->set_addr(sect
->addr() + slide
);
256 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
262 template <typename A
>
263 typename
A::P::uint_t Rebaser
<A
>::getSlideForVMAddress(pint_t vmaddress
)
265 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
266 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
267 const MachOLayoutAbstraction::Segment
& seg
= *it
;
268 if ( (seg
.address() <= vmaddress
) && (seg
.size() != 0) && ((vmaddress
< (seg
.address()+seg
.size())) || (seg
.address() == vmaddress
)) ) {
269 return seg
.newAddress() - seg
.address();
272 throwf("vm address 0x%08llX not found", (uint64_t)vmaddress
);
276 template <typename A
>
277 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForVMAddress(pint_t vmaddress
)
279 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
280 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
281 const MachOLayoutAbstraction::Segment
& seg
= *it
;
282 if ( (seg
.address() <= vmaddress
) && (vmaddress
< (seg
.address()+seg
.size())) ) {
283 return (pint_t
*)((vmaddress
- seg
.address()) + (uint8_t*)seg
.mappedAddress());
286 throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddress
);
289 template <typename A
>
290 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForNewAddress(pint_t vmaddress
)
292 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
293 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
294 const MachOLayoutAbstraction::Segment
& seg
= *it
;
295 if ( (seg
.newAddress() <= vmaddress
) && (vmaddress
< (seg
.newAddress()+seg
.size())) ) {
296 return (pint_t
*)((vmaddress
- seg
.newAddress()) + (uint8_t*)seg
.mappedAddress());
299 throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress
);
302 template <typename A
>
303 typename
A::P::uint_t Rebaser
<A
>::getSlideForNewAddress(pint_t newAddress
)
305 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
306 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
307 const MachOLayoutAbstraction::Segment
& seg
= *it
;
308 if ( (seg
.newAddress() <= newAddress
) && (newAddress
< (seg
.newAddress()+seg
.size())) ) {
309 return seg
.newAddress() - seg
.address();
312 throwf("new address 0x%08llX not found", (uint64_t)newAddress
);
315 template <typename A
>
316 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForRelocAddress(pint_t r_address
)
318 return this->mappedAddressForVMAddress(r_address
+ fOrignalVMRelocBaseAddress
);
322 template <typename A
>
323 void Rebaser
<A
>::adjustSymbolTable()
325 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
326 macho_nlist
<P
>* symbolTable
= NULL
;
328 // get symbol table info
329 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
330 const uint32_t cmd_count
= fHeader
->ncmds();
331 const macho_load_command
<P
>* cmd
= cmds
;
332 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
333 switch (cmd
->cmd()) {
336 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
337 symbolTable
= (macho_nlist
<P
>*)(&fLinkEditBase
[symtab
->symoff()]);
341 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
344 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
347 // walk all exports and slide their n_value
348 macho_nlist
<P
>* lastExport
= &symbolTable
[dysymtab
->iextdefsym()+dysymtab
->nextdefsym()];
349 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->iextdefsym()]; entry
< lastExport
; ++entry
) {
350 if ( (entry
->n_type() & N_TYPE
) == N_SECT
)
351 entry
->set_n_value(entry
->n_value() + this->getSlideForVMAddress(entry
->n_value()));
354 // walk all local symbols and slide their n_value (don't adjust and stabs)
355 macho_nlist
<P
>* lastLocal
= &symbolTable
[dysymtab
->ilocalsym()+dysymtab
->nlocalsym()];
356 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->ilocalsym()]; entry
< lastLocal
; ++entry
) {
357 if ( (entry
->n_sect() != NO_SECT
) && ((entry
->n_type() & N_STAB
) == 0) )
358 entry
->set_n_value(entry
->n_value() + this->getSlideForVMAddress(entry
->n_value()));
365 template <typename A
>
366 void Rebaser
<A
>::doCodeUpdate(uint8_t kind
, uint64_t address
, int64_t codeToDataDelta
, int64_t codeToImportDelta
)
368 //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta);
370 uint32_t instruction
;
374 case 1: // 32-bit pointer
375 p
= (uint32_t*)mappedAddressForVMAddress(address
);
376 value
= A::P::E::get32(*p
);
377 value
+= codeToDataDelta
;
378 A::P::E::set32(*p
, value
);
380 case 2: // 64-bit pointer
381 p
= (uint32_t*)mappedAddressForVMAddress(address
);
382 value64
= A::P::E::get64(*(uint64_t*)p
);
383 value64
+= codeToDataDelta
;
384 A::P::E::set64(*(uint64_t*)p
, value64
);
386 case 3: // used only for ppc/ppc64, an instruction that sets the hi16 of a register
387 // adjust low 16 bits of instruction which contain hi16 of distance to something in DATA
388 if ( (codeToDataDelta
& 0xFFFF) != 0 )
389 throwf("codeToDataDelta=0x%0llX is not a multiple of 64K", codeToDataDelta
);
390 p
= (uint32_t*)mappedAddressForVMAddress(address
);
391 instruction
= BigEndian::get32(*p
);
393 uint16_t originalLo16
= instruction
& 0x0000FFFF;
394 uint16_t delta64Ks
= codeToDataDelta
>> 16;
395 instruction
= (instruction
& 0xFFFF0000) | ((originalLo16
+delta64Ks
) & 0x0000FFFF);
397 BigEndian::set32(*p
, instruction
);
399 case 4: // only used for i386, a reference to something in the IMPORT segment
400 p
= (uint32_t*)mappedAddressForVMAddress(address
);
401 value
= A::P::E::get32(*p
);
402 value
+= codeToImportDelta
;
403 A::P::E::set32(*p
, value
);
406 throwf("invalid kind=%d in split seg info", kind
);
410 template <typename A
>
411 const uint8_t* Rebaser
<A
>::doCodeUpdateForEachULEB128Address(const uint8_t* p
, uint8_t kind
, uint64_t orgBaseAddress
, int64_t codeToDataDelta
, int64_t codeToImportDelta
)
413 uint64_t address
= 0;
419 delta
|= ((byte
& 0x7F) << shift
);
424 doCodeUpdate(kind
, address
+orgBaseAddress
, codeToDataDelta
, codeToImportDelta
);
436 template <typename A
>
437 void Rebaser
<A
>::adjustCode()
439 if ( fSplittingSegments
) {
440 // get uleb128 compressed runs of code addresses to update
441 const uint8_t* infoStart
= NULL
;
442 const uint8_t* infoEnd
= NULL
;
443 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
444 const uint32_t cmd_count
= fHeader
->ncmds();
445 const macho_load_command
<P
>* cmd
= cmds
;
446 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
447 switch (cmd
->cmd()) {
448 case LC_SEGMENT_SPLIT_INFO
:
450 const macho_linkedit_data_command
<P
>* segInfo
= (macho_linkedit_data_command
<P
>*)cmd
;
451 infoStart
= &fLinkEditBase
[segInfo
->dataoff()];
452 infoEnd
= &infoStart
[segInfo
->datasize()];
456 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
458 // calculate how much we need to slide writable segments
459 const uint64_t orgBaseAddress
= this->getBaseAddress();
460 int64_t codeToDataDelta
= 0;
461 int64_t codeToImportDelta
= 0;
462 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
463 const MachOLayoutAbstraction::Segment
& codeSeg
= segments
[0];
464 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
465 const MachOLayoutAbstraction::Segment
& dataSeg
= *it
;
466 if ( strcmp(dataSeg
.name(), "__IMPORT") == 0 )
467 codeToImportDelta
= (dataSeg
.newAddress() - codeSeg
.newAddress()) - (dataSeg
.address() - codeSeg
.address());
468 else if ( dataSeg
.writable() )
469 codeToDataDelta
= (dataSeg
.newAddress() - codeSeg
.newAddress()) - (dataSeg
.address() - codeSeg
.address());
471 // decompress and call doCodeUpdate() on each address
472 for(const uint8_t* p
= infoStart
; *p
!= 0;) {
474 p
= this->doCodeUpdateForEachULEB128Address(p
, kind
, orgBaseAddress
, codeToDataDelta
, codeToImportDelta
);
480 template <typename A
>
481 void Rebaser
<A
>::adjustDATA()
483 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
485 // get symbol table info
486 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
487 const uint32_t cmd_count
= fHeader
->ncmds();
488 const macho_load_command
<P
>* cmd
= cmds
;
489 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
490 switch (cmd
->cmd()) {
492 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
495 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
499 // walk all local relocations and slide every pointer
500 const macho_relocation_info
<P
>* const relocsStart
= (macho_relocation_info
<P
>*)(&fLinkEditBase
[dysymtab
->locreloff()]);
501 const macho_relocation_info
<P
>* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel()];
502 for (const macho_relocation_info
<P
>* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
503 this->doLocalRelocation(reloc
);
506 // walk non-lazy-pointers and slide the ones that are LOCAL
508 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
509 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
510 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
511 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
512 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
513 const uint32_t* const indirectTable
= (uint32_t*)(&fLinkEditBase
[dysymtab
->indirectsymoff()]);
514 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
515 if ( (sect
->flags() & SECTION_TYPE
) == S_NON_LAZY_SYMBOL_POINTERS
) {
516 const uint32_t indirectTableOffset
= sect
->reserved1();
517 uint32_t pointerCount
= sect
->size() / sizeof(pint_t
);
518 pint_t
* nonLazyPointerAddr
= this->mappedAddressForVMAddress(sect
->addr());
519 for (uint32_t j
=0; j
< pointerCount
; ++j
, ++nonLazyPointerAddr
) {
520 if ( E::get32(indirectTable
[indirectTableOffset
+ j
]) == INDIRECT_SYMBOL_LOCAL
) {
521 pint_t value
= A::P::getP(*nonLazyPointerAddr
);
522 P::setP(*nonLazyPointerAddr
, value
+ this->getSlideForVMAddress(value
));
528 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
533 template <typename A
>
534 void Rebaser
<A
>::adjustRelocBaseAddresses()
536 // split seg file alreday have reloc base of first writable segment
537 // only non-split-segs that are being split need this adjusted
538 if ( (fHeader
->flags() & MH_SPLIT_SEGS
) == 0 ) {
540 // get symbol table to find relocation records
541 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
542 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
543 const uint32_t cmd_count
= fHeader
->ncmds();
544 const macho_load_command
<P
>* cmd
= cmds
;
545 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
546 switch (cmd
->cmd()) {
548 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
551 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
554 // get amount to adjust reloc address
555 int32_t relocAddressAdjust
= 0;
556 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
557 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
558 const MachOLayoutAbstraction::Segment
& seg
= *it
;
559 if ( seg
.writable() ) {
560 relocAddressAdjust
= seg
.address() - segments
[0].address();
565 // walk all local relocations and adjust every address
566 macho_relocation_info
<P
>* const relocsStart
= (macho_relocation_info
<P
>*)(&fLinkEditBase
[dysymtab
->locreloff()]);
567 macho_relocation_info
<P
>* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel()];
568 for (macho_relocation_info
<P
>* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
569 reloc
->set_r_address(reloc
->r_address()-relocAddressAdjust
);
572 // walk all external relocations and adjust every address
573 macho_relocation_info
<P
>* const externRelocsStart
= (macho_relocation_info
<P
>*)(&fLinkEditBase
[dysymtab
->extreloff()]);
574 macho_relocation_info
<P
>* const externRelocsEnd
= &externRelocsStart
[dysymtab
->nextrel()];
575 for (macho_relocation_info
<P
>* reloc
=externRelocsStart
; reloc
< externRelocsEnd
; ++reloc
) {
576 reloc
->set_r_address(reloc
->r_address()-relocAddressAdjust
);
582 void Rebaser
<x86_64
>::adjustRelocBaseAddresses()
584 // x86_64 already have reloc base of first writable segment
589 void Rebaser
<x86_64
>::doLocalRelocation(const macho_relocation_info
<x86_64::P
>* reloc
)
591 if ( reloc
->r_type() == X86_64_RELOC_UNSIGNED
) {
592 pint_t
* addr
= this->mappedAddressForRelocAddress(reloc
->r_address());
593 pint_t value
= P::getP(*addr
);
594 P::setP(*addr
, value
+ this->getSlideForVMAddress(value
));
597 throw "invalid relocation type";
602 void Rebaser
<ppc
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
604 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
605 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
606 pint_t
* addr
= this->mappedAddressForRelocAddress(reloc
->r_address());
607 pint_t value
= P::getP(*addr
);
608 P::setP(*addr
, value
+ this->getSlideForVMAddress(value
));
612 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
613 if ( sreloc
->r_type() == PPC_RELOC_PB_LA_PTR
) {
614 sreloc
->set_r_value( sreloc
->r_value() + this->getSlideForVMAddress(sreloc
->r_value()) );
617 throw "cannot rebase final linked image with scattered relocations";
623 void Rebaser
<x86
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
625 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
626 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
627 pint_t
* addr
= this->mappedAddressForRelocAddress(reloc
->r_address());
628 pint_t value
= P::getP(*addr
);
629 P::setP(*addr
, value
+ this->getSlideForVMAddress(value
));
633 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
634 if ( sreloc
->r_type() == GENERIC_RELOC_PB_LA_PTR
) {
635 sreloc
->set_r_value( sreloc
->r_value() + this->getSlideForVMAddress(sreloc
->r_value()) );
638 throw "cannot rebase final linked image with scattered relocations";
643 template <typename A
>
644 void Rebaser
<A
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
646 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
647 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
648 pint_t
* addr
= this->mappedAddressForRelocAddress(reloc
->r_address());
649 pint_t value
= P::getP(*addr
);
650 P::setP(*addr
, value
+ this->getSlideForVMAddress(value
));
654 throw "cannot rebase final linked image with scattered relocations";
659 template <typename A
>
660 typename
A::P::uint_t Rebaser
<A
>::calculateRelocBase()
662 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
663 if ( fHeader
->flags() & MH_SPLIT_SEGS
) {
664 // reloc addresses are from the start of the first writable segment
665 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
666 const MachOLayoutAbstraction::Segment
& seg
= *it
;
667 if ( seg
.writable() ) {
668 // found first writable segment
669 return seg
.address();
672 throw "no writable segment";
675 // reloc addresses are from the start of the mapped file (base address)
676 return segments
[0].address();
681 ppc64::P::uint_t Rebaser
<ppc64
>::calculateRelocBase()
683 // reloc addresses either:
684 // 1) from the first segment vmaddr if no writable segment is > 4GB from first segment vmaddr
685 // 2) from start of first writable segment
686 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
687 uint64_t threshold
= segments
[0].address() + 0x100000000ULL
;
688 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
689 const MachOLayoutAbstraction::Segment
& seg
= *it
;
690 if ( seg
.writable() && (seg
.address()+seg
.size()) > threshold
) {
691 // found writable segment with address > 4GB past base address
692 return seg
.address();
695 // just use base address
696 return segments
[0].address();
700 x86_64::P::uint_t Rebaser
<x86_64
>::calculateRelocBase()
702 // reloc addresses are always based from the start of the first writable segment
703 const std::vector
<MachOLayoutAbstraction::Segment
>& segments
= fLayout
.getSegments();
704 for(std::vector
<MachOLayoutAbstraction::Segment
>::const_iterator it
= segments
.begin(); it
!= segments
.end(); ++it
) {
705 const MachOLayoutAbstraction::Segment
& seg
= *it
;
706 if ( seg
.writable() ) {
707 // found first writable segment
708 return seg
.address();
711 throw "no writable segment";
716 class MultiArchRebaser
719 MultiArchRebaser::MultiArchRebaser(const char* path
, bool writable
=false)
720 : fMappingAddress(0), fFileSize(0)
723 int fd
= ::open(path
, (writable
? O_RDWR
: O_RDONLY
), 0);
725 throwf("can't open file, errno=%d", errno
);
726 struct stat stat_buf
;
727 if ( fstat(fd
, &stat_buf
) == -1)
728 throwf("can't stat open file %s, errno=%d", path
, errno
);
729 if ( stat_buf
.st_size
< 20 )
730 throwf("file too small %s", path
);
731 const int prot
= writable
? (PROT_READ
| PROT_WRITE
) : PROT_READ
;
732 const int flags
= writable
? (MAP_FILE
| MAP_SHARED
) : (MAP_FILE
| MAP_PRIVATE
);
733 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, prot
, flags
, fd
, 0);
734 if ( p
== (uint8_t*)(-1) )
735 throwf("can't map file %s, errno=%d", path
, errno
);
738 // if fat file, process each architecture
739 const fat_header
* fh
= (fat_header
*)p
;
740 const mach_header
* mh
= (mach_header
*)p
;
741 if ( fh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
742 // Fat header is always big-endian
743 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
744 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
745 uint32_t fileOffset
= OSSwapBigToHostInt32(archs
[i
].offset
);
747 switch ( OSSwapBigToHostInt32(archs
[i
].cputype
) ) {
748 case CPU_TYPE_POWERPC
:
749 fRebasers
.push_back(new Rebaser
<ppc
>(&p
[fileOffset
]));
751 case CPU_TYPE_POWERPC64
:
752 fRebasers
.push_back(new Rebaser
<ppc64
>(&p
[fileOffset
]));
755 fRebasers
.push_back(new Rebaser
<x86
>(&p
[fileOffset
]));
757 case CPU_TYPE_X86_64
:
758 fRebasers
.push_back(new Rebaser
<x86_64
>(&p
[fileOffset
]));
761 throw "unknown file format";
764 catch (const char* msg
) {
765 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
771 if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC
)) {
772 fRebasers
.push_back(new Rebaser
<ppc
>(mh
));
774 else if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC64
)) {
775 fRebasers
.push_back(new Rebaser
<ppc64
>(mh
));
777 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_I386
)) {
778 fRebasers
.push_back(new Rebaser
<x86
>(mh
));
780 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_X86_64
)) {
781 fRebasers
.push_back(new Rebaser
<x86_64
>(mh
));
784 throw "unknown file format";
787 catch (const char* msg
) {
788 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
793 fFileSize
= stat_buf
.st_size
;
797 ~MultiArchRebaser() {::munmap(fMappingAddress
, fFileSize
); }
799 const std::vector
<AbstractRebaser
*>& getArchs() const { return fRebasers
; }
800 void commit() { ::msync(fMappingAddress
, fFileSize
, MS_ASYNC
); }
803 std::vector
<AbstractRebaser
*> fRebasers
;
804 void* fMappingAddress
;
810 #endif // __MACHO_REBASER__