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>
35 #include <mach-o/loader.h>
36 #include <mach-o/fat.h>
37 #include <mach-o/reloc.h>
38 #include <mach-o/ppc/reloc.h>
39 #include <mach-o/x86_64/reloc.h>
43 #include "MachOFileAbstraction.hpp"
44 #include "Architectures.hpp"
47 static bool verbose
= false;
49 __attribute__((noreturn
))
50 void throwf(const char* format
, ...)
54 va_start(list
, format
);
55 vasprintf(&p
, format
, list
);
66 virtual cpu_type_t
getArchitecture() const = 0;
67 virtual uint64_t getBaseAddress() const = 0;
68 virtual uint64_t getVMSize() const = 0;
69 virtual void setBaseAddress(uint64_t) = 0;
74 class Rebaser
: public AbstractRebaser
77 Rebaser(const void* machHeader
);
80 virtual cpu_type_t
getArchitecture() const;
81 virtual uint64_t getBaseAddress() const;
82 virtual uint64_t getVMSize() const;
83 virtual void setBaseAddress(uint64_t);
86 typedef typename
A::P P
;
87 typedef typename
A::P::E E
;
88 typedef typename
A::P::uint_t pint_t
;
90 struct vmmap
{ pint_t vmaddr
; pint_t vmsize
; pint_t fileoff
; };
93 void buildSectionTable();
94 void adjustLoadCommands();
95 void adjustSymbolTable();
97 void doLocalRelocation(const macho_relocation_info
<P
>* reloc
);
98 pint_t
* mappedAddressForVMAddress(uint32_t vmaddress
);
100 const macho_header
<P
>* fHeader
;
101 pint_t fOrignalVMRelocBaseAddress
;
104 std::vector
<vmmap
> fVMMApping
;
109 class MultiArchRebaser
112 MultiArchRebaser(const char* path
, bool writable
=false);
115 const std::vector
<AbstractRebaser
*>& getArchs() const { return fRebasers
; }
119 std::vector
<AbstractRebaser
*> fRebasers
;
120 void* fMappingAddress
;
126 MultiArchRebaser::MultiArchRebaser(const char* path
, bool writable
)
127 : fMappingAddress(0), fFileSize(0)
130 int fd
= ::open(path
, (writable
? O_RDWR
: O_RDONLY
), 0);
132 throwf("can't open file, errno=%d", errno
);
133 struct stat stat_buf
;
134 if ( fstat(fd
, &stat_buf
) == -1)
135 throwf("can't stat open file %s, errno=%d", path
, errno
);
136 if ( stat_buf
.st_size
< 20 )
137 throwf("file too small %s", path
);
138 const int prot
= writable
? (PROT_READ
| PROT_WRITE
) : PROT_READ
;
139 const int flags
= writable
? (MAP_FILE
| MAP_SHARED
) : (MAP_FILE
| MAP_PRIVATE
);
140 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, prot
, flags
, fd
, 0);
141 if ( p
== (uint8_t*)(-1) )
142 throwf("can't map file %s, errno=%d", path
, errno
);
145 // if fat file, process each architecture
146 const fat_header
* fh
= (fat_header
*)p
;
147 const mach_header
* mh
= (mach_header
*)p
;
148 if ( fh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
149 // Fat header is always big-endian
150 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
151 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
152 uint32_t fileOffset
= OSSwapBigToHostInt32(archs
[i
].offset
);
154 switch ( OSSwapBigToHostInt32(archs
[i
].cputype
) ) {
155 case CPU_TYPE_POWERPC
:
156 fRebasers
.push_back(new Rebaser
<ppc
>(&p
[fileOffset
]));
158 case CPU_TYPE_POWERPC64
:
159 fRebasers
.push_back(new Rebaser
<ppc64
>(&p
[fileOffset
]));
162 fRebasers
.push_back(new Rebaser
<x86
>(&p
[fileOffset
]));
164 case CPU_TYPE_X86_64
:
165 fRebasers
.push_back(new Rebaser
<x86_64
>(&p
[fileOffset
]));
168 throw "unknown file format";
171 catch (const char* msg
) {
172 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
178 if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC
)) {
179 fRebasers
.push_back(new Rebaser
<ppc
>(mh
));
181 else if ( (OSSwapBigToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapBigToHostInt32(mh
->cputype
) == CPU_TYPE_POWERPC64
)) {
182 fRebasers
.push_back(new Rebaser
<ppc64
>(mh
));
184 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_I386
)) {
185 fRebasers
.push_back(new Rebaser
<x86
>(mh
));
187 else if ( (OSSwapLittleToHostInt32(mh
->magic
) == MH_MAGIC_64
) && (OSSwapLittleToHostInt32(mh
->cputype
) == CPU_TYPE_X86_64
)) {
188 fRebasers
.push_back(new Rebaser
<x86_64
>(mh
));
191 throw "unknown file format";
194 catch (const char* msg
) {
195 fprintf(stderr
, "rebase warning: %s for %s\n", msg
, path
);
200 fFileSize
= stat_buf
.st_size
;
204 MultiArchRebaser::~MultiArchRebaser()
206 ::munmap(fMappingAddress
, fFileSize
);
209 void MultiArchRebaser::commit()
211 ::msync(fMappingAddress
, fFileSize
, MS_ASYNC
);
216 template <typename A
>
217 Rebaser
<A
>::Rebaser(const void* machHeader
)
218 : fHeader((const macho_header
<P
>*)machHeader
)
220 switch ( fHeader
->filetype() ) {
222 if ( (fHeader
->flags() & MH_SPLIT_SEGS
) != 0 )
223 throw "split-seg dylibs cannot be rebased";
228 throw "file is not a dylib or bundle";
233 template <> cpu_type_t Rebaser
<ppc
>::getArchitecture() const { return CPU_TYPE_POWERPC
; }
234 template <> cpu_type_t Rebaser
<ppc64
>::getArchitecture() const { return CPU_TYPE_POWERPC64
; }
235 template <> cpu_type_t Rebaser
<x86
>::getArchitecture() const { return CPU_TYPE_I386
; }
236 template <> cpu_type_t Rebaser
<x86_64
>::getArchitecture() const { return CPU_TYPE_X86_64
; }
239 template <typename A
>
240 uint64_t Rebaser
<A
>::getBaseAddress() const
242 uint64_t lowestSegmentAddress
= LLONG_MAX
;
243 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
244 const uint32_t cmd_count
= fHeader
->ncmds();
245 const macho_load_command
<P
>* cmd
= cmds
;
246 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
247 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
248 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
249 if ( segCmd
->vmaddr() < lowestSegmentAddress
) {
250 lowestSegmentAddress
= segCmd
->vmaddr();
253 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
255 return lowestSegmentAddress
;
258 template <typename A
>
259 uint64_t Rebaser
<A
>::getVMSize() const
261 const macho_segment_command
<P
>* highestSegmentCmd
= NULL
;
262 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
263 const uint32_t cmd_count
= fHeader
->ncmds();
264 const macho_load_command
<P
>* cmd
= cmds
;
265 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
266 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
267 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
268 if ( (highestSegmentCmd
== NULL
) || (segCmd
->vmaddr() > highestSegmentCmd
->vmaddr()) ) {
269 highestSegmentCmd
= segCmd
;
272 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
275 return ((highestSegmentCmd
->vmaddr() + highestSegmentCmd
->vmsize() - this->getBaseAddress() + 4095) & (-4096));
279 template <typename A
>
280 void Rebaser
<A
>::setBaseAddress(uint64_t addr
)
283 fSlide
= addr
- this->getBaseAddress();
285 // compute base address for relocations
286 this->setRelocBase();
288 // build cache of section index to section
289 this->buildSectionTable();
291 // update load commands
292 this->adjustLoadCommands();
294 // update symbol table
295 this->adjustSymbolTable();
297 // update writable segments that have internal pointers
301 template <typename A
>
302 void Rebaser
<A
>::adjustLoadCommands()
304 const macho_segment_command
<P
>* highestSegmentCmd
= NULL
;
305 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
306 const uint32_t cmd_count
= fHeader
->ncmds();
307 const macho_load_command
<P
>* cmd
= cmds
;
308 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
309 switch ( cmd
->cmd() ) {
311 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
312 // clear timestamp so that any prebound clients are invalidated
313 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
314 dylib
->set_timestamp(1);
318 case LC_LOAD_WEAK_DYLIB
:
319 if ( (fHeader
->flags() & MH_PREBOUND
) != 0 ) {
320 // clear expected timestamps so that this image will load with invalid prebinding
321 macho_dylib_command
<P
>* dylib
= (macho_dylib_command
<P
>*)cmd
;
322 dylib
->set_timestamp(2);
325 case macho_routines_command
<P
>::CMD
:
326 // update -init command
328 struct macho_routines_command
<P
>* routines
= (struct macho_routines_command
<P
>*)cmd
;
329 routines
->set_init_address(routines
->init_address() + fSlide
);
332 case macho_segment_command
<P
>::CMD
:
333 // update segment commands
335 macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
336 seg
->set_vmaddr(seg
->vmaddr() + fSlide
);
337 macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
338 macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
339 for(macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
340 sect
->set_addr(sect
->addr() + fSlide
);
345 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
350 template <typename A
>
351 void Rebaser
<A
>::buildSectionTable()
353 // build vector of sections
354 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
355 const uint32_t cmd_count
= fHeader
->ncmds();
356 const macho_load_command
<P
>* cmd
= cmds
;
357 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
358 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
359 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
361 mapping
.vmaddr
= seg
->vmaddr();
362 mapping
.vmsize
= seg
->vmsize();
363 mapping
.fileoff
= seg
->fileoff();
364 fVMMApping
.push_back(mapping
);
366 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
371 template <typename A
>
372 void Rebaser
<A
>::adjustSymbolTable()
374 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
375 macho_nlist
<P
>* symbolTable
= NULL
;
377 // get symbol table info
378 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
379 const uint32_t cmd_count
= fHeader
->ncmds();
380 const macho_load_command
<P
>* cmd
= cmds
;
381 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
382 switch (cmd
->cmd()) {
385 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
386 symbolTable
= (macho_nlist
<P
>*)(((uint8_t*)fHeader
) + symtab
->symoff());
390 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
393 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
396 // walk all exports and slide their n_value
397 macho_nlist
<P
>* lastExport
= &symbolTable
[dysymtab
->iextdefsym()+dysymtab
->nextdefsym()];
398 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->iextdefsym()]; entry
< lastExport
; ++entry
) {
399 if ( (entry
->n_type() & N_TYPE
) == N_SECT
)
400 entry
->set_n_value(entry
->n_value() + fSlide
);
403 // walk all local symbols and slide their n_value
404 macho_nlist
<P
>* lastLocal
= &symbolTable
[dysymtab
->ilocalsym()+dysymtab
->nlocalsym()];
405 for (macho_nlist
<P
>* entry
= &symbolTable
[dysymtab
->ilocalsym()]; entry
< lastLocal
; ++entry
) {
406 if ( entry
->n_sect() != NO_SECT
)
407 entry
->set_n_value(entry
->n_value() + fSlide
);
410 // FIXME ¥¥¥ adjust dylib_module if it exists
413 template <typename A
>
414 void Rebaser
<A
>::adjustDATA()
416 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
418 // get symbol table info
419 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
420 const uint32_t cmd_count
= fHeader
->ncmds();
421 const macho_load_command
<P
>* cmd
= cmds
;
422 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
423 switch (cmd
->cmd()) {
425 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
428 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
431 // walk all local relocations and slide every pointer
432 const macho_relocation_info
<P
>* const relocsStart
= (macho_relocation_info
<P
>*)(((uint8_t*)fHeader
) + dysymtab
->locreloff());
433 const macho_relocation_info
<P
>* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel()];
434 for (const macho_relocation_info
<P
>* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
435 this->doLocalRelocation(reloc
);
438 // walk non-lazy-pointers and slide the ones that are LOCAL
440 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
441 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
442 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
443 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
444 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
445 const uint32_t* const indirectTable
= (uint32_t*)(((uint8_t*)fHeader
) + dysymtab
->indirectsymoff());
446 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
447 if ( (sect
->flags() & SECTION_TYPE
) == S_NON_LAZY_SYMBOL_POINTERS
) {
448 const uint32_t indirectTableOffset
= sect
->reserved1();
449 uint32_t pointerCount
= sect
->size() / sizeof(pint_t
);
450 pint_t
* nonLazyPointer
= (pint_t
*)(((uint8_t*)fHeader
) + sect
->offset());
451 for (uint32_t i
=0; i
< pointerCount
; ++i
, ++nonLazyPointer
) {
452 if ( E::get32(indirectTable
[indirectTableOffset
+ i
]) == INDIRECT_SYMBOL_LOCAL
) {
453 P::setP(*nonLazyPointer
, A::P::getP(*nonLazyPointer
) + fSlide
);
459 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
465 template <typename A
>
466 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForVMAddress(uint32_t vmaddress
)
468 for(typename
std::vector
<vmmap
>::iterator it
= fVMMApping
.begin(); it
!= fVMMApping
.end(); ++it
) {
469 //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize);
470 if ( (vmaddress
>= it
->vmaddr
) && (vmaddress
< (it
->vmaddr
+it
->vmsize
)) ) {
471 return (pint_t
*)((vmaddress
- it
->vmaddr
) + it
->fileoff
+ (uint8_t*)fHeader
);
474 throwf("reloc address 0x%08X not found", vmaddress
);
479 void Rebaser
<x86_64
>::doLocalRelocation(const macho_relocation_info
<x86_64::P
>* reloc
)
481 if ( reloc
->r_type() == X86_64_RELOC_UNSIGNED
) {
482 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
483 P::setP(*addr
, P::getP(*addr
) + fSlide
);
486 throw "invalid relocation type";
491 void Rebaser
<ppc
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
493 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
494 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
495 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
496 P::setP(*addr
, P::getP(*addr
) + fSlide
);
500 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
501 if ( sreloc
->r_type() == PPC_RELOC_PB_LA_PTR
) {
502 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
505 throw "cannot rebase final linked image with scattered relocations";
511 void Rebaser
<x86
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
513 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
514 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
515 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
516 P::setP(*addr
, P::getP(*addr
) + fSlide
);
520 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
521 if ( sreloc
->r_type() == GENERIC_RELOC_PB_LA_PTR
) {
522 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
525 throw "cannot rebase final linked image with scattered relocations";
530 template <typename A
>
531 void Rebaser
<A
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
533 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
534 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
535 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
536 P::setP(*addr
, P::getP(*addr
) + fSlide
);
540 throw "cannot rebase final linked image with scattered relocations";
545 template <typename A
>
546 void Rebaser
<A
>::setRelocBase()
548 // reloc addresses are from the start of the mapped file (base address)
549 fRelocBase
= (pint_t
)fHeader
;
550 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
551 //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress);
555 void Rebaser
<ppc64
>::setRelocBase()
557 // reloc addresses either:
558 // 1) from the base address if no writable segment is > 4GB from base address
559 // 2) from start of first writable segment
560 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
561 const uint32_t cmd_count
= fHeader
->ncmds();
562 const macho_load_command
<P
>* cmd
= cmds
;
563 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
564 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
565 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
566 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
567 if ( (segCmd
->vmaddr() + segCmd
->vmsize() - this->getBaseAddress()) > 0x100000000ULL
) {
568 // found writable segment with address > 4GB past base address
569 fRelocBase
= segCmd
->fileoff() + (pint_t
)fHeader
;
570 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
575 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
577 // just use base address
578 fRelocBase
= (pint_t
)fHeader
;
579 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
583 void Rebaser
<x86_64
>::setRelocBase()
585 // reloc addresses are always based from the start of the first writable segment
586 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
587 const uint32_t cmd_count
= fHeader
->ncmds();
588 const macho_load_command
<P
>* cmd
= cmds
;
589 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
590 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
591 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
592 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
593 fRelocBase
= segCmd
->fileoff() + (pint_t
)fHeader
;
594 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
598 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
600 throw "no writable segment";
604 static void copyFile(const char* srcFile
, const char* dstFile
)
607 int src
= open(srcFile
, O_RDONLY
);
609 throwf("can't open file %s, errno=%d", srcFile
, errno
);
610 struct stat stat_buf
;
611 if ( fstat(src
, &stat_buf
) == -1)
612 throwf("can't stat open file %s, errno=%d", srcFile
, errno
);
614 // create new file with all same permissions to hold copy of dylib
616 int dst
= open(dstFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
618 throwf("can't create temp file %s, errnor=%d", dstFile
, errno
);
620 // mark source as "don't cache"
621 (void)fcntl(src
, F_NOCACHE
, 1);
622 // we want to cache the dst because we are about to map it in and modify it
624 // copy permission bits
625 if ( chmod(dstFile
, stat_buf
.st_mode
& 07777) == -1 )
626 throwf("can't chmod temp file %s, errno=%d", dstFile
, errno
);
627 if ( chown(dstFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
628 throwf("can't chown temp file %s, errno=%d", dstFile
, errno
);
632 const uint32_t kBufferSize
= 128*1024;
633 static uint8_t* buffer
= NULL
;
634 if ( buffer
== NULL
) {
635 vm_address_t addr
= 0;
636 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
637 buffer
= (uint8_t*)addr
;
639 throw "can't allcoate copy buffer";
641 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
642 if ( write(dst
, buffer
, len
) == -1 )
643 throwf("write failure copying feil %s, errno=%d", dstFile
, errno
);
647 int result1
= close(dst
);
648 int result2
= close(src
);
649 if ( (result1
!= 0) || (result2
!= 0) )
650 throw "can't close file";
654 // scan dylibs and collect size info
655 // calculate new base address for each dylib
657 // copy to temp and mmap
671 fileInfo(const char* p
) : path(p
) {}
674 std::vector
<archInfo
> archs
;
678 // add archInfos to fileInfo for every slice of a fat file
679 // for ppc, there may be duplicate architectures (with different sub-types)
681 static void setSizes(fileInfo
& info
, const std::set
<cpu_type_t
>& onlyArchs
)
683 const MultiArchRebaser
mar(info
.path
);
684 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
685 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
686 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
687 AbstractRebaser
* rebaser
= *rit
;
688 if ( rebaser
->getArchitecture() == *ait
) {
691 ai
.vmSize
= rebaser
->getVMSize();
692 ai
.orgBase
= rebaser
->getBaseAddress();
694 //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize);
695 info
.archs
.push_back(ai
);
701 static const char* nameForArch(cpu_type_t arch
)
704 case CPU_TYPE_POWERPC
:
706 case CPU_TYPE_POWERPC64
:
710 case CPU_TYPE_X86_64
:
716 static void rebase(const fileInfo
& info
)
718 // generate temp file name
719 char realFilePath
[PATH_MAX
];
720 if ( realpath(info
.path
, realFilePath
) == NULL
) {
721 throwf("realpath() failed on %s, errno=%d", info
.path
, errno
);
723 const char* tempPath
;
724 asprintf((char**)&tempPath
, "%s_rebase", realFilePath
);
726 // copy whole file to temp file
727 copyFile(info
.path
, tempPath
);
731 MultiArchRebaser
mar(tempPath
, true);
732 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
733 for(std::vector
<archInfo
>::const_iterator fait
=info
.archs
.begin(); fait
!= info
.archs
.end(); ++fait
) {
734 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
735 if ( (*rit
)->getArchitecture() == fait
->arch
) {
736 (*rit
)->setBaseAddress(fait
->newBase
);
738 printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait
->arch
), fait
->orgBase
, fait
->newBase
, info
.path
);
743 // flush temp file out to disk
747 int result
= rename(tempPath
, info
.path
);
749 throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath
, info
.path
, errno
);
752 // make sure every really gets out to disk
755 catch (const char* msg
) {
759 // throw exception with file name added
761 asprintf((char**)&newMsg
, "%s for file %s", msg
, info
.path
);
766 static uint64_t totalVMSize(cpu_type_t arch
, std::vector
<fileInfo
>& files
)
768 uint64_t totalSize
= 0;
769 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
771 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
772 if ( fait
->arch
== arch
)
773 totalSize
+= fait
->vmSize
;
779 static uint64_t startAddress(cpu_type_t arch
, std::vector
<fileInfo
>& files
, uint64_t lowAddress
, uint64_t highAddress
)
781 if ( lowAddress
!= 0 )
783 else if ( highAddress
!= 0 ) {
784 uint64_t totalSize
= totalVMSize(arch
, files
);
785 if ( highAddress
< totalSize
)
786 throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress
, totalSize
);
787 return highAddress
- totalSize
;
790 if ( (arch
== CPU_TYPE_I386
) || (arch
== CPU_TYPE_POWERPC
) ) {
791 // place dylibs below dyld
792 uint64_t topAddr
= 0x8FE00000;
793 uint64_t totalSize
= totalVMSize(arch
, files
);
794 if ( totalSize
> topAddr
)
795 throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize
);
796 return topAddr
- totalSize
;
798 else if ( arch
== CPU_TYPE_POWERPC64
) {
799 return 0x200000000ULL
;
801 else if ( arch
== CPU_TYPE_X86_64
) {
802 return 0x200000000ULL
;
805 throw "unknown architecture";
811 fprintf(stderr
, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n");
815 int main(int argc
, const char* argv
[])
817 std::vector
<fileInfo
> files
;
818 std::set
<cpu_type_t
> onlyArchs
;
819 uint64_t lowAddress
= 0;
820 uint64_t highAddress
= 0;
823 // parse command line options
825 for(int i
=1; i
< argc
; ++i
) {
826 const char* arg
= argv
[i
];
827 if ( arg
[0] == '-' ) {
828 if ( strcmp(arg
, "-v") == 0 ) {
831 else if ( strcmp(arg
, "-low_address") == 0 ) {
832 lowAddress
= strtoull(argv
[++i
], &endptr
, 16);
834 else if ( strcmp(arg
, "-high_address") == 0 ) {
835 highAddress
= strtoull(argv
[++i
], &endptr
, 16);
837 else if ( strcmp(arg
, "-arch") == 0 ) {
838 const char* arch
= argv
[++i
];
839 if ( strcmp(arch
, "ppc") == 0 )
840 onlyArchs
.insert(CPU_TYPE_POWERPC
);
841 else if ( strcmp(arch
, "ppc64") == 0 )
842 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
843 else if ( strcmp(arch
, "i386") == 0 )
844 onlyArchs
.insert(CPU_TYPE_I386
);
845 else if ( strcmp(arch
, "x86_64") == 0 )
846 onlyArchs
.insert(CPU_TYPE_X86_64
);
848 throwf("unknown architecture %s", arch
);
852 throwf("unknown option: %s\n", arg
);
856 files
.push_back(fileInfo(arg
));
860 if ( files
.size() == 0 )
861 throw "no files specified";
863 // use all architectures if no restrictions specified
864 if ( onlyArchs
.size() == 0 ) {
865 onlyArchs
.insert(CPU_TYPE_POWERPC
);
866 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
867 onlyArchs
.insert(CPU_TYPE_I386
);
868 onlyArchs
.insert(CPU_TYPE_X86_64
);
871 // scan files and collect sizes
872 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
873 setSizes(*it
, onlyArchs
);
876 // assign new base address for each arch
877 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
878 cpu_type_t arch
= *ait
;
879 uint64_t baseAddress
= startAddress(arch
, files
, lowAddress
, highAddress
);
880 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
882 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
883 if ( fait
->arch
== arch
) {
884 fait
->newBase
= baseAddress
;
885 baseAddress
+= fait
->vmSize
;
886 baseAddress
= (baseAddress
+ 4095) & (-4096); // page align
892 // rebase each file if it contains something rebaseable
893 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
895 if ( fi
.archs
.size() > 0 )
900 catch (const char* msg
) {
901 fprintf(stderr
, "rebase failed: %s\n", msg
);