+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <vector>
-#include <set>
-
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-
-static bool verbose = false;
-
-__attribute__((noreturn))
-void throwf(const char* format, ...)
-{
- va_list list;
- char* p;
- va_start(list, format);
- vasprintf(&p, format, list);
- va_end(list);
-
- const char* t = p;
- throw t;
-}
-
-
-class AbstractRebaser
-{
-public:
- virtual cpu_type_t getArchitecture() const = 0;
- virtual uint64_t getBaseAddress() const = 0;
- virtual uint64_t getVMSize() const = 0;
- virtual void setBaseAddress(uint64_t) = 0;
-};
-
-
-template <typename A>
-class Rebaser : public AbstractRebaser
-{
-public:
- Rebaser(const void* machHeader);
- virtual ~Rebaser() {}
-
- virtual cpu_type_t getArchitecture() const;
- virtual uint64_t getBaseAddress() const;
- virtual uint64_t getVMSize() const;
- virtual void setBaseAddress(uint64_t);
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
- struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; };
-
- void setRelocBase();
- void buildSectionTable();
- void adjustLoadCommands();
- void adjustSymbolTable();
- void adjustDATA();
- void doLocalRelocation(const macho_relocation_info<P>* reloc);
- pint_t* mappedAddressForVMAddress(uint32_t vmaddress);
-
- const macho_header<P>* fHeader;
- pint_t fOrignalVMRelocBaseAddress;
- pint_t fSlide;
- pint_t fRelocBase;
- std::vector<vmmap> fVMMApping;
-};
-
-
-
-class MultiArchRebaser
-{
-public:
- MultiArchRebaser(const char* path, bool writable=false);
- ~MultiArchRebaser();
-
- const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; }
- void commit();
-
-private:
- std::vector<AbstractRebaser*> fRebasers;
- void* fMappingAddress;
- uint64_t fFileSize;
-};
-
-
-
-MultiArchRebaser::MultiArchRebaser(const char* path, bool writable)
- : fMappingAddress(0), fFileSize(0)
-{
- // map in whole file
- int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0);
- if ( fd == -1 )
- throwf("can't open file, errno=%d", errno);
- struct stat stat_buf;
- if ( fstat(fd, &stat_buf) == -1)
- throwf("can't stat open file %s, errno=%d", path, errno);
- if ( stat_buf.st_size < 20 )
- throwf("file too small %s", path);
- const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
- const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE);
- uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0);
- if ( p == (uint8_t*)(-1) )
- throwf("can't map file %s, errno=%d", path, errno);
- ::close(fd);
-
- // if fat file, process each architecture
- const fat_header* fh = (fat_header*)p;
- const mach_header* mh = (mach_header*)p;
- if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- // Fat header is always big-endian
- const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
- for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset);
- try {
- switch ( OSSwapBigToHostInt32(archs[i].cputype) ) {
- case CPU_TYPE_POWERPC:
- fRebasers.push_back(new Rebaser<ppc>(&p[fileOffset]));
- break;
- case CPU_TYPE_POWERPC64:
- fRebasers.push_back(new Rebaser<ppc64>(&p[fileOffset]));
- break;
- case CPU_TYPE_I386:
- fRebasers.push_back(new Rebaser<x86>(&p[fileOffset]));
- break;
- case CPU_TYPE_X86_64:
- fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset]));
- break;
- case CPU_TYPE_ARM:
- fRebasers.push_back(new Rebaser<arm>(&p[fileOffset]));
- break;
- default:
- throw "unknown file format";
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
- }
- }
- }
- else {
- try {
- if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
- fRebasers.push_back(new Rebaser<ppc>(mh));
- }
- else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
- fRebasers.push_back(new Rebaser<ppc64>(mh));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
- fRebasers.push_back(new Rebaser<x86>(mh));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
- fRebasers.push_back(new Rebaser<x86_64>(mh));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
- fRebasers.push_back(new Rebaser<arm>(mh));
- }
- else {
- throw "unknown file format";
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
- }
- }
-
- fMappingAddress = p;
- fFileSize = stat_buf.st_size;
-}
-
-
-MultiArchRebaser::~MultiArchRebaser()
-{
- ::munmap(fMappingAddress, fFileSize);
-}
-
-void MultiArchRebaser::commit()
-{
- ::msync(fMappingAddress, fFileSize, MS_ASYNC);
-}
-
-
-
-template <typename A>
-Rebaser<A>::Rebaser(const void* machHeader)
- : fHeader((const macho_header<P>*)machHeader)
-{
- switch ( fHeader->filetype() ) {
- case MH_DYLIB:
- if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 )
- throw "split-seg dylibs cannot be rebased";
- break;
- case MH_BUNDLE:
- break;
- default:
- throw "file is not a dylib or bundle";
- }
-
-}
-
-template <> cpu_type_t Rebaser<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
-template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; }
-template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
-template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
-template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
-
-template <typename A>
-uint64_t Rebaser<A>::getBaseAddress() const
-{
- uint64_t lowestSegmentAddress = LLONG_MAX;
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
- if ( segCmd->vmaddr() < lowestSegmentAddress ) {
- lowestSegmentAddress = segCmd->vmaddr();
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- return lowestSegmentAddress;
-}
-
-template <typename A>
-uint64_t Rebaser<A>::getVMSize() const
-{
- const macho_segment_command<P>* highestSegmentCmd = NULL;
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
- if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) {
- highestSegmentCmd = segCmd;
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096));
-}
-
-
-template <typename A>
-void Rebaser<A>::setBaseAddress(uint64_t addr)
-{
- // calculate slide
- fSlide = addr - this->getBaseAddress();
-
- // compute base address for relocations
- this->setRelocBase();
-
- // build cache of section index to section
- this->buildSectionTable();
-
- // update load commands
- this->adjustLoadCommands();
-
- // update symbol table
- this->adjustSymbolTable();
-
- // update writable segments that have internal pointers
- this->adjustDATA();
-}
-
-template <typename A>
-void Rebaser<A>::adjustLoadCommands()
-{
- const macho_segment_command<P>* highestSegmentCmd = NULL;
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch ( cmd->cmd() ) {
- case LC_ID_DYLIB:
- if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
- // clear timestamp so that any prebound clients are invalidated
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- dylib->set_timestamp(1);
- }
- break;
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
- // clear expected timestamps so that this image will load with invalid prebinding
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- dylib->set_timestamp(2);
- }
- break;
- case macho_routines_command<P>::CMD:
- // update -init command
- {
- struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
- routines->set_init_address(routines->init_address() + fSlide);
- }
- break;
- case macho_segment_command<P>::CMD:
- // update segment commands
- {
- macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- seg->set_vmaddr(seg->vmaddr() + fSlide);
- macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
- macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
- for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- sect->set_addr(sect->addr() + fSlide);
- }
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-
-template <typename A>
-void Rebaser<A>::buildSectionTable()
-{
- // build vector of sections
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- vmmap mapping;
- mapping.vmaddr = seg->vmaddr();
- mapping.vmsize = seg->vmsize();
- mapping.fileoff = seg->fileoff();
- fVMMApping.push_back(mapping);
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-
-template <typename A>
-void Rebaser<A>::adjustSymbolTable()
-{
- const macho_dysymtab_command<P>* dysymtab = NULL;
- macho_nlist<P>* symbolTable = NULL;
-
- // get symbol table info
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_SYMTAB:
- {
- const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
- symbolTable = (macho_nlist<P>*)(((uint8_t*)fHeader) + symtab->symoff());
- }
- break;
- case LC_DYSYMTAB:
- dysymtab = (macho_dysymtab_command<P>*)cmd;
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- // walk all exports and slide their n_value
- macho_nlist<P>* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()];
- for (macho_nlist<P>* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) {
- if ( (entry->n_type() & N_TYPE) == N_SECT )
- entry->set_n_value(entry->n_value() + fSlide);
- }
-
- // walk all local symbols and slide their n_value
- macho_nlist<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()];
- for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) {
- if ( entry->n_sect() != NO_SECT )
- entry->set_n_value(entry->n_value() + fSlide);
- }
-
- // FIXME ¥¥¥ adjust dylib_module if it exists
-}
-
-template <typename A>
-void Rebaser<A>::adjustDATA()
-{
- const macho_dysymtab_command<P>* dysymtab = NULL;
-
- // get symbol table info
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_DYSYMTAB:
- dysymtab = (macho_dysymtab_command<P>*)cmd;
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- // walk all local relocations and slide every pointer
- const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(((uint8_t*)fHeader) + dysymtab->locreloff());
- const macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()];
- for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
- this->doLocalRelocation(reloc);
- }
-
- // walk non-lazy-pointers and slide the ones that are LOCAL
- cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
- const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
- const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff());
- for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
- const uint32_t indirectTableOffset = sect->reserved1();
- uint32_t pointerCount = sect->size() / sizeof(pint_t);
- pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset());
- for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) {
- if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) {
- P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide);
- }
- }
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
-}
-
-
-template <typename A>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(uint32_t vmaddress)
-{
- for(typename std::vector<vmmap>::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) {
- //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize);
- if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) {
- return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader);
- }
- }
- throwf("reloc address 0x%08X not found", vmaddress);
-}
-
-
-template <>
-void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc)
-{
- if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) {
- pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
- P::setP(*addr, P::getP(*addr) + fSlide);
- }
- else {
- throw "invalid relocation type";
- }
-}
-
-template <>
-void Rebaser<ppc>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
- pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
- P::setP(*addr, P::getP(*addr) + fSlide);
- }
- }
- else {
- macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) {
- sreloc->set_r_value( sreloc->r_value() + fSlide );
- }
- else {
- throw "cannot rebase final linked image with scattered relocations";
- }
- }
-}
-
-template <>
-void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
- pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
- P::setP(*addr, P::getP(*addr) + fSlide);
- }
- }
- else {
- macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) {
- sreloc->set_r_value( sreloc->r_value() + fSlide );
- }
- else {
- throw "cannot rebase final linked image with scattered relocations";
- }
- }
-}
-
-template <>
-void Rebaser<arm>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- if ( reloc->r_type() == ARM_RELOC_VANILLA ) {
- pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
- P::setP(*addr, P::getP(*addr) + fSlide);
- }
- }
- else {
- macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- if ( sreloc->r_type() == ARM_RELOC_PB_LA_PTR ) {
- sreloc->set_r_value( sreloc->r_value() + fSlide );
- }
- else {
- throw "cannot rebase final linked image with scattered relocations";
- }
- }
-}
-
-template <typename A>
-void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
- pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress);
- P::setP(*addr, P::getP(*addr) + fSlide);
- }
- }
- else {
- throw "cannot rebase final linked image with scattered relocations";
- }
-}
-
-
-template <typename A>
-void Rebaser<A>::setRelocBase()
-{
- // reloc addresses are from the start of the mapped file (base address)
- fRelocBase = (pint_t)fHeader;
- fOrignalVMRelocBaseAddress = this->getBaseAddress();
- //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress);
-}
-
-template <>
-void Rebaser<ppc64>::setRelocBase()
-{
- // reloc addresses either:
- // 1) from the base address if no writable segment is > 4GB from base address
- // 2) from start of first writable segment
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
- if ( segCmd->initprot() & VM_PROT_WRITE ) {
- if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) {
- // found writable segment with address > 4GB past base address
- fRelocBase = segCmd->fileoff() + (pint_t)fHeader;
- fOrignalVMRelocBaseAddress = segCmd->vmaddr();
- return;
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- // just use base address
- fRelocBase = (pint_t)fHeader;
- fOrignalVMRelocBaseAddress = this->getBaseAddress();
-}
-
-template <>
-void Rebaser<x86_64>::setRelocBase()
-{
- // reloc addresses are always based from the start of the first writable segment
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
- if ( segCmd->initprot() & VM_PROT_WRITE ) {
- fRelocBase = segCmd->fileoff() + (pint_t)fHeader;
- fOrignalVMRelocBaseAddress = segCmd->vmaddr();
- return;
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- throw "no writable segment";
-}
-
-
-static void copyFile(const char* srcFile, const char* dstFile)
-{
- // open files
- int src = open(srcFile, O_RDONLY);
- if ( src == -1 )
- throwf("can't open file %s, errno=%d", srcFile, errno);
- struct stat stat_buf;
- if ( fstat(src, &stat_buf) == -1)
- throwf("can't stat open file %s, errno=%d", srcFile, errno);
-
- // create new file with all same permissions to hold copy of dylib
- ::unlink(dstFile);
- int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode);
- if ( dst == -1 )
- throwf("can't create temp file %s, errnor=%d", dstFile, errno);
-
- // mark source as "don't cache"
- (void)fcntl(src, F_NOCACHE, 1);
- // we want to cache the dst because we are about to map it in and modify it
-
- // copy permission bits
- if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 )
- throwf("can't chmod temp file %s, errno=%d", dstFile, errno);
- if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1)
- throwf("can't chown temp file %s, errno=%d", dstFile, errno);
-
- // copy contents
- ssize_t len;
- const uint32_t kBufferSize = 128*1024;
- static uint8_t* buffer = NULL;
- if ( buffer == NULL ) {
- vm_address_t addr = 0;
- if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS )
- buffer = (uint8_t*)addr;
- else
- throw "can't allcoate copy buffer";
- }
- while ( (len = read(src, buffer, kBufferSize)) > 0 ) {
- if ( write(dst, buffer, len) == -1 )
- throwf("write failure copying feil %s, errno=%d", dstFile, errno);
- }
-
- // close files
- int result1 = close(dst);
- int result2 = close(src);
- if ( (result1 != 0) || (result2 != 0) )
- throw "can't close file";
-}
-
-
-// scan dylibs and collect size info
-// calculate new base address for each dylib
-// rebase each file
-// copy to temp and mmap
-// update content
-// unmap/flush
-// rename
-
-struct archInfo {
- cpu_type_t arch;
- uint64_t vmSize;
- uint64_t orgBase;
- uint64_t newBase;
-};
-
-struct fileInfo
-{
- fileInfo(const char* p) : path(p) {}
-
- const char* path;
- std::vector<archInfo> archs;
-};
-
-//
-// add archInfos to fileInfo for every slice of a fat file
-// for ppc, there may be duplicate architectures (with different sub-types)
-//
-static void setSizes(fileInfo& info, const std::set<cpu_type_t>& onlyArchs)
-{
- const MultiArchRebaser mar(info.path);
- const std::vector<AbstractRebaser*>& rebasers = mar.getArchs();
- for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
- for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) {
- AbstractRebaser* rebaser = *rit;
- if ( rebaser->getArchitecture() == *ait ) {
- archInfo ai;
- ai.arch = *ait;
- ai.vmSize = rebaser->getVMSize();
- ai.orgBase = rebaser->getBaseAddress();
- ai.newBase = 0;
- //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize);
- info.archs.push_back(ai);
- }
- }
- }
-}
-
-static const char* nameForArch(cpu_type_t arch)
-{
- switch( arch ) {
- case CPU_TYPE_POWERPC:
- return "ppc";
- case CPU_TYPE_POWERPC64:
- return "ppca64";
- case CPU_TYPE_I386:
- return "i386";
- case CPU_TYPE_X86_64:
- return "x86_64";
- case CPU_TYPE_ARM:
- return "arm";
- }
- return "unknown";
-}
-
-static void rebase(const fileInfo& info)
-{
- // generate temp file name
- char realFilePath[PATH_MAX];
- if ( realpath(info.path, realFilePath) == NULL ) {
- throwf("realpath() failed on %s, errno=%d", info.path, errno);
- }
- const char* tempPath;
- asprintf((char**)&tempPath, "%s_rebase", realFilePath);
-
- // copy whole file to temp file
- copyFile(info.path, tempPath);
-
- try {
- // rebase temp file
- MultiArchRebaser mar(tempPath, true);
- const std::vector<AbstractRebaser*>& rebasers = mar.getArchs();
- for(std::vector<archInfo>::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) {
- for(std::vector<AbstractRebaser*>::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) {
- if ( (*rit)->getArchitecture() == fait->arch ) {
- (*rit)->setBaseAddress(fait->newBase);
- if ( verbose )
- printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path);
- }
- }
- }
-
- // flush temp file out to disk
- mar.commit();
-
- // rename
- int result = rename(tempPath, info.path);
- if ( result != 0 ) {
- throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno);
- }
-
- // make sure every really gets out to disk
- ::sync();
- }
- catch (const char* msg) {
- // delete temp file
- ::unlink(tempPath);
-
- // throw exception with file name added
- const char* newMsg;
- asprintf((char**)&newMsg, "%s for file %s", msg, info.path);
- throw newMsg;
- }
-}
-
-static uint64_t totalVMSize(cpu_type_t arch, std::vector<fileInfo>& files)
-{
- uint64_t totalSize = 0;
- for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
- fileInfo& fi = *fit;
- for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) {
- if ( fait->arch == arch )
- totalSize += fait->vmSize;
- }
- }
- return totalSize;
-}
-
-static uint64_t startAddress(cpu_type_t arch, std::vector<fileInfo>& files, uint64_t lowAddress, uint64_t highAddress)
-{
- if ( lowAddress != 0 )
- return lowAddress;
- else if ( highAddress != 0 ) {
- uint64_t totalSize = totalVMSize(arch, files);
- if ( highAddress < totalSize )
- throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize);
- return highAddress - totalSize;
- }
- else {
- if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) {
- // place dylibs below dyld
- uint64_t topAddr = 0x8FE00000;
- uint64_t totalSize = totalVMSize(arch, files);
- if ( totalSize > topAddr )
- throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize);
- return topAddr - totalSize;
- }
- else if ( arch == CPU_TYPE_POWERPC64 ) {
- return 0x200000000ULL;
- }
- else if ( arch == CPU_TYPE_X86_64 ) {
- return 0x200000000ULL;
- }
- else if ( arch == CPU_TYPE_ARM ) {
- // place dylibs below dyld
- uint64_t topAddr = 0x2FE00000;
- uint64_t totalSize = totalVMSize(arch, files);
- if ( totalSize > topAddr )
- throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize);
- return topAddr - totalSize;
- }
- else
- throw "unknown architecture";
- }
-}
-
-static void usage()
-{
- fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch <arch>] files...\n");
-}
-
-
-int main(int argc, const char* argv[])
-{
- std::vector<fileInfo> files;
- std::set<cpu_type_t> onlyArchs;
- uint64_t lowAddress = 0;
- uint64_t highAddress = 0;
-
- try {
- // parse command line options
- char* endptr;
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-v") == 0 ) {
- verbose = true;
- }
- else if ( strcmp(arg, "-low_address") == 0 ) {
- lowAddress = strtoull(argv[++i], &endptr, 16);
- }
- else if ( strcmp(arg, "-high_address") == 0 ) {
- highAddress = strtoull(argv[++i], &endptr, 16);
- }
- else if ( strcmp(arg, "-arch") == 0 ) {
- const char* arch = argv[++i];
- if ( strcmp(arch, "ppc") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC);
- else if ( strcmp(arch, "ppc64") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC64);
- else if ( strcmp(arch, "i386") == 0 )
- onlyArchs.insert(CPU_TYPE_I386);
- else if ( strcmp(arch, "x86_64") == 0 )
- onlyArchs.insert(CPU_TYPE_X86_64);
- else if ( strcmp(arch, "arm") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- else if ( strcmp(arch, "armv6") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- else
- throwf("unknown architecture %s", arch);
- }
- else {
- usage();
- throwf("unknown option: %s\n", arg);
- }
- }
- else {
- files.push_back(fileInfo(arg));
- }
- }
-
- if ( files.size() == 0 )
- throw "no files specified";
-
- // use all architectures if no restrictions specified
- if ( onlyArchs.size() == 0 ) {
- onlyArchs.insert(CPU_TYPE_POWERPC);
- onlyArchs.insert(CPU_TYPE_POWERPC64);
- onlyArchs.insert(CPU_TYPE_I386);
- onlyArchs.insert(CPU_TYPE_X86_64);
- onlyArchs.insert(CPU_TYPE_ARM);
- }
-
- // scan files and collect sizes
- for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) {
- setSizes(*it, onlyArchs);
- }
-
- // assign new base address for each arch
- for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
- cpu_type_t arch = *ait;
- uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress);
- for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
- fileInfo& fi = *fit;
- for(std::vector<archInfo>::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) {
- if ( fait->arch == arch ) {
- fait->newBase = baseAddress;
- baseAddress += fait->vmSize;
- baseAddress = (baseAddress + 4095) & (-4096); // page align
- }
- }
- }
- }
-
- // rebase each file if it contains something rebaseable
- for(std::vector<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) {
- fileInfo& fi = *it;
- if ( fi.archs.size() > 0 )
- rebase(fi);
- }
-
- }
- catch (const char* msg) {
- fprintf(stderr, "rebase failed: %s\n", msg);
- return 1;
- }
-
- return 0;
-}
-
-
-