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 #include <sys/types.h>
28 #include <mach/mach.h>
39 #include "MachOFileAbstraction.hpp"
40 #include "Architectures.hpp"
42 static bool verbose
= false;
44 __attribute__((noreturn
))
45 void throwf(const char* format
, ...)
49 va_start(list
, format
);
50 vasprintf(&p
, format
, list
);
61 virtual cpu_type_t
getArchitecture() const = 0;
62 virtual uint64_t getBaseAddress() const = 0;
63 virtual uint64_t getVMSize() const = 0;
64 virtual void setBaseAddress(uint64_t) = 0;
69 class Rebaser
: public AbstractRebaser
72 Rebaser(const void* machHeader
);
75 virtual cpu_type_t
getArchitecture() const;
76 virtual uint64_t getBaseAddress() const;
77 virtual uint64_t getVMSize() const;
78 virtual void setBaseAddress(uint64_t);
81 typedef typename
A::P P
;
82 typedef typename
A::P::E E
;
83 typedef typename
A::P::uint_t pint_t
;
85 struct vmmap
{ pint_t vmaddr
; pint_t vmsize
; pint_t fileoff
; };
88 void buildSectionTable();
89 void adjustLoadCommands();
90 void adjustSymbolTable();
92 void doLocalRelocation(const macho_relocation_info
<P
>* reloc
);
93 pint_t
* mappedAddressForVMAddress(uint32_t vmaddress
);
95 const macho_header
<P
>* fHeader
;
96 pint_t fOrignalVMRelocBaseAddress
;
99 std::vector
<vmmap
> fVMMApping
;
104 class MultiArchRebaser
107 MultiArchRebaser(const char* path
, bool writable
=false);
110 const std::vector
<AbstractRebaser
*>& getArchs() const { return fRebasers
; }
114 std::vector
<AbstractRebaser
*> fRebasers
;
115 void* fMappingAddress
;
121 MultiArchRebaser::MultiArchRebaser(const char* path
, bool writable
)
122 : fMappingAddress(0), fFileSize(0)
125 int fd
= ::open(path
, (writable
? O_RDWR
: O_RDONLY
), 0);
127 throwf("can't open file, errno=%d", errno
);
128 struct stat stat_buf
;
129 if ( fstat(fd
, &stat_buf
) == -1)
130 throwf("can't stat open file %s, errno=%d", path
, errno
);
131 if ( stat_buf
.st_size
< 20 )
132 throwf("file too small %s", path
);
133 const int prot
= writable
? (PROT_READ
| PROT_WRITE
) : PROT_READ
;
134 const int flags
= writable
? (MAP_FILE
| MAP_SHARED
) : (MAP_FILE
| MAP_PRIVATE
);
135 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, prot
, flags
, fd
, 0);
136 if ( p
== (uint8_t*)(-1) )
137 throwf("can't map file %s, errno=%d", path
, errno
);
140 // if fat file, process each architecture
141 const fat_header
* fh
= (fat_header
*)p
;
142 const mach_header
* mh
= (mach_header
*)p
;
143 if ( fh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
144 // Fat header is always big-endian
145 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
146 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
147 uint32_t fileOffset
= OSSwapBigToHostInt32(archs
[i
].offset
);
149 switch ( OSSwapBigToHostInt32(archs
[i
].cputype
) ) {
150 case CPU_TYPE_POWERPC
:
151 fRebasers
.push_back(new Rebaser
<ppc
>(&p
[fileOffset
]));
153 case CPU_TYPE_POWERPC64
:
154 fRebasers
.push_back(new Rebaser
<ppc64
>(&p
[fileOffset
]));
157 fRebasers
.push_back(new Rebaser
<x86
>(&p
[fileOffset
]));
159 case CPU_TYPE_X86_64
:
160 fRebasers
.push_back(new Rebaser
<x86_64
>(&p
[fileOffset
]));
163 fRebasers
.push_back(new Rebaser
<arm
>(&p
[fileOffset
]));
166 throw "unknown file format";
169 catch (const char* msg
) {
170 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
176 if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC
)) {
177 fRebasers
.push_back(new Rebaser
<ppc
>(mh
));
179 else if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC64
)) {
180 fRebasers
.push_back(new Rebaser
<ppc64
>(mh
));
182 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_I386
)) {
183 fRebasers
.push_back(new Rebaser
<x86
>(mh
));
185 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_X86_64
)) {
186 fRebasers
.push_back(new Rebaser
<x86_64
>(mh
));
188 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_ARM
)) {
189 fRebasers
.push_back(new Rebaser
<arm
>(mh
));
192 throw "unknown file format";
195 catch (const char* msg
) {
196 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
201 fFileSize
= stat_buf
.st_size
;
205 MultiArchRebaser::~MultiArchRebaser()
207 ::munmap(fMappingAddress
, fFileSize
);
210 void MultiArchRebaser::commit()
212 ::msync(fMappingAddress
, fFileSize
, MS_ASYNC
);
217 template <typename A
>
218 Rebaser
<A
>::Rebaser(const void* machHeader
)
219 : fHeader((const macho_header
<P
>*)machHeader
)
221 switch ( fHeader
->filetype() ) {
223 if ( (fHeader
->flags() & MH_SPLIT_SEGS
) != 0 )
224 throw "split-seg dylibs cannot be rebased";
229 throw "file is not a dylib or bundle";
234 template <> cpu_type_t Rebaser
<ppc
>::getArchitecture() const { return CPU_TYPE_POWERPC
; }
235 template <> cpu_type_t Rebaser
<ppc64
>::getArchitecture() const { return CPU_TYPE_POWERPC64
; }
236 template <> cpu_type_t Rebaser
<x86
>::getArchitecture() const { return CPU_TYPE_I386
; }
237 template <> cpu_type_t Rebaser
<x86_64
>::getArchitecture() const { return CPU_TYPE_X86_64
; }
238 template <> cpu_type_t Rebaser
<arm
>::getArchitecture() const { return CPU_TYPE_ARM
; }
240 template <typename A
>
241 uint64_t Rebaser
<A
>::getBaseAddress() const
243 uint64_t lowestSegmentAddress
= LLONG_MAX
;
244 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
245 const uint32_t cmd_count
= fHeader
->ncmds();
246 const macho_load_command
<P
>* cmd
= cmds
;
247 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
248 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
249 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
250 if ( segCmd
->vmaddr() < lowestSegmentAddress
) {
251 lowestSegmentAddress
= segCmd
->vmaddr();
254 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
256 return lowestSegmentAddress
;
259 template <typename A
>
260 uint64_t Rebaser
<A
>::getVMSize() const
262 const macho_segment_command
<P
>* highestSegmentCmd
= NULL
;
263 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
264 const uint32_t cmd_count
= fHeader
->ncmds();
265 const macho_load_command
<P
>* cmd
= cmds
;
266 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
267 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
268 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
269 if ( (highestSegmentCmd
== NULL
) || (segCmd
->vmaddr() > highestSegmentCmd
->vmaddr()) ) {
270 highestSegmentCmd
= segCmd
;
273 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
276 return ((highestSegmentCmd
->vmaddr() + highestSegmentCmd
->vmsize() - this->getBaseAddress() + 4095) & (-4096));
280 template <typename A
>
281 void Rebaser
<A
>::setBaseAddress(uint64_t addr
)
284 fSlide
= addr
- this->getBaseAddress();
286 // compute base address for relocations
287 this->setRelocBase();
289 // build cache of section index to section
290 this->buildSectionTable();
292 // update load commands
293 this->adjustLoadCommands();
295 // update symbol table
296 this->adjustSymbolTable();
298 // update writable segments that have internal pointers
302 template <typename A
>
303 void Rebaser
<A
>::adjustLoadCommands()
305 const macho_segment_command
<P
>* highestSegmentCmd
= NULL
;
306 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
307 const uint32_t cmd_count
= fHeader
->ncmds();
308 const macho_load_command
<P
>* cmd
= cmds
;
309 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
310 switch ( cmd
->cmd() ) {
312 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
313 // clear timestamp so that any prebound clients are invalidated
314 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
315 dylib
->set_timestamp(1);
319 case LC_LOAD_WEAK_DYLIB
:
320 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
321 // clear expected timestamps so that this image will load with invalid prebinding
322 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
323 dylib
->set_timestamp(2);
326 case macho_routines_command
<P
>::CMD
:
327 // update -init command
329 struct macho_routines_command
<P
>* routines
= (struct macho_routines_command
<P
>*)cmd
;
330 routines
->set_init_address(routines
->init_address() + fSlide
);
333 case macho_segment_command
<P
>::CMD
:
334 // update segment commands
336 macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
337 seg
->set_vmaddr(seg
->vmaddr() + fSlide
);
338 macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
339 macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
340 for(macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
341 sect
->set_addr(sect
->addr() + fSlide
);
346 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
351 template <typename A
>
352 void Rebaser
<A
>::buildSectionTable()
354 // build vector of sections
355 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
356 const uint32_t cmd_count
= fHeader
->ncmds();
357 const macho_load_command
<P
>* cmd
= cmds
;
358 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
359 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
360 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
362 mapping
.vmaddr
= seg
->vmaddr();
363 mapping
.vmsize
= seg
->vmsize();
364 mapping
.fileoff
= seg
->fileoff();
365 fVMMApping
.push_back(mapping
);
367 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
372 template <typename A
>
373 void Rebaser
<A
>::adjustSymbolTable()
375 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
376 macho_nlist
<P
>* symbolTable
= NULL
;
378 // get symbol table info
379 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
380 const uint32_t cmd_count
= fHeader
->ncmds();
381 const macho_load_command
<P
>* cmd
= cmds
;
382 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
383 switch (cmd
->cmd()) {
386 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
387 symbolTable
= (macho_nlist
<P
>*)(((uint8_t*)fHeader
) + symtab
->symoff());
391 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
394 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
397 // walk all exports and slide their n_value
398 macho_nlist
<P
>* lastExport
= &symbolTable
[dysymtab
->iextdefsym()+dysymtab
->nextdefsym()];
399 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->iextdefsym()]; entry
< lastExport
; ++entry
) {
400 if ( (entry
->n_type() & N_TYPE
) == N_SECT
)
401 entry
->set_n_value(entry
->n_value() + fSlide
);
404 // walk all local symbols and slide their n_value
405 macho_nlist
<P
>* lastLocal
= &symbolTable
[dysymtab
->ilocalsym()+dysymtab
->nlocalsym()];
406 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->ilocalsym()]; entry
< lastLocal
; ++entry
) {
407 if ( entry
->n_sect() != NO_SECT
)
408 entry
->set_n_value(entry
->n_value() + fSlide
);
411 // FIXME ¥¥¥ adjust dylib_module if it exists
414 template <typename A
>
415 void Rebaser
<A
>::adjustDATA()
417 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
419 // get symbol table info
420 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
421 const uint32_t cmd_count
= fHeader
->ncmds();
422 const macho_load_command
<P
>* cmd
= cmds
;
423 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
424 switch (cmd
->cmd()) {
426 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
429 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
432 // walk all local relocations and slide every pointer
433 const macho_relocation_info
<P
>* const relocsStart
= (macho_relocation_info
<P
>*)(((uint8_t*)fHeader
) + dysymtab
->locreloff());
434 const macho_relocation_info
<P
>* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel()];
435 for (const macho_relocation_info
<P
>* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
436 this->doLocalRelocation(reloc
);
439 // walk non-lazy-pointers and slide the ones that are LOCAL
441 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
442 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
443 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
444 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
445 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
446 const uint32_t* const indirectTable
= (uint32_t*)(((uint8_t*)fHeader
) + dysymtab
->indirectsymoff());
447 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
448 if ( (sect
->flags() & SECTION_TYPE
) == S_NON_LAZY_SYMBOL_POINTERS
) {
449 const uint32_t indirectTableOffset
= sect
->reserved1();
450 uint32_t pointerCount
= sect
->size() / sizeof(pint_t
);
451 pint_t
* nonLazyPointer
= (pint_t
*)(((uint8_t*)fHeader
) + sect
->offset());
452 for (uint32_t i
=0; i
< pointerCount
; ++i
, ++nonLazyPointer
) {
453 if ( E::get32(indirectTable
[indirectTableOffset
+ i
]) == INDIRECT_SYMBOL_LOCAL
) {
454 P::setP(*nonLazyPointer
, A::P::getP(*nonLazyPointer
) + fSlide
);
460 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
466 template <typename A
>
467 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForVMAddress(uint32_t vmaddress
)
469 for(typename
std::vector
<vmmap
>::iterator it
= fVMMApping
.begin(); it
!= fVMMApping
.end(); ++it
) {
470 //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize);
471 if ( (vmaddress
>= it
->vmaddr
) && (vmaddress
< (it
->vmaddr
+it
->vmsize
)) ) {
472 return (pint_t
*)((vmaddress
- it
->vmaddr
) + it
->fileoff
+ (uint8_t*)fHeader
);
475 throwf("reloc address 0x%08X not found", vmaddress
);
480 void Rebaser
<x86_64
>::doLocalRelocation(const macho_relocation_info
<x86_64::P
>* reloc
)
482 if ( reloc
->r_type() == X86_64_RELOC_UNSIGNED
) {
483 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
484 P::setP(*addr
, P::getP(*addr
) + fSlide
);
487 throw "invalid relocation type";
492 void Rebaser
<ppc
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
494 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
495 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
496 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
497 P::setP(*addr
, P::getP(*addr
) + fSlide
);
501 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
502 if ( sreloc
->r_type() == PPC_RELOC_PB_LA_PTR
) {
503 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
506 throw "cannot rebase final linked image with scattered relocations";
512 void Rebaser
<x86
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
514 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
515 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
516 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
517 P::setP(*addr
, P::getP(*addr
) + fSlide
);
521 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
522 if ( sreloc
->r_type() == GENERIC_RELOC_PB_LA_PTR
) {
523 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
526 throw "cannot rebase final linked image with scattered relocations";
532 void Rebaser
<arm
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
534 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
535 if ( reloc
->r_type() == ARM_RELOC_VANILLA
) {
536 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
537 P::setP(*addr
, P::getP(*addr
) + fSlide
);
541 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
542 if ( sreloc
->r_type() == ARM_RELOC_PB_LA_PTR
) {
543 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
546 throw "cannot rebase final linked image with scattered relocations";
551 template <typename A
>
552 void Rebaser
<A
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
554 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
555 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
556 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
557 P::setP(*addr
, P::getP(*addr
) + fSlide
);
561 throw "cannot rebase final linked image with scattered relocations";
566 template <typename A
>
567 void Rebaser
<A
>::setRelocBase()
569 // reloc addresses are from the start of the mapped file (base address)
570 fRelocBase
= (pint_t
)fHeader
;
571 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
572 //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress);
576 void Rebaser
<ppc64
>::setRelocBase()
578 // reloc addresses either:
579 // 1) from the base address if no writable segment is > 4GB from base address
580 // 2) from start of first writable segment
581 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
582 const uint32_t cmd_count
= fHeader
->ncmds();
583 const macho_load_command
<P
>* cmd
= cmds
;
584 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
585 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
586 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
587 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
588 if ( (segCmd
->vmaddr() + segCmd
->vmsize() - this->getBaseAddress()) > 0x100000000ULL
) {
589 // found writable segment with address > 4GB past base address
590 fRelocBase
= segCmd
->fileoff() + (pint_t
)fHeader
;
591 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
596 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
598 // just use base address
599 fRelocBase
= (pint_t
)fHeader
;
600 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
604 void Rebaser
<x86_64
>::setRelocBase()
606 // reloc addresses are always based from the start of the first writable segment
607 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
608 const uint32_t cmd_count
= fHeader
->ncmds();
609 const macho_load_command
<P
>* cmd
= cmds
;
610 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
611 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
612 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
613 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
614 fRelocBase
= segCmd
->fileoff() + (pint_t
)fHeader
;
615 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
619 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
621 throw "no writable segment";
625 static void copyFile(const char* srcFile
, const char* dstFile
)
628 int src
= open(srcFile
, O_RDONLY
);
630 throwf("can't open file %s, errno=%d", srcFile
, errno
);
631 struct stat stat_buf
;
632 if ( fstat(src
, &stat_buf
) == -1)
633 throwf("can't stat open file %s, errno=%d", srcFile
, errno
);
635 // create new file with all same permissions to hold copy of dylib
637 int dst
= open(dstFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
639 throwf("can't create temp file %s, errnor=%d", dstFile
, errno
);
641 // mark source as "don't cache"
642 (void)fcntl(src
, F_NOCACHE
, 1);
643 // we want to cache the dst because we are about to map it in and modify it
645 // copy permission bits
646 if ( chmod(dstFile
, stat_buf
.st_mode
& 07777) == -1 )
647 throwf("can't chmod temp file %s, errno=%d", dstFile
, errno
);
648 if ( chown(dstFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
649 throwf("can't chown temp file %s, errno=%d", dstFile
, errno
);
653 const uint32_t kBufferSize
= 128*1024;
654 static uint8_t* buffer
= NULL
;
655 if ( buffer
== NULL
) {
656 vm_address_t addr
= 0;
657 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
658 buffer
= (uint8_t*)addr
;
660 throw "can't allcoate copy buffer";
662 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
663 if ( write(dst
, buffer
, len
) == -1 )
664 throwf("write failure copying feil %s, errno=%d", dstFile
, errno
);
668 int result1
= close(dst
);
669 int result2
= close(src
);
670 if ( (result1
!= 0) || (result2
!= 0) )
671 throw "can't close file";
675 // scan dylibs and collect size info
676 // calculate new base address for each dylib
678 // copy to temp and mmap
692 fileInfo(const char* p
) : path(p
) {}
695 std::vector
<archInfo
> archs
;
699 // add archInfos to fileInfo for every slice of a fat file
700 // for ppc, there may be duplicate architectures (with different sub-types)
702 static void setSizes(fileInfo
& info
, const std::set
<cpu_type_t
>& onlyArchs
)
704 const MultiArchRebaser
mar(info
.path
);
705 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
706 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
707 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
708 AbstractRebaser
* rebaser
= *rit
;
709 if ( rebaser
->getArchitecture() == *ait
) {
712 ai
.vmSize
= rebaser
->getVMSize();
713 ai
.orgBase
= rebaser
->getBaseAddress();
715 //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize);
716 info
.archs
.push_back(ai
);
722 static const char* nameForArch(cpu_type_t arch
)
725 case CPU_TYPE_POWERPC
:
727 case CPU_TYPE_POWERPC64
:
731 case CPU_TYPE_X86_64
:
739 static void rebase(const fileInfo
& info
)
741 // generate temp file name
742 char realFilePath
[PATH_MAX
];
743 if ( realpath(info
.path
, realFilePath
) == NULL
) {
744 throwf("realpath() failed on %s, errno=%d", info
.path
, errno
);
746 const char* tempPath
;
747 asprintf((char**)&tempPath
, "%s_rebase", realFilePath
);
749 // copy whole file to temp file
750 copyFile(info
.path
, tempPath
);
754 MultiArchRebaser
mar(tempPath
, true);
755 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
756 for(std::vector
<archInfo
>::const_iterator fait
=info
.archs
.begin(); fait
!= info
.archs
.end(); ++fait
) {
757 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
758 if ( (*rit
)->getArchitecture() == fait
->arch
) {
759 (*rit
)->setBaseAddress(fait
->newBase
);
761 printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait
->arch
), fait
->orgBase
, fait
->newBase
, info
.path
);
766 // flush temp file out to disk
770 int result
= rename(tempPath
, info
.path
);
772 throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath
, info
.path
, errno
);
775 // make sure every really gets out to disk
778 catch (const char* msg
) {
782 // throw exception with file name added
784 asprintf((char**)&newMsg
, "%s for file %s", msg
, info
.path
);
789 static uint64_t totalVMSize(cpu_type_t arch
, std::vector
<fileInfo
>& files
)
791 uint64_t totalSize
= 0;
792 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
794 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
795 if ( fait
->arch
== arch
)
796 totalSize
+= fait
->vmSize
;
802 static uint64_t startAddress(cpu_type_t arch
, std::vector
<fileInfo
>& files
, uint64_t lowAddress
, uint64_t highAddress
)
804 if ( lowAddress
!= 0 )
806 else if ( highAddress
!= 0 ) {
807 uint64_t totalSize
= totalVMSize(arch
, files
);
808 if ( highAddress
< totalSize
)
809 throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress
, totalSize
);
810 return highAddress
- totalSize
;
813 if ( (arch
== CPU_TYPE_I386
) || (arch
== CPU_TYPE_POWERPC
) ) {
814 // place dylibs below dyld
815 uint64_t topAddr
= 0x8FE00000;
816 uint64_t totalSize
= totalVMSize(arch
, files
);
817 if ( totalSize
> topAddr
)
818 throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize
);
819 return topAddr
- totalSize
;
821 else if ( arch
== CPU_TYPE_POWERPC64
) {
822 return 0x200000000ULL
;
824 else if ( arch
== CPU_TYPE_X86_64
) {
825 return 0x200000000ULL
;
827 else if ( arch
== CPU_TYPE_ARM
) {
828 // place dylibs below dyld
829 uint64_t topAddr
= 0x2FE00000;
830 uint64_t totalSize
= totalVMSize(arch
, files
);
831 if ( totalSize
> topAddr
)
832 throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize
);
833 return topAddr
- totalSize
;
836 throw "unknown architecture";
842 fprintf(stderr
, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n");
846 int main(int argc
, const char* argv
[])
848 std::vector
<fileInfo
> files
;
849 std::set
<cpu_type_t
> onlyArchs
;
850 uint64_t lowAddress
= 0;
851 uint64_t highAddress
= 0;
854 // parse command line options
856 for(int i
=1; i
< argc
; ++i
) {
857 const char* arg
= argv
[i
];
858 if ( arg
[0] == '-' ) {
859 if ( strcmp(arg
, "-v") == 0 ) {
862 else if ( strcmp(arg
, "-low_address") == 0 ) {
863 lowAddress
= strtoull(argv
[++i
], &endptr
, 16);
865 else if ( strcmp(arg
, "-high_address") == 0 ) {
866 highAddress
= strtoull(argv
[++i
], &endptr
, 16);
868 else if ( strcmp(arg
, "-arch") == 0 ) {
869 const char* arch
= argv
[++i
];
870 if ( strcmp(arch
, "ppc") == 0 )
871 onlyArchs
.insert(CPU_TYPE_POWERPC
);
872 else if ( strcmp(arch
, "ppc64") == 0 )
873 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
874 else if ( strcmp(arch
, "i386") == 0 )
875 onlyArchs
.insert(CPU_TYPE_I386
);
876 else if ( strcmp(arch
, "x86_64") == 0 )
877 onlyArchs
.insert(CPU_TYPE_X86_64
);
878 else if ( strcmp(arch
, "arm") == 0 )
879 onlyArchs
.insert(CPU_TYPE_ARM
);
880 else if ( strcmp(arch
, "armv6") == 0 )
881 onlyArchs
.insert(CPU_TYPE_ARM
);
883 throwf("unknown architecture %s", arch
);
887 throwf("unknown option: %s\n", arg
);
891 files
.push_back(fileInfo(arg
));
895 if ( files
.size() == 0 )
896 throw "no files specified";
898 // use all architectures if no restrictions specified
899 if ( onlyArchs
.size() == 0 ) {
900 onlyArchs
.insert(CPU_TYPE_POWERPC
);
901 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
902 onlyArchs
.insert(CPU_TYPE_I386
);
903 onlyArchs
.insert(CPU_TYPE_X86_64
);
904 onlyArchs
.insert(CPU_TYPE_ARM
);
907 // scan files and collect sizes
908 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
909 setSizes(*it
, onlyArchs
);
912 // assign new base address for each arch
913 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
914 cpu_type_t arch
= *ait
;
915 uint64_t baseAddress
= startAddress(arch
, files
, lowAddress
, highAddress
);
916 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
918 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
919 if ( fait
->arch
== arch
) {
920 fait
->newBase
= baseAddress
;
921 baseAddress
+= fait
->vmSize
;
922 baseAddress
= (baseAddress
+ 4095) & (-4096); // page align
928 // rebase each file if it contains something rebaseable
929 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
931 if ( fi
.archs
.size() > 0 )
936 catch (const char* msg
) {
937 fprintf(stderr
, "rebase failed: %s\n", msg
);