1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2008 Apple 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
);
94 void rebaseAt(int segIndex
, uint64_t offset
, uint8_t type
);
96 const macho_header
<P
>* fHeader
;
97 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 %s, errno=%d", path
, 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 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
420 throwf("malformed uleb128");
422 uint64_t slice
= *p
& 0x7f;
424 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
425 throwf("uleb128 too big");
427 result
|= (slice
<< bit
);
435 template <typename A
>
436 void Rebaser
<A
>::rebaseAt(int segIndex
, uint64_t offset
, uint8_t type
)
438 //fprintf(stderr, "rebaseAt(seg=%d, offset=0x%08llX, type=%d\n", segIndex, offset, type);
439 static int lastSegIndex
= -1;
440 static uint8_t* lastSegMappedStart
= NULL
;
441 if ( segIndex
!= lastSegIndex
) {
442 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
443 const uint32_t cmd_count
= fHeader
->ncmds();
444 const macho_load_command
<P
>* cmd
= cmds
;
446 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
447 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
448 if ( segIndex
== segCount
) {
449 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
450 lastSegMappedStart
= (uint8_t*)fHeader
+ seg
->fileoff();
451 lastSegIndex
== segCount
;
456 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
460 pint_t
* locationToFix
= (pint_t
*)(lastSegMappedStart
+offset
);
461 uint32_t* locationToFix32
= (uint32_t*)(lastSegMappedStart
+offset
);
463 case REBASE_TYPE_POINTER
:
464 P::setP(*locationToFix
, A::P::getP(*locationToFix
) + fSlide
);
466 case REBASE_TYPE_TEXT_ABSOLUTE32
:
467 E::set32(*locationToFix32
, E::get32(*locationToFix32
) + fSlide
);
470 throwf("bad rebase type %d", type
);
475 template <typename A
>
476 void Rebaser
<A
>::adjustDATA()
478 const macho_dysymtab_command
<P
>* dysymtab
= NULL
;
479 const macho_dyld_info_command
<P
>* dyldInfo
= NULL
;
481 // get symbol table info
482 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
483 const uint32_t cmd_count
= fHeader
->ncmds();
484 const macho_load_command
<P
>* cmd
= cmds
;
485 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
486 switch (cmd
->cmd()) {
488 dysymtab
= (macho_dysymtab_command
<P
>*)cmd
;
491 case LC_DYLD_INFO_ONLY
:
492 dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
495 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
498 // use new encoding of rebase info if present
499 if ( dyldInfo
!= NULL
) {
500 if ( dyldInfo
->rebase_size() != 0 ) {
501 const uint8_t* p
= (uint8_t*)fHeader
+ dyldInfo
->rebase_off();
502 const uint8_t* end
= &p
[dyldInfo
->rebase_size()];
510 while ( !done
&& (p
< end
) ) {
511 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
512 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
515 case REBASE_OPCODE_DONE
:
518 case REBASE_OPCODE_SET_TYPE_IMM
:
521 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
522 segIndex
= immediate
;
523 offset
= read_uleb128(p
, end
);
525 case REBASE_OPCODE_ADD_ADDR_ULEB
:
526 offset
+= read_uleb128(p
, end
);
528 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
529 offset
+= immediate
*sizeof(pint_t
);
531 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
532 for (int i
=0; i
< immediate
; ++i
) {
533 rebaseAt(segIndex
, offset
, type
);
534 offset
+= sizeof(pint_t
);
537 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
538 count
= read_uleb128(p
, end
);
539 for (uint32_t i
=0; i
< count
; ++i
) {
540 rebaseAt(segIndex
, offset
, type
);
541 offset
+= sizeof(pint_t
);
544 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
545 rebaseAt(segIndex
, offset
, type
);
546 offset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
548 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
549 count
= read_uleb128(p
, end
);
550 skip
= read_uleb128(p
, end
);
551 for (uint32_t i
=0; i
< count
; ++i
) {
552 rebaseAt(segIndex
, offset
, type
);
553 offset
+= skip
+ sizeof(pint_t
);
557 throwf("bad rebase opcode %d", *p
);
566 // walk all local relocations and slide every pointer
567 const macho_relocation_info
<P
>* const relocsStart
= (macho_relocation_info
<P
>*)(((uint8_t*)fHeader
) + dysymtab
->locreloff());
568 const macho_relocation_info
<P
>* const relocsEnd
= &relocsStart
[dysymtab
->nlocrel()];
569 for (const macho_relocation_info
<P
>* reloc
=relocsStart
; reloc
< relocsEnd
; ++reloc
) {
570 this->doLocalRelocation(reloc
);
573 // walk non-lazy-pointers and slide the ones that are LOCAL
575 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
576 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
577 const macho_segment_command
<P
>* seg
= (macho_segment_command
<P
>*)cmd
;
578 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)seg
+ sizeof(macho_segment_command
<P
>));
579 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[seg
->nsects()];
580 const uint32_t* const indirectTable
= (uint32_t*)(((uint8_t*)fHeader
) + dysymtab
->indirectsymoff());
581 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
582 if ( (sect
->flags() & SECTION_TYPE
) == S_NON_LAZY_SYMBOL_POINTERS
) {
583 const uint32_t indirectTableOffset
= sect
->reserved1();
584 uint32_t pointerCount
= sect
->size() / sizeof(pint_t
);
585 pint_t
* nonLazyPointer
= (pint_t
*)(((uint8_t*)fHeader
) + sect
->offset());
586 for (uint32_t i
=0; i
< pointerCount
; ++i
, ++nonLazyPointer
) {
587 if ( E::get32(indirectTable
[indirectTableOffset
+ i
]) == INDIRECT_SYMBOL_LOCAL
) {
588 P::setP(*nonLazyPointer
, A::P::getP(*nonLazyPointer
) + fSlide
);
594 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
600 template <typename A
>
601 typename
A::P::uint_t
* Rebaser
<A
>::mappedAddressForVMAddress(uint32_t vmaddress
)
603 for(typename
std::vector
<vmmap
>::iterator it
= fVMMApping
.begin(); it
!= fVMMApping
.end(); ++it
) {
604 //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize);
605 if ( (vmaddress
>= it
->vmaddr
) && (vmaddress
< (it
->vmaddr
+it
->vmsize
)) ) {
606 return (pint_t
*)((vmaddress
- it
->vmaddr
) + it
->fileoff
+ (uint8_t*)fHeader
);
609 throwf("reloc address 0x%08X not found", vmaddress
);
614 void Rebaser
<x86_64
>::doLocalRelocation(const macho_relocation_info
<x86_64::P
>* reloc
)
616 if ( reloc
->r_type() == X86_64_RELOC_UNSIGNED
) {
617 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
618 P::setP(*addr
, P::getP(*addr
) + fSlide
);
621 throw "invalid relocation type";
626 void Rebaser
<ppc
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
628 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
629 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
630 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
631 P::setP(*addr
, P::getP(*addr
) + fSlide
);
635 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
636 if ( sreloc
->r_type() == PPC_RELOC_PB_LA_PTR
) {
637 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
640 throw "cannot rebase final linked image with scattered relocations";
646 void Rebaser
<x86
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
648 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
649 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
650 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
651 P::setP(*addr
, P::getP(*addr
) + fSlide
);
655 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
656 if ( sreloc
->r_type() == GENERIC_RELOC_PB_LA_PTR
) {
657 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
660 throw "cannot rebase final linked image with scattered relocations";
666 void Rebaser
<arm
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
668 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
669 if ( reloc
->r_type() == ARM_RELOC_VANILLA
) {
670 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
671 P::setP(*addr
, P::getP(*addr
) + fSlide
);
675 macho_scattered_relocation_info
<P
>* sreloc
= (macho_scattered_relocation_info
<P
>*)reloc
;
676 if ( sreloc
->r_type() == ARM_RELOC_PB_LA_PTR
) {
677 sreloc
->set_r_value( sreloc
->r_value() + fSlide
);
680 throw "cannot rebase final linked image with scattered relocations";
685 template <typename A
>
686 void Rebaser
<A
>::doLocalRelocation(const macho_relocation_info
<P
>* reloc
)
688 if ( (reloc
->r_address() & R_SCATTERED
) == 0 ) {
689 if ( reloc
->r_type() == GENERIC_RELOC_VANILLA
) {
690 pint_t
* addr
= mappedAddressForVMAddress(reloc
->r_address() + fOrignalVMRelocBaseAddress
);
691 P::setP(*addr
, P::getP(*addr
) + fSlide
);
695 throw "cannot rebase final linked image with scattered relocations";
700 template <typename A
>
701 void Rebaser
<A
>::setRelocBase()
703 // reloc addresses are from the start of the mapped file (base address)
704 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
705 //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress);
709 void Rebaser
<ppc64
>::setRelocBase()
711 // reloc addresses either:
712 // 1) from the base address if no writable segment is > 4GB from base address
713 // 2) from start of first writable segment
714 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
715 const uint32_t cmd_count
= fHeader
->ncmds();
716 const macho_load_command
<P
>* cmd
= cmds
;
717 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
718 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
719 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
720 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
721 if ( (segCmd
->vmaddr() + segCmd
->vmsize() - this->getBaseAddress()) > 0x100000000ULL
) {
722 // found writable segment with address > 4GB past base address
723 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
728 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
730 // just use base address
731 fOrignalVMRelocBaseAddress
= this->getBaseAddress();
735 void Rebaser
<x86_64
>::setRelocBase()
737 // reloc addresses are always based from the start of the first writable segment
738 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
739 const uint32_t cmd_count
= fHeader
->ncmds();
740 const macho_load_command
<P
>* cmd
= cmds
;
741 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
742 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
743 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
744 if ( segCmd
->initprot() & VM_PROT_WRITE
) {
745 fOrignalVMRelocBaseAddress
= segCmd
->vmaddr();
749 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
751 throw "no writable segment";
755 static void copyFile(const char* srcFile
, const char* dstFile
)
758 int src
= open(srcFile
, O_RDONLY
);
760 throwf("can't open file %s, errno=%d", srcFile
, errno
);
761 struct stat stat_buf
;
762 if ( fstat(src
, &stat_buf
) == -1)
763 throwf("can't stat open file %s, errno=%d", srcFile
, errno
);
765 // create new file with all same permissions to hold copy of dylib
767 int dst
= open(dstFile
, O_CREAT
| O_RDWR
| O_TRUNC
, stat_buf
.st_mode
);
769 throwf("can't create temp file %s, errnor=%d", dstFile
, errno
);
771 // mark source as "don't cache"
772 (void)fcntl(src
, F_NOCACHE
, 1);
773 // we want to cache the dst because we are about to map it in and modify it
775 // copy permission bits
776 if ( chmod(dstFile
, stat_buf
.st_mode
& 07777) == -1 )
777 throwf("can't chmod temp file %s, errno=%d", dstFile
, errno
);
778 if ( chown(dstFile
, stat_buf
.st_uid
, stat_buf
.st_gid
) == -1)
779 throwf("can't chown temp file %s, errno=%d", dstFile
, errno
);
783 const uint32_t kBufferSize
= 128*1024;
784 static uint8_t* buffer
= NULL
;
785 if ( buffer
== NULL
) {
786 vm_address_t addr
= 0;
787 if ( vm_allocate(mach_task_self(), &addr
, kBufferSize
, true /*find range*/) == KERN_SUCCESS
)
788 buffer
= (uint8_t*)addr
;
790 throw "can't allcoate copy buffer";
792 while ( (len
= read(src
, buffer
, kBufferSize
)) > 0 ) {
793 if ( write(dst
, buffer
, len
) == -1 )
794 throwf("write failure copying feil %s, errno=%d", dstFile
, errno
);
798 int result1
= close(dst
);
799 int result2
= close(src
);
800 if ( (result1
!= 0) || (result2
!= 0) )
801 throw "can't close file";
805 // scan dylibs and collect size info
806 // calculate new base address for each dylib
808 // copy to temp and mmap
822 fileInfo(const char* p
) : path(p
) {}
825 std::vector
<archInfo
> archs
;
829 // add archInfos to fileInfo for every slice of a fat file
830 // for ppc, there may be duplicate architectures (with different sub-types)
832 static void setSizes(fileInfo
& info
, const std::set
<cpu_type_t
>& onlyArchs
)
834 const MultiArchRebaser
mar(info
.path
);
835 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
836 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
837 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
838 AbstractRebaser
* rebaser
= *rit
;
839 if ( rebaser
->getArchitecture() == *ait
) {
842 ai
.vmSize
= rebaser
->getVMSize();
843 ai
.orgBase
= rebaser
->getBaseAddress();
845 //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize);
846 info
.archs
.push_back(ai
);
852 static const char* nameForArch(cpu_type_t arch
)
855 case CPU_TYPE_POWERPC
:
857 case CPU_TYPE_POWERPC64
:
861 case CPU_TYPE_X86_64
:
869 static void rebase(const fileInfo
& info
)
871 // generate temp file name
872 char realFilePath
[PATH_MAX
];
873 if ( realpath(info
.path
, realFilePath
) == NULL
) {
874 throwf("realpath() failed on %s, errno=%d", info
.path
, errno
);
876 const char* tempPath
;
877 asprintf((char**)&tempPath
, "%s_rebase", realFilePath
);
879 // copy whole file to temp file
880 copyFile(info
.path
, tempPath
);
884 MultiArchRebaser
mar(tempPath
, true);
885 const std::vector
<AbstractRebaser
*>& rebasers
= mar
.getArchs();
886 for(std::vector
<archInfo
>::const_iterator fait
=info
.archs
.begin(); fait
!= info
.archs
.end(); ++fait
) {
887 for(std::vector
<AbstractRebaser
*>::const_iterator rit
=rebasers
.begin(); rit
!= rebasers
.end(); ++rit
) {
888 if ( (*rit
)->getArchitecture() == fait
->arch
) {
889 (*rit
)->setBaseAddress(fait
->newBase
);
891 printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait
->arch
), fait
->orgBase
, fait
->newBase
, info
.path
);
896 // flush temp file out to disk
900 int result
= rename(tempPath
, info
.path
);
902 throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath
, info
.path
, errno
);
905 // make sure every really gets out to disk
908 catch (const char* msg
) {
912 // throw exception with file name added
914 asprintf((char**)&newMsg
, "%s for file %s", msg
, info
.path
);
919 static uint64_t totalVMSize(cpu_type_t arch
, std::vector
<fileInfo
>& files
)
921 uint64_t totalSize
= 0;
922 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
924 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
925 if ( fait
->arch
== arch
)
926 totalSize
+= fait
->vmSize
;
932 static uint64_t startAddress(cpu_type_t arch
, std::vector
<fileInfo
>& files
, uint64_t lowAddress
, uint64_t highAddress
)
934 if ( lowAddress
!= 0 )
936 else if ( highAddress
!= 0 ) {
937 uint64_t totalSize
= totalVMSize(arch
, files
);
938 if ( highAddress
< totalSize
)
939 throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress
, totalSize
);
940 return highAddress
- totalSize
;
943 if ( (arch
== CPU_TYPE_I386
) || (arch
== CPU_TYPE_POWERPC
) ) {
944 // place dylibs below dyld
945 uint64_t topAddr
= 0x8FE00000;
946 uint64_t totalSize
= totalVMSize(arch
, files
);
947 if ( totalSize
> topAddr
)
948 throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize
);
949 return topAddr
- totalSize
;
951 else if ( arch
== CPU_TYPE_POWERPC64
) {
952 return 0x200000000ULL
;
954 else if ( arch
== CPU_TYPE_X86_64
) {
955 return 0x200000000ULL
;
957 else if ( arch
== CPU_TYPE_ARM
) {
958 // place dylibs below dyld
959 uint64_t topAddr
= 0x2FE00000;
960 uint64_t totalSize
= totalVMSize(arch
, files
);
961 if ( totalSize
> topAddr
)
962 throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize
);
963 return topAddr
- totalSize
;
966 throw "unknown architecture";
972 fprintf(stderr
, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n");
976 int main(int argc
, const char* argv
[])
978 std::vector
<fileInfo
> files
;
979 std::set
<cpu_type_t
> onlyArchs
;
980 uint64_t lowAddress
= 0;
981 uint64_t highAddress
= 0;
984 // parse command line options
986 for(int i
=1; i
< argc
; ++i
) {
987 const char* arg
= argv
[i
];
988 if ( arg
[0] == '-' ) {
989 if ( strcmp(arg
, "-v") == 0 ) {
992 else if ( strcmp(arg
, "-low_address") == 0 ) {
993 lowAddress
= strtoull(argv
[++i
], &endptr
, 16);
995 else if ( strcmp(arg
, "-high_address") == 0 ) {
996 highAddress
= strtoull(argv
[++i
], &endptr
, 16);
998 else if ( strcmp(arg
, "-arch") == 0 ) {
999 const char* arch
= argv
[++i
];
1000 if ( strcmp(arch
, "ppc") == 0 )
1001 onlyArchs
.insert(CPU_TYPE_POWERPC
);
1002 else if ( strcmp(arch
, "ppc64") == 0 )
1003 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
1004 else if ( strcmp(arch
, "i386") == 0 )
1005 onlyArchs
.insert(CPU_TYPE_I386
);
1006 else if ( strcmp(arch
, "x86_64") == 0 )
1007 onlyArchs
.insert(CPU_TYPE_X86_64
);
1008 else if ( strcmp(arch
, "arm") == 0 )
1009 onlyArchs
.insert(CPU_TYPE_ARM
);
1010 else if ( strcmp(arch
, "armv6") == 0 )
1011 onlyArchs
.insert(CPU_TYPE_ARM
);
1013 throwf("unknown architecture %s", arch
);
1017 throwf("unknown option: %s\n", arg
);
1021 files
.push_back(fileInfo(arg
));
1025 if ( files
.size() == 0 )
1026 throw "no files specified";
1028 // use all architectures if no restrictions specified
1029 if ( onlyArchs
.size() == 0 ) {
1030 onlyArchs
.insert(CPU_TYPE_POWERPC
);
1031 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
1032 onlyArchs
.insert(CPU_TYPE_I386
);
1033 onlyArchs
.insert(CPU_TYPE_X86_64
);
1034 onlyArchs
.insert(CPU_TYPE_ARM
);
1037 // scan files and collect sizes
1038 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1039 setSizes(*it
, onlyArchs
);
1042 // assign new base address for each arch
1043 for(std::set
<cpu_type_t
>::iterator ait
=onlyArchs
.begin(); ait
!= onlyArchs
.end(); ++ait
) {
1044 cpu_type_t arch
= *ait
;
1045 uint64_t baseAddress
= startAddress(arch
, files
, lowAddress
, highAddress
);
1046 for(std::vector
<fileInfo
>::iterator fit
=files
.begin(); fit
!= files
.end(); ++fit
) {
1047 fileInfo
& fi
= *fit
;
1048 for(std::vector
<archInfo
>::iterator fait
=fi
.archs
.begin(); fait
!= fi
.archs
.end(); ++fait
) {
1049 if ( fait
->arch
== arch
) {
1050 fait
->newBase
= baseAddress
;
1051 baseAddress
+= fait
->vmSize
;
1052 baseAddress
= (baseAddress
+ 4095) & (-4096); // page align
1058 // rebase each file if it contains something rebaseable
1059 for(std::vector
<fileInfo
>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
1061 if ( fi
.archs
.size() > 0 )
1066 catch (const char* msg
) {
1067 fprintf(stderr
, "rebase failed: %s\n", msg
);