/* * Copyright (c) 2005 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@ */ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- */ namespace ObjectFileMachO { class Reference : public ObjectFile::Reference { public: Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget); virtual ~Reference(); virtual bool isUnbound() const; virtual bool isWeakReference() const; virtual bool requiresRuntimeFixUp() const; virtual bool isLazyReference() const; virtual Kind getKind() const; virtual uint64_t getFixUpOffset() const; virtual const char* getTargetName() const; virtual ObjectFile::Atom& getTarget() const; virtual uint64_t getTargetOffset() const; virtual ObjectFile::Atom& getFromTarget() const; virtual const char* getFromTargetName() const; virtual void setTarget(ObjectFile::Atom&); virtual void setFromTarget(ObjectFile::Atom&); virtual void setFromTargetName(const char*); virtual const char* getDescription() const; virtual uint64_t getFromTargetOffset() const; void setLazy(bool); void setWeak(bool); private: ObjectFile::Atom* fTarget; ObjectFile::Atom* fFromTarget; const char* fTargetName; const char* fFromTargetName; macho_uintptr_t fTargetOffset; macho_uintptr_t fFromTargetOffset; macho_uintptr_t fFixUpOffsetInSrc; Kind fKind; bool fLazy; bool fWeak; }; class Reader : public ObjectFile::Reader { public: Reader(const char* path); Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); virtual ~Reader(); virtual const char* getPath(); virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); virtual std::vector* getStabsDebugInfo(); // stabs info not associated with an atom private: friend class Atom; void init(const macho_header* header, const char* path); void buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& offsets, std::set& dontUse); void addRelocReference(const macho_section* sect, const macho_relocation_info* reloc); Atom* findAtomCoveringOffset(uint32_t offset); uint32_t findAtomIndex(const Atom& atom); void addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr); class Segment* makeSegmentFromSection(const macho_section*); macho_uintptr_t commonsOffset(); void insertOffsetIfText(std::set& offsets, uint32_t value); void insertOffsetIfNotText(std::set& offsets, uint32_t value); const macho_section* findSectionCoveringOffset(uint32_t offset); void addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom); void deadStub(Atom& target); const char* fPath; const ObjectFile::ReaderOptions& fOptions; const macho_header* fHeader; const char* fStrings; const macho_nlist* fSymbols; uint32_t fSymbolCount; const macho_segment_command* fSegment; const uint32_t* fIndirectTable; std::vector fAtoms; std::vector fSegments; std::set fDeadAtoms; uint32_t fNonAtomStabsStartIndex; uint32_t fNonAtomStabsCount; std::vector fNonAtomExtras; }; class Segment : public ObjectFile::Segment { public: virtual const char* getName() const; virtual bool isContentReadable() const; virtual bool isContentWritable() const; virtual bool isContentExecutable() const; protected: Segment(const macho_section*); friend class Reader; private: const macho_section* fSection; }; class Atom : public ObjectFile::Atom { public: virtual ObjectFile::Reader* getFile() const; virtual const char* getName() const; virtual const char* getDisplayName() const; virtual Scope getScope() const; virtual bool isTentativeDefinition() const; virtual bool isWeakDefinition() const; virtual bool isCoalesableByName() const; virtual bool isCoalesableByValue() const; virtual bool isZeroFill() const; virtual bool dontDeadStrip() const; virtual bool dontStripName() const; // referenced dynamically virtual bool isImportProxy() const; virtual uint64_t getSize() const; virtual std::vector& getReferences() const; virtual bool mustRemainInSection() const; virtual const char* getSectionName() const; virtual Segment& getSegment() const; virtual bool requiresFollowOnAtom() const; virtual ObjectFile::Atom& getFollowOnAtom() const; virtual std::vector* getStabsDebugInfo() const; virtual uint8_t getAlignment() const; virtual WeakImportSetting getImportWeakness() const { return ObjectFile::Atom::kWeakUnset; } virtual void copyRawContent(uint8_t buffer[]) const; virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; virtual void setScope(Scope); virtual void setImportWeakness(bool weakImport) { } bool isLazyStub(); protected: friend class Reader; Atom(Reader&, const macho_nlist*); Atom(Reader&, uint32_t offset); virtual ~Atom(); const macho_section* findSectionFromOffset(macho_uintptr_t offset); const macho_section* getCommonsSection(); void setSize(macho_uintptr_t); void setFollowOnAtom(Atom&); static bool atomCompare(const Atom* lhs, const Atom* rhs); Reference* addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); Reference* addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); Reference* addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget); Reference* addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); Reader& fOwner; const macho_nlist* fSymbol; macho_uintptr_t fOffset; macho_uintptr_t fSize; const macho_section* fSection; Segment* fSegment; const char* fSynthesizedName; std::vector fReferences; ObjectFile::Atom::Scope fScope; uint32_t fStabsStartIndex; uint32_t fStabsCount; static macho_section fgCommonsSection; // for us by tentative definitions }; Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) { return new Reader(mh, path, options); } Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) : fPath(NULL), fOptions(options), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL) { init(header, path); } Reader::Reader(const char* path) : fPath(NULL), fOptions(*(new ObjectFile::ReaderOptions())), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fNonAtomStabsStartIndex(0), fNonAtomStabsCount(0) { struct stat stat_buf; int fd = ::open(path, O_RDONLY, 0); ::fstat(fd, &stat_buf); void* p = ::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); ::close(fd); if ( ((macho_header*)p)->magic() == MH_MAGIC ) { init((macho_header*)p, path); return; } throw "add fat handling"; } Reader::~Reader() { } bool Atom::atomCompare(const Atom* lhs, const Atom* rhs) { return lhs->fOffset < rhs->fOffset; } void Reader::init(const macho_header* header, const char* path) { // sanity check #if defined(ARCH_PPC) if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_POWERPC) ) throw "not a valid ppc mach-o file"; #elif defined(ARCH_I386) if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_I386) ) throw "not a valid i386 mach-o file"; #elif defined(ARCH_PPC64) if ( (header->magic() != MH_MAGIC_64) || (header->cputype() != CPU_TYPE_POWERPC64) ) throw "not a valid ppc64 mach-o file"; #endif // cache intersting pointers fPath = strdup(path); fHeader = header; const uint32_t cmd_count = header->ncmds(); const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); const macho_load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SYMTAB: { const macho_symtab_command* symtab = (macho_symtab_command*)cmd; fSymbolCount = symtab->nsyms(); fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); fStrings = (char*)header + symtab->stroff(); } break; case LC_DYSYMTAB: { const macho_dysymtab_command* dsymtab = (struct macho_dysymtab_command*)cmd; fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); } break; default: if ( cmd->cmd() == macho_segment_command::command ) { fSegment= (macho_segment_command*)cmd; } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } // add all atoms that have entries in symbol table std::set symbolAtomOffsets; for (uint32_t i=0; i < fSymbolCount; ++i) { const macho_nlist& sym = fSymbols[i]; if ( (sym.n_type() & N_STAB) == 0 ) { uint8_t type = (sym.n_type() & N_TYPE); if ( (type == N_SECT) || ((type == N_UNDF) && (sym.n_value() != 0)) ) { // real definition or "tentative definition" Atom* newAtom = new Atom(*this, &sym); fAtoms.push_back(newAtom); symbolAtomOffsets.insert(newAtom->fOffset); } } } // add all points referenced in relocations const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; std::set cleavePoints; std::set dontCleavePoints; for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); const uint32_t relocCount = sect->nreloc(); for (uint32_t r = 0; r < relocCount; ++r) { buildOffsetsSet(&relocs[r], sect, cleavePoints, dontCleavePoints); } } // add all stub functions and (non)lazy pointers std::set deadStubOffsets; for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { uint8_t type (sect->flags() & SECTION_TYPE); switch ( type ) { case S_SYMBOL_STUBS: { const uint32_t stubSize = sect->reserved2(); // TVector glue sections are marked as S_SYMBOL_STUBS but are only 8 bytes if ( stubSize > 8 ) { for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += stubSize) { uint32_t stubAddr = sect->addr() + sectOffset; if ( cleavePoints.count(stubAddr) == 0 ) { cleavePoints.insert(stubAddr); deadStubOffsets.insert(stubAddr); } } } } break; case S_NON_LAZY_SYMBOL_POINTERS: case S_LAZY_SYMBOL_POINTERS: for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += sizeof(macho_uintptr_t)) { uint32_t pointerAddr = sect->addr() + sectOffset; cleavePoints.insert(pointerAddr); } break; } // also make sure each section break is a cleave point if ( sect->size() != 0 ) cleavePoints.insert(sect->addr()); } for (std::set::iterator it=cleavePoints.begin(); it != cleavePoints.end(); it++) { uint32_t cleavePoint = *it; //printf("cleave offset 0x%08X\n", cleavePoint); // only create an atom if it is not a don't-cleave point and there is not already an atom at this offset if ( (dontCleavePoints.count(cleavePoint) == 0) && (symbolAtomOffsets.count(cleavePoint) == 0) ) fAtoms.push_back(new Atom(*this, cleavePoint)); } const uint32_t atomCount = fAtoms.size(); if ( atomCount > 0 ) { // sort the atoms so the occur in source file order std::sort(fAtoms.begin(), fAtoms.end(), Atom::atomCompare); // tell each atom its size and follow on const bool dontDeadStrip = ((fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0); Atom* lastAtom = fAtoms[0]; for (uint32_t i=1; i < atomCount; ++i) { Atom* thisAtom = fAtoms[i]; if ( lastAtom->getSize() == 0 ) { if ( lastAtom->fSection == thisAtom->fSection ) lastAtom->setSize(thisAtom->fOffset - lastAtom->fOffset); else lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); } if ( dontDeadStrip ) lastAtom->setFollowOnAtom(*thisAtom); lastAtom = thisAtom; } lastAtom = fAtoms[atomCount-1]; if ( lastAtom->getSize() == 0 ) lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); // add relocation based references for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); const uint32_t relocCount = sect->nreloc(); for (uint32_t r = 0; r < relocCount; ++r) { addRelocReference(sect, &relocs[r]); } } // add dead stubs to list to delete for (std::set::iterator it=deadStubOffsets.begin(); it != deadStubOffsets.end(); it++) { Atom* deadStub = findAtomCoveringOffset(*it); this->deadStub(*deadStub); } // remove dead stubs and lazy pointers for (std::set::iterator deadIt=fDeadAtoms.begin(); deadIt != fDeadAtoms.end(); deadIt++) { for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { if ( *deadIt == *it ) { fAtoms.erase(it); break; } } } } // process stabs debugging info if ( ! fOptions.fStripDebugInfo ) { // scan symbol table for stabs entries fNonAtomStabsStartIndex = 0xFFFFFFFF; fNonAtomStabsCount = 0; uint32_t possibleStart = 0; Atom* atom = NULL; const uint32_t atomCount = fAtoms.size(); enum { start, inBeginEnd, foundFirst, inFunction } state = start; for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { const macho_nlist* sym = &fSymbols[symbolIndex]; uint8_t type = sym->n_type(); if ( (type & N_STAB) != 0 ) { if ( fNonAtomStabsStartIndex == 0xFFFFFFFF ) fNonAtomStabsStartIndex = symbolIndex; switch (state) { case start: if ( (type == N_SLINE) || (type == N_SOL) ) { possibleStart = symbolIndex; state = foundFirst; } else if ( type == N_BNSYM ) { macho_uintptr_t targetAddr = sym->n_value(); atom = this->findAtomCoveringOffset(targetAddr); if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { atom->fStabsStartIndex = symbolIndex; if ( fNonAtomStabsCount == 0 ) fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; } else { fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); atom = NULL; } state = inBeginEnd; } else if ( (type == N_STSYM) || (type == N_LCSYM) ) { macho_uintptr_t targetAddr = sym->n_value(); atom = this->findAtomCoveringOffset(targetAddr); if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { atom->fStabsStartIndex = symbolIndex; atom->fStabsCount = 1; if ( fNonAtomStabsCount == 0 ) fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; } else { fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); atom = NULL; } } else if ( type == N_GSYM ) { // n_value field is NOT atom address ;-( // need to find atom by name match const char* symString = &fStrings[sym->n_strx()]; const char* colon = strchr(symString, ':'); bool found = false; if ( colon != NULL ) { int nameLen = colon - symString; for (uint32_t searchIndex = 0; searchIndex < atomCount; ++searchIndex) { const char* atomName = fAtoms[searchIndex]->getName(); if ( (atomName != NULL) && (strncmp(&atomName[1], symString, nameLen) == 0) ) { atom = fAtoms[searchIndex]; atom->fStabsStartIndex = symbolIndex; atom->fStabsCount = 1; if ( fNonAtomStabsCount == 0 ) fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; found = true; break; } } } if ( !found ) { fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); atom = NULL; } } else if ( type == N_LSYM ) { if ( fNonAtomStabsCount != 0 ) { // built with -gfull and some type definition not at start of source fNonAtomExtras.push_back(symbolIndex); } } break; case inBeginEnd: if ( type == N_ENSYM ) { state = start; if ( atom != NULL ) atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; } break; case foundFirst: if ( (type == N_FUN) && (sym->n_sect() != 0) ) { state = inFunction; macho_uintptr_t targetAddr = sym->n_value(); atom = this->findAtomCoveringOffset(targetAddr); if ( (atom == NULL) || (atom->fOffset != targetAddr) ) { fprintf(stderr, "can't find atom for stabs FUN index: %d at 0x%08llX in %s\n", symbolIndex, (uint64_t)targetAddr, path); atom = NULL; } else { atom->fStabsStartIndex = possibleStart; if ( fNonAtomStabsCount == 0 ) fNonAtomStabsCount = possibleStart - fNonAtomStabsStartIndex; } } else if ( (type == N_FUN) && (sym->n_sect() == 0) ) { fprintf(stderr, "end stab FUN found without start FUN, index=%d in %s\n", symbolIndex, path); state = start; } break; case inFunction: if ( (type == N_FUN) && (sym->n_sect() == 0) ) { state = start; if ( atom != NULL ) atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; } break; } } } } } void Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) { uint32_t srcAddr; uint32_t dstAddr; Atom* src; Atom* dst; #if defined(ARCH_PPC) || defined(ARCH_PPC64) uint32_t instruction; #endif uint32_t* fixUpPtr; if ( (reloc->r_address() & R_SCATTERED) == 0 ) { fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); #if defined(ARCH_PPC) || defined(ARCH_PPC64) const macho_relocation_info* nextReloc = &reloc[1]; #endif switch ( reloc->r_type() ) { #if defined(ARCH_PPC) || defined(ARCH_PPC64) case PPC_RELOC_BR24: { if ( reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); int32_t displacement = (instruction & 0x03FFFFFC); if ( (displacement & 0x02000000) != 0 ) displacement |= 0xFC000000; uint32_t offsetInTarget = sect->addr() + reloc->r_address() + displacement; srcAddr = sect->addr() + reloc->r_address(); src = findAtomCoveringOffset(srcAddr); const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch24, targetName, offsetInTarget, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); if ( (instruction & 0x4C000000) == 0x48000000 ) { int32_t displacement = (instruction & 0x03FFFFFC); if ( (displacement & 0x02000000) != 0 ) displacement |= 0xFC000000; srcAddr = sect->addr() + reloc->r_address(); dstAddr = srcAddr + displacement; src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, dstAddr - dst->fOffset); } } } break; case PPC_RELOC_BR14: { if ( reloc->r_extern() ) { srcAddr = sect->addr() + reloc->r_address(); src = findAtomCoveringOffset(srcAddr); const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch14, targetName, 0, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); int32_t displacement = (instruction & 0x0000FFFC); if ( (displacement & 0x00008000) != 0 ) displacement |= 0xFFFF0000; srcAddr = sect->addr() + reloc->r_address(); dstAddr = srcAddr + displacement; src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch14, *dst, 0, dstAddr - dst->fOffset); } } break; case PPC_RELOC_PAIR: // skip, processed by a previous look ahead break; case PPC_RELOC_LO16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_LO16 missing following pair\n"); break; } srcAddr = sect->addr() + reloc->r_address(); if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; src = findAtomCoveringOffset(srcAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, targetName, dstAddr, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (instruction & 0xFFFF); dstAddr = (nextReloc->r_address() << 16) + (int32_t)lowBits; if ( (lowBits & 0x8000) != 0 ) dstAddr += 0x10000; addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow16, 0); } } break; case PPC_RELOC_LO14: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_LO14 missing following pair\n"); break; } srcAddr = sect->addr() + reloc->r_address(); if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; src = findAtomCoveringOffset(srcAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, targetName, dstAddr, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow14, 0); } } break; case PPC_RELOC_HI16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_HI16 missing following pair\n"); break; } srcAddr = sect->addr() + reloc->r_address(); if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; src = findAtomCoveringOffset(srcAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16, targetName, dstAddr, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16, 0); } } break; case PPC_RELOC_HA16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_HA16 missing following pair\n"); break; } srcAddr = sect->addr() + reloc->r_address(); if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; const char* targetName = &fStrings[targetSymbol->n_strx()]; instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; src = findAtomCoveringOffset(srcAddr); src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, targetName, dstAddr, 0); } else { instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16AddLow, 0); } } break; case GENERIC_RELOC_VANILLA: { srcAddr = sect->addr() + reloc->r_address(); Atom* srcAtom = findAtomCoveringOffset(srcAddr); // lazy pointers have references to dyld_stub_binding_helper which need to be ignored if ( (srcAtom->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { uint32_t offsetInSrcAtom = srcAddr - srcAtom->fOffset; macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; uint8_t type = targetSymbol->n_type() & N_TYPE; if ( type == N_UNDF ) { const char* targetName = &fStrings[targetSymbol->n_strx()]; macho_uintptr_t addend = pointerValue; srcAtom->addByNameReference(offsetInSrcAtom, Reference::pointer, targetName, addend, 0); } else { dstAddr = targetSymbol->n_value(); Atom* dstAtom = findAtomCoveringOffset(dstAddr); macho_uintptr_t addend = pointerValue; srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, addend, 0); } } else { Atom* dstAtom = findAtomCoveringOffset(pointerValue); srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, pointerValue-dstAtom->fOffset, 0); } } } break; case PPC_RELOC_JBSR: // ignore for now break; #endif #if defined(ARCH_I386) case GENERIC_RELOC_VANILLA: { srcAddr = sect->addr() + reloc->r_address(); src = findAtomCoveringOffset(srcAddr); // lazy pointers have references to dyld_stub_binding_helper which need to be ignored if ( (src->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { if ( reloc->r_length() != 2 ) throw "bad vanilla relocation length"; Reference::Kind kind; macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); if ( reloc->r_pcrel() ) { kind = Reference::x86FixupBranch32; pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); } else { kind = Reference::pointer; } uint32_t offsetInSrcAtom = srcAddr - src->fOffset; if ( reloc->r_extern() ) { const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; uint8_t type = targetSymbol->n_type() & N_TYPE; if ( type == N_UNDF ) { const char* targetName = &fStrings[targetSymbol->n_strx()]; macho_uintptr_t addend = pointerValue; src->addByNameReference(offsetInSrcAtom, kind, targetName, addend, 0); } else { dstAddr = targetSymbol->n_value(); dst = findAtomCoveringOffset(dstAddr); macho_uintptr_t addend = pointerValue - dstAddr; src->addReference(offsetInSrcAtom, kind, *dst, addend, 0); } } else { dst = findAtomCoveringOffset(pointerValue); src->addReference(offsetInSrcAtom, kind, *dst, 0, 0); } } } break; #endif default: printf("unknown relocation type %d\n", reloc->r_type()); } } else { const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; srcAddr = sect->addr() + sreloc->r_address(); dstAddr = sreloc->r_value(); fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); const macho_scattered_relocation_info* nextSReloc = &sreloc[1]; const macho_relocation_info* nextReloc = &reloc[1]; // file format allows pair to be scattered or not bool nextRelocIsPair = false; uint32_t nextRelocAddress = 0; uint32_t nextRelocValue = 0; if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { nextRelocIsPair = true; nextRelocAddress = nextReloc->r_address(); } } else { if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { nextRelocIsPair = true; nextRelocAddress = nextSReloc->r_address(); nextRelocValue = nextSReloc->r_value(); } } switch (sreloc->r_type()) { case GENERIC_RELOC_VANILLA: { macho_uintptr_t betterDstAddr = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) Atom* src = findAtomCoveringOffset(srcAddr); Atom* dst = findAtomCoveringOffset(dstAddr); src->addReference(srcAddr - src->fOffset, Reference::pointer, *dst, betterDstAddr - dst->fOffset, 0); } break; #if defined(ARCH_PPC) || defined(ARCH_PPC64) case PPC_RELOC_BR24: { instruction = OSSwapBigToHostInt32(*fixUpPtr); if ( (instruction & 0x4C000000) == 0x48000000 ) { int32_t displacement = (instruction & 0x03FFFFFC); if ( (displacement & 0x02000000) != 0 ) displacement |= 0xFC000000; srcAddr = sect->addr() + sreloc->r_address(); dstAddr = sreloc->r_value(); src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, srcAddr + displacement - sreloc->r_value()); } } break; case PPC_RELOC_LO16_SECTDIFF: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_LO16_SECTDIFF missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (instruction & 0xFFFF); int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; if ( (lowBits & 0x8000) != 0 ) displacement += 0x10000; uint32_t picBaseOffset = nextRelocValue - src->fOffset; int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow16, *dst, dstOffset, picBaseOffset); } break; case PPC_RELOC_LO14_SECTDIFF: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (instruction & 0xFFFC); int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; if ( (lowBits & 0x8000) != 0 ) displacement += 0x10000; uint32_t picBaseOffset = nextRelocValue - src->fOffset; int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow14, *dst, dstOffset, picBaseOffset); } break; case PPC_RELOC_HA16_SECTDIFF: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (nextRelocAddress & 0x0000FFFF); int32_t displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; uint32_t picBaseOffset = nextRelocValue - src->fOffset; int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseHigh16, *dst, dstOffset, picBaseOffset); } break; case PPC_RELOC_LO14: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_LO14 missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (instruction & 0xFFFC); uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; if ( (lowBits & 0x8000) != 0 ) betterDstAddr += 0x10000; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, *dst, betterDstAddr - dst->fOffset, 0); } break; case PPC_RELOC_LO16: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_LO16 missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (instruction & 0xFFFF); uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; if ( (lowBits & 0x8000) != 0 ) betterDstAddr += 0x10000; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, *dst, betterDstAddr - dst->fOffset, 0); } break; case PPC_RELOC_HA16: { if ( ! nextRelocIsPair) { printf("PPC_RELOC_HA16 missing following PAIR\n"); break; } src = findAtomCoveringOffset(srcAddr); dst = findAtomCoveringOffset(dstAddr); instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (nextRelocAddress & 0xFFFF); uint32_t betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, *dst, betterDstAddr - dst->fOffset, 0); } break; case PPC_RELOC_SECTDIFF: case PPC_RELOC_LOCAL_SECTDIFF: { const macho_scattered_relocation_info* nextReloc = &sreloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_SECTDIFF missing following pair\n"); break; } srcAddr = sect->addr() + sreloc->r_address(); uint32_t toAddr = sreloc->r_value(); uint32_t fromAddr = nextReloc->r_value(); src = findAtomCoveringOffset(srcAddr); Atom* to = findAtomCoveringOffset(toAddr); Atom* from = findAtomCoveringOffset(fromAddr); //macho_intptr_t pointerValue = *(macho_intptr_t*)fixUpPtr; //uint64_t toOffset = to->fOffset; //uint64_t fromOffset = from->fOffset; //int64_t pointerValue64 = pointerValue; //uint64_t addend = pointerValue64 - (toOffset - fromOffset); Reference::Kind kind = Reference::pointer32Difference; if ( sreloc->r_length() == 3 ) kind = Reference::pointer64Difference; src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); } break; case PPC_RELOC_PAIR: break; case PPC_RELOC_HI16_SECTDIFF: printf("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF\n"); break; #endif #if defined(ARCH_I386) case GENERIC_RELOC_SECTDIFF: case GENERIC_RELOC_LOCAL_SECTDIFF: { if ( nextSReloc->r_type() != GENERIC_RELOC_PAIR ) { printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); break; } srcAddr = sect->addr() + sreloc->r_address(); uint32_t toAddr = sreloc->r_value(); uint32_t fromAddr = nextSReloc->r_value(); src = findAtomCoveringOffset(srcAddr); Atom* to = findAtomCoveringOffset(toAddr); Atom* from = findAtomCoveringOffset(fromAddr); Reference::Kind kind = Reference::pointer32Difference; if ( sreloc->r_length() != 2 ) throw "bad length for GENERIC_RELOC_SECTDIFF"; src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); } break; case GENERIC_RELOC_PAIR: // do nothing, already used via a look ahead break; #endif default: printf("unknown scattered relocation type %d\n", sreloc->r_type()); } } } void Reader::addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr) { Atom* src = findAtomCoveringOffset(srcAddr); Atom* dst = findAtomCoveringOffset(dstAddr); src->addReference(srcAddr - src->fOffset, kind, *dst, dstAddr - dst->fOffset, picBaseAddr - src->fOffset); } Atom* Reader::findAtomCoveringOffset(uint32_t offset) { #if 1 // binary search of sorted atoms Atom** base = &fAtoms[0]; for (uint32_t n = fAtoms.size(); n > 0; n /= 2) { Atom** pivot = &base[n/2]; Atom* pivotAtom = *pivot; if ( pivotAtom->fOffset <= offset ) { if ( offset < (pivotAtom->fOffset + pivotAtom->fSize) ) return pivotAtom; // key > pivot // move base to symbol after pivot base = &pivot[1]; --n; } else { // key < pivot // keep same base } } #else const uint32_t atomCount = fAtoms.size(); for (uint32_t i=0; i < atomCount; ++i) { Atom* atom = fAtoms[i]; if ( (atom->fOffset <= offset) && (offset < (atom->fOffset + atom->fSize)) ) return atom; } #endif return NULL; } uint32_t Reader::findAtomIndex(const Atom& atom) { const Atom* target = &atom; const uint32_t atomCount = fAtoms.size(); for (uint32_t i=0; i < atomCount; ++i) { Atom* anAtom = fAtoms[i]; if ( anAtom == target ) return i; } return 0xffffffff; } static void insertOffset(std::set& offsets, uint32_t value) { //fprintf(stderr, "cleave point at 0x%08X\n", value); offsets.insert(value); } void Reader::insertOffsetIfNotText(std::set& offsets, uint32_t value) { const macho_section* sect = findSectionCoveringOffset(value); if ( (sect == NULL) || (strcmp(sect->segname(),"__TEXT") != 0) || (strncmp(sect->sectname(),"__text", 6) != 0) ) { offsets.insert(value); } } void Reader::insertOffsetIfText(std::set& offsets, uint32_t value) { const macho_section* sect = findSectionCoveringOffset(value); if ( (sect != NULL) && (strcmp(sect->segname(),"__TEXT") == 0) && (strncmp(sect->sectname(),"__text", 6) == 0) ) { //fprintf(stderr, "don't cleave point at 0x%08X\n", value); offsets.insert(value); } } const macho_section* Reader::findSectionCoveringOffset(uint32_t offset) { const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if ( (sect->addr() <= offset) && (offset < (sect->addr() + sect->size())) ) return sect; } return NULL; } void Reader::buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& cleavePoints, std::set& dontCleavePoints) { #if defined(ARCH_PPC) || defined(ARCH_PPC64) uint32_t targetAddr; #endif if ( (reloc->r_address() & R_SCATTERED) == 0 ) { uint32_t* fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); #if defined(ARCH_PPC) || defined(ARCH_PPC64) uint32_t instruction; #endif switch ( reloc->r_type() ) { #if defined(ARCH_PPC) || defined(ARCH_PPC64) case PPC_RELOC_BR14: // do nothing. local branch should not cleave break; case PPC_RELOC_BR24: { if ( ! reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); if ( (instruction & 0x4C000000) == 0x48000000 ) { int32_t displacement = (instruction & 0x03FFFFFC); if ( (displacement & 0x02000000) != 0 ) displacement |= 0xFC000000; //cleavePoints.insert(reloc->r_address() + displacement); insertOffset(cleavePoints, sect->addr() + reloc->r_address() + displacement); } } } break; case PPC_RELOC_PAIR: // skip, processed by a look ahead break; case PPC_RELOC_LO16: { const macho_relocation_info* nextReloc = &reloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_LO16 missing following pair\n"); break; } if ( ! reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); //cleavePoints.insert(targetAddr); insertOffset(cleavePoints, (targetAddr)); } } break; case PPC_RELOC_LO14: { const macho_relocation_info* nextReloc = &reloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_LO14 missing following pair\n"); break; } if ( ! reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); //cleavePoints.insert(targetAddr); insertOffset(cleavePoints, (targetAddr)); } } break; case PPC_RELOC_HI16: { const macho_relocation_info* nextReloc = &reloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_HI16 missing following pair\n"); break; } if ( ! reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); targetAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); //cleavePoints.insert(targetAddr); insertOffset(cleavePoints, targetAddr); } } break; case PPC_RELOC_HA16: { const macho_relocation_info* nextReloc = &reloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_HA16 missing following pair\n"); break; } if ( ! reloc->r_extern() ) { instruction = OSSwapBigToHostInt32(*fixUpPtr); int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); targetAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; //cleavePoints.insert(targetAddr); insertOffset(cleavePoints, targetAddr); } } break; case PPC_RELOC_JBSR: // ignore for now break; #endif case GENERIC_RELOC_VANILLA: { #if defined(ARCH_PPC64) if ( reloc->r_length() != 3 ) throw "vanilla pointer relocation found that is not 8-bytes"; #elif defined(ARCH_PPC) || defined(ARCH_I386) if ( reloc->r_length() != 2 ) throw "vanilla pointer relocation found that is not 4-bytes"; #endif //fprintf(stderr, "pcrel=%d, len=%d, extern=%d, type=%d\n", reloc->r_pcrel(), reloc->r_length(), reloc->r_extern(), reloc->r_type()); if ( !reloc->r_extern() ) { macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); if ( reloc->r_pcrel() ) pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); // a pointer into code does not cleave the code (gcc always pointers to labels) insertOffsetIfNotText(cleavePoints, pointerValue); } } break; default: printf("unknown relocation type %d\n", reloc->r_type()); } } else { const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; switch (sreloc->r_type()) { case GENERIC_RELOC_VANILLA: insertOffset(cleavePoints, sreloc->r_value()); break; #if defined(ARCH_PPC) || defined(ARCH_PPC64) case PPC_RELOC_BR24: insertOffset(cleavePoints, sreloc->r_value()); break; case PPC_RELOC_HA16: case PPC_RELOC_HI16: case PPC_RELOC_LO16: case PPC_RELOC_LO14: case PPC_RELOC_LO16_SECTDIFF: case PPC_RELOC_LO14_SECTDIFF: case PPC_RELOC_HA16_SECTDIFF: case PPC_RELOC_HI16_SECTDIFF: //cleavePoints.insert(sreloc->r_value()); insertOffset(cleavePoints, sreloc->r_value()); insertOffsetIfText(dontCleavePoints, sreloc->r_value()); break; case PPC_RELOC_SECTDIFF: case PPC_RELOC_LOCAL_SECTDIFF: // these do not cleave up a .o file // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave { const macho_scattered_relocation_info* nextReloc = &sreloc[1]; if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { printf("PPC_RELOC_SECTDIFF missing following pair\n"); break; } insertOffsetIfText(dontCleavePoints, sreloc->r_value()); insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); } break; case PPC_RELOC_PAIR: // do nothing, already used via a look ahead break; #endif #if defined(ARCH_I386) case GENERIC_RELOC_SECTDIFF: case GENERIC_RELOC_LOCAL_SECTDIFF: // these do not cleave up a .o file // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave { const macho_scattered_relocation_info* nextReloc = &sreloc[1]; if ( nextReloc->r_type() != GENERIC_RELOC_PAIR ) { printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); break; } insertOffsetIfText(dontCleavePoints, sreloc->r_value()); insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); } break; case GENERIC_RELOC_PAIR: // do nothing, already used via a look ahead break; #endif default: printf("unknown relocation type %d\n", sreloc->r_type()); } } } Segment* Reader::makeSegmentFromSection(const macho_section* sect) { // make segment object if one does not already exist const uint32_t segmentCount = fSegments.size(); for (uint32_t i=0; i < segmentCount; ++i) { Segment* seg = fSegments[i]; if ( strcmp(sect->segname(), seg->getName()) == 0 ) return seg; } Segment* seg = new Segment(sect); fSegments.push_back(seg); return seg; } macho_uintptr_t Reader::commonsOffset() { return fSegment->vmsize(); } const char* Reader::getPath() { return fPath; } std::vector& Reader::getAtoms() { return (std::vector&)(fAtoms); } std::vector* Reader::getJustInTimeAtomsFor(const char* name) { // object files have no just-in-time atoms return NULL; } std::vector* Reader::getStabsDebugInfo() { if ( fNonAtomStabsCount == 0 ) return NULL; std::vector* stabs = new std::vector(); stabs->reserve(fNonAtomStabsCount); for (uint32_t i=0; i < fNonAtomStabsCount; ++i) { const macho_nlist* sym = &fSymbols[fNonAtomStabsStartIndex+i]; if ( (sym->n_type() & N_STAB) != 0 ) { ObjectFile::StabsInfo stab; stab.atomOffset = sym->n_value(); stab.string = &fStrings[sym->n_strx()]; stab.type = sym->n_type(); stab.other = sym->n_sect(); stab.desc = sym->n_desc(); // for N_SO n_value is offset of first atom, but our gdb ignores this, so we omit that calculation if ( stab.type == N_SO ) stab.atomOffset = 0; stabs->push_back(stab); } } // add any extra N_LSYM's not at start of symbol table for (std::vector::iterator it=fNonAtomExtras.begin(); it != fNonAtomExtras.end(); ++it) { const macho_nlist* sym = &fSymbols[*it]; ObjectFile::StabsInfo stab; stab.atomOffset = sym->n_value(); stab.string = &fStrings[sym->n_strx()]; stab.type = sym->n_type(); stab.other = sym->n_sect(); stab.desc = sym->n_desc(); stabs->push_back(stab); } return stabs; } void Reader::deadStub(Atom& target) { // remove stub fDeadAtoms.insert(&target); // remove lazy pointer const int stubNameLen = strlen(target.fSynthesizedName); char lazyName[stubNameLen+8]; strcpy(lazyName, target.fSynthesizedName); strcpy(&lazyName[stubNameLen-5], "$lazy_ptr"); const uint32_t atomCount = fAtoms.size(); for (uint32_t i=1; i < atomCount; ++i) { Atom* atom = fAtoms[i]; if ( (atom->fSynthesizedName != NULL) && (strcmp(atom->fSynthesizedName, lazyName) == 0) ) { fDeadAtoms.insert(atom); break; } } } void Reader::addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom) { // the compiler some times produces stub to static functions and then calls the stubs // we need to skip the stub if a static function exists with the same name and remove the stub if ( target.isLazyStub() ) { const macho_section* section = target.fSection; uint32_t index = (target.fOffset - section->addr()) / section->reserved2(); uint32_t indirectTableIndex = section->reserved1() + index; uint32_t symbolIndex = ENDIAN_READ32(fIndirectTable[indirectTableIndex]); if ( (symbolIndex & INDIRECT_SYMBOL_LOCAL) == 0 ) { const macho_nlist* sym = &fSymbols[symbolIndex]; if ( (sym->n_value() != 0) && ((sym->n_type() & N_EXT) == 0) ) { Atom* betterTarget = this->findAtomCoveringOffset(sym->n_value()); if ( (betterTarget != NULL) && (betterTarget->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) { // use direct reference to static function src.addDirectReference(offsetInSrcAtom, kind, *betterTarget, offsetInTargetAtom, picBaseOffset); // remove stub and lazy pointer this->deadStub(target); return; } } } } // fall through to general case src.addReference(offsetInSrcAtom, kind, target, offsetInTargetAtom, picBaseOffset); } Atom::Atom(Reader& owner, const macho_nlist* symbol) : fOwner(owner), fSymbol(symbol), fOffset(0), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), fStabsStartIndex(0), fStabsCount(0) { uint8_t type = symbol->n_type(); if ( (type & N_EXT) == 0 ) fScope = ObjectFile::Atom::scopeTranslationUnit; else if ( (type & N_PEXT) != 0 ) fScope = ObjectFile::Atom::scopeLinkageUnit; else fScope = ObjectFile::Atom::scopeGlobal; if ( (type & N_TYPE) == N_SECT ) { // real definition const macho_section* sections = (macho_section*)((char*)fOwner.fSegment + sizeof(macho_segment_command)); fSection = §ions[fSymbol->n_sect()-1]; fSegment = owner.makeSegmentFromSection(fSection); fOffset = fSymbol->n_value(); uint8_t type = fSection->flags() & SECTION_TYPE; switch ( type ) { case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { // get target name out of indirect symbol table uint32_t index = (fOffset - fSection->addr()) / sizeof(macho_uintptr_t); index += fSection->reserved1(); uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); const char* name = &fOwner.fStrings[strOffset]; Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); if ( type == S_LAZY_SYMBOL_POINTERS ) { ref->setLazy(true); ref->setFromTargetName("dyld_stub_binding_helper"); } } break; } } else if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { // tentative definition fSize = symbol->n_value(); fSection = getCommonsSection(); fSegment = owner.makeSegmentFromSection(fSection); fOffset = owner.commonsOffset(); } else { printf("unknown symbol type: %d\n", type); } } Atom::Atom(Reader& owner, uint32_t offset) : fOwner(owner), fSymbol(NULL), fOffset(offset), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), fStabsStartIndex(0), fStabsCount(0) { fSection = findSectionFromOffset(offset); fScope = ObjectFile::Atom::scopeLinkageUnit; fSegment = owner.makeSegmentFromSection(fSection); uint8_t type = fSection->flags() & SECTION_TYPE; switch ( type ) { case S_SYMBOL_STUBS: { uint32_t index = (offset - fSection->addr()) / fSection->reserved2(); index += fSection->reserved1(); uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); const char* name = &fOwner.fStrings[strOffset]; char* str = new char[strlen(name)+8]; strcpy(str, name); strcat(str, "$stub"); fSynthesizedName = str; } break; case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { uint32_t index = (offset - fSection->addr()) / sizeof(macho_uintptr_t); index += fSection->reserved1(); uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); const char* name = &fOwner.fStrings[strOffset]; char* str = new char[strlen(name)+16]; strcpy(str, name); if ( type == S_LAZY_SYMBOL_POINTERS ) strcat(str, "$lazy_ptr"); else strcat(str, "$non_lazy_ptr"); fSynthesizedName = str; Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); if ( type == S_LAZY_SYMBOL_POINTERS ) { ref->setLazy(true); ref->setFromTargetName("dyld_stub_binding_helper"); } const macho_nlist* sym = &fOwner.fSymbols[symbolIndex]; if ( (sym->n_type() & N_TYPE) == N_UNDF ) { if ( (sym->n_desc() & N_WEAK_REF) != 0 ) ref->setWeak(true); } } break; } } Atom::~Atom() { } macho_section Atom::fgCommonsSection; bool Atom::isLazyStub() { return ( (fSection->flags() & SECTION_TYPE) == S_SYMBOL_STUBS); } const macho_section* Atom::getCommonsSection() { if ( strcmp(fgCommonsSection.sectname(), "__common") != 0 ) { fgCommonsSection.set_sectname("__common"); fgCommonsSection.set_segname("__DATA"); fgCommonsSection.set_flags(S_ZEROFILL); } return &fgCommonsSection; } ObjectFile::Reader* Atom::getFile() const { return &fOwner; } const char* Atom::getName() const { if ( fSymbol != NULL ) return &fOwner.fStrings[fSymbol->n_strx()]; else return fSynthesizedName; } const char* Atom::getDisplayName() const { if ( fSymbol != NULL ) return &fOwner.fStrings[fSymbol->n_strx()]; if ( fSynthesizedName != NULL ) return fSynthesizedName; static char temp[32]; sprintf(temp, "atom #%u", fOwner.findAtomIndex(*this)); return temp; } ObjectFile::Atom::Scope Atom::getScope() const { return fScope; } void Atom::setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } bool Atom::isWeakDefinition() const { if ( isTentativeDefinition() ) return true; if ( fSymbol != NULL ) return ( (fSymbol->n_desc() & N_WEAK_DEF) != 0 ); uint8_t type = fSection->flags() & SECTION_TYPE; switch ( type ) { case S_SYMBOL_STUBS: case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: return true; } return false; } bool Atom::isTentativeDefinition() const { return (fSection == &fgCommonsSection); } bool Atom::isCoalesableByName() const { uint8_t type = fSection->flags() & SECTION_TYPE; switch ( type ) { case S_SYMBOL_STUBS: case S_COALESCED: return true; }; if ( isTentativeDefinition() ) return true; return false; } bool Atom::isCoalesableByValue() const { uint8_t type = fSection->flags() & SECTION_TYPE; switch ( type ) { case S_CSTRING_LITERALS: case S_4BYTE_LITERALS: case S_8BYTE_LITERALS: return true; }; return false; } bool Atom::isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } bool Atom::dontDeadStrip() const { if ( fSymbol != NULL ) return ( (fSymbol->n_desc() & N_NO_DEAD_STRIP) != 0 ); return false; } bool Atom::dontStripName() const { if ( fSymbol != NULL ) return ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0 ); return false; } bool Atom::isImportProxy() const { return false; } uint64_t Atom::getSize() const { //return fOffset; return fSize; } std::vector& Atom::getReferences() const { return (std::vector&)(fReferences); } bool Atom::mustRemainInSection() const { return true; } const char* Atom::getSectionName() const { if ( strlen(fSection->sectname()) > 15 ) { static char temp[18]; strncpy(temp, fSection->sectname(), 16); temp[17] = '\0'; return temp; } return fSection->sectname(); } Segment& Atom::getSegment() const { return *fSegment; } bool Atom::requiresFollowOnAtom() const { // requires follow-on if built with old compiler and not the last atom if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { if ( fOwner.findAtomIndex(*this) < (fOwner.fAtoms.size()-1) ) return true; } return false; } ObjectFile::Atom& Atom::getFollowOnAtom() const { uint32_t myIndex = fOwner.findAtomIndex(*this); return *fOwner.fAtoms[myIndex+1]; } std::vector* Atom::getStabsDebugInfo() const { if ( fStabsCount == 0 ) return NULL; std::vector* stabs = new std::vector(); stabs->reserve(fStabsCount); for (uint32_t i=0; i < fStabsCount; ++i) { const macho_nlist* sym = &fOwner.fSymbols[fStabsStartIndex+i]; if ( (sym->n_type() & N_STAB) != 0 ) { ObjectFile::StabsInfo stab; stab.atomOffset = sym->n_value(); stab.string = &fOwner.fStrings[sym->n_strx()]; stab.type = sym->n_type(); stab.other = sym->n_sect(); stab.desc = sym->n_desc(); switch ( stab.type ) { case N_FUN: if ( stab.other == 0 ) break; // end of function N_FUN has size (not address) so should not be adjusted // fall through case N_BNSYM: case N_ENSYM: case N_LBRAC: case N_RBRAC: case N_SLINE: case N_STSYM: case N_LCSYM: // all these stab types need their value changed from an absolute address to the atom offset stab.atomOffset -= fOffset; break; } stabs->push_back(stab); } } return stabs; } uint8_t Atom::getAlignment() const { // mach-o file format has no alignment information for atoms - just whole sections if ( fSection != NULL ) { if ( isTentativeDefinition() ) { // common symbols align to their size // that is, a 4-byte common aligns to 4-bytes // to be safe, odd size commons align to the next power-of-2 size uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); // limit alignment of extremely large commons to 2^15 bytes (8-page) if ( alignment < 15 ) return alignment; else return 15; } else { // so we assume every atom requires the same alignment as the whole section return fSection->align(); } } else { return 2; } } void Atom::copyRawContent(uint8_t buffer[]) const { // copy base bytes if ( isZeroFill() ) bzero(buffer, fSize); else { uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); } } void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const { const uint32_t referencesCount = fReferences.size(); // skip copy if no fix-ups if ( referencesCount == 0 ) { uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; writer.write(0, (char*)(fOwner.fHeader)+fileOffset, fSize); return; } // copy base bytes uint8_t buffer[this->getSize()]; this->copyRawContent(buffer); // apply any fix-ups for (uint32_t i=0; i < referencesCount; ++i) { Reference* ref = fReferences[i]; uint32_t offset = ref->getFixUpOffset(); uint32_t* instructionPtr = (uint32_t*)&buffer[offset]; ObjectFile::Atom& target = ref->getTarget(); if ( &target == NULL ) { if ( finalLinkedImage ) throw "target not found"; else continue; } uint32_t instruction; uint32_t newInstruction; switch ( ref->getKind() ) { case Reference::noFixUp: break; case Reference::pointer: { //fprintf(stderr, "writeContent: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); if ( target.isImportProxy() ) { if ( ref->isLazyReference() && finalLinkedImage ) { // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress()); } else { // external realocation ==> pointer contains addend *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); } } else { // internal relocation if ( finalLinkedImage || (strcmp(target.getSectionName(), "__common") != 0) ) { // pointer contains target address //printf("Atom::writeContent() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(target.getAddress() + ref->getTargetOffset()); } else { // pointer contains addend *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); } } } break; case Reference::ppcFixupBranch24: { //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); int64_t displacement = (target.getAddress() + ref->getTargetOffset() ) - (this->getAddress() + offset); if ( !finalLinkedImage && target.isImportProxy() ) { // doing "ld -r" to an external symbol // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target displacement -= target.getAddress(); } else { const int64_t bl_eightMegLimit = 0x00FFFFFF; if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { //fprintf(stderr, "bl out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); throw "bl out of range"; } } instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupBranch14: break; case Reference::ppcFixupPicBaseLow16: { uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); int64_t displacement = targetAddr - picBaseAddr; const int64_t picbase_twoGigLimit = 0x80000000; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; uint16_t instructionLowHalf = (displacement & 0xFFFF); instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupPicBaseLow14: { uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); int64_t displacement = targetAddr - picBaseAddr; const int64_t picbase_twoGigLimit = 0x80000000; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; uint16_t instructionLowHalf = (displacement & 0xFFFF); if ( (instructionLowHalf & 0x3) != 0 ) throw "bad address for lo14 instruction fix-up"; instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupPicBaseHigh16: { uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); int64_t displacement = targetAddr - picBaseAddr; const int64_t picbase_twoGigLimit = 0x80000000; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; uint16_t instructionLowHalf = displacement >> 16; if ( (displacement & 0x00008000) != 0 ) ++instructionLowHalf; instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupAbsLow16: { int64_t addr = target.getAddress() + ref->getTargetOffset(); if ( !finalLinkedImage && target.isImportProxy() ) addr -= target.getAddress() ; uint16_t instructionLowHalf = (addr & 0xFFFF); instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupAbsLow14: { int64_t addr = target.getAddress() + ref->getTargetOffset(); if ( !finalLinkedImage && target.isImportProxy() ) addr -= target.getAddress() ; uint16_t instructionLowHalf = (addr & 0xFFFF); if ( (instructionLowHalf & 0x3) != 0 ) throw "bad address for lo14 instruction fix-up"; instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupAbsHigh16: { int64_t addr = target.getAddress() + ref->getTargetOffset(); if ( !finalLinkedImage && target.isImportProxy() ) addr -= target.getAddress() ; uint16_t hi16 = (addr >> 16); instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0000) | hi16; OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::ppcFixupAbsHigh16AddLow: { int64_t addr = target.getAddress() + ref->getTargetOffset(); if ( !finalLinkedImage && target.isImportProxy() ) addr -= target.getAddress() ; if ( addr & 0x00008000 ) addr += 0x00010000; instruction = OSReadBigInt32(instructionPtr, 0); newInstruction = (instruction & 0xFFFF0000) | (addr >> 16); OSWriteBigInt32(instructionPtr, 0, newInstruction); } break; case Reference::pointer32Difference: ENDIAN_WRITE32(*instructionPtr, target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); break; case Reference::pointer64Difference: *((uint64_t*)instructionPtr) = ENDIAN_SWAP64(target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); break; case Reference::x86FixupBranch32: { int64_t displacement = target.getAddress() - (this->getAddress() + offset); if ( target.isImportProxy() ) { displacement = 0; } else { const int64_t bl_twoGigLimit = 0x7FFFFFFF; if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) { //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); throw "call out of range"; } } OSWriteLittleInt32(instructionPtr, 0, (int32_t)displacement); } break; } } // write out writer.write(0, buffer, getSize()); } const macho_section* Atom::findSectionFromOffset(macho_uintptr_t offset) { const macho_section* const sectionsStart = (const macho_section*)( (char*)fOwner.fSegment + sizeof(macho_segment_command) ); const macho_section* const sectionsEnd = §ionsStart[fOwner.fSegment->nsects()]; for (const macho_section* s = sectionsStart; s < sectionsEnd; ++s) { if ( (s->addr() <= offset) && (offset < (s->addr()+s->size())) ) return s; } throw "section not found"; } void Atom::setSize(macho_uintptr_t size) { fSize = size; } void Atom::setFollowOnAtom(Atom&) { } Reference* Atom::addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) { if ( (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && ((target.fSymbol != NULL) || (target.fSynthesizedName != NULL)) ) return this->addByNameReference(offsetInSrcAtom, kind, target.getName(), offsetInTarget, offsetInFromTarget); else return this->addDirectReference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); } Reference* Atom::addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) { Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); // in rare cases, there may already be a by-name reference to the same atom. If so, replace with this direct reference for (std::vector::iterator it=fReferences.begin(); it != fReferences.end(); it++) { ObjectFile::Reference* aRef = *it; if ( (aRef->getFixUpOffset() == offsetInSrcAtom) && (aRef->getKind() == kind) ) { *it = ref; return ref; } } // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file fReferences.insert(fReferences.begin(), ref); return ref; } Reference* Atom::addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) { Reference* ref = new Reference(offsetInSrcAtom, kind, targetName, offsetInTarget, offsetInFromTarget); // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file fReferences.insert(fReferences.begin(), ref); return ref; } Reference* Atom::addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget) { Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, fromTarget, offsetInFromTarget); // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file fReferences.insert(fReferences.begin(), ref); return ref; } Segment::Segment(const macho_section* sect) : fSection(sect) { } const char* Segment::getName() const { return fSection->segname(); } bool Segment::isContentReadable() const { return true; } bool Segment::isContentWritable() const { if ( strcmp(fSection->segname(), "__DATA") == 0 ) return true; if ( strcmp(fSection->segname(), "__OBJC") == 0 ) return true; return false; } bool Segment::isContentExecutable() const { return ( strcmp(fSection->segname(), "__TEXT") == 0 ); } Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) : fTarget(NULL), fFromTarget(NULL), fTargetName(targetName), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) { } Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) : fTarget(&target), fFromTarget(NULL), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) { } Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget) : fTarget(&target), fFromTarget(&fromTarget), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) { // assure no direct references to something that might be coalesced if ( (target.isWeakDefinition() || target.isCoalesableByName()) && (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (target.getName() != NULL) ) { //fprintf(stderr, "change TO direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); fTargetName = target.getName(); fTarget = NULL; } // Note: We should also allow by-name from references, but many other chunks of code assume from targets are always direct// // if ( (fromTarget.isWeakDefinition() || fromTarget.isCoalesableByName()) && (fromTarget.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (fromTarget.getName() != NULL)) { // fprintf(stderr, "change FROM direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); // fFromTargetName = fromTarget.getName(); // fFromTarget = NULL; // } } Reference::~Reference() { } bool Reference::isUnbound() const { if ( fTarget == NULL ) return true; if ( (fFromTargetName!=NULL) && (fFromTarget==NULL) ) return true; return false; } bool Reference::isWeakReference() const { return fWeak; } bool Reference::requiresRuntimeFixUp() const { return ( fKind == Reference::pointer ); } bool Reference::isLazyReference() const { return fLazy; } ObjectFile::Reference::Kind Reference::getKind() const { return fKind; } uint64_t Reference::getFixUpOffset() const { return fFixUpOffsetInSrc; } const char* Reference::getTargetName() const { if ( fTargetName != NULL ) return fTargetName; return fTarget->getName(); } ObjectFile::Atom& Reference::getTarget() const { return *fTarget; } void Reference::setTarget(ObjectFile::Atom& target) { fTarget = ⌖ } ObjectFile::Atom& Reference::getFromTarget() const { return *fFromTarget; } const char* Reference::getFromTargetName() const { if ( fFromTargetName != NULL ) return fFromTargetName; return fFromTarget->getName(); } void Reference::setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } void Reference::setFromTargetName(const char* name) { fFromTargetName = name; } uint64_t Reference::getTargetOffset() const { return fTargetOffset; } uint64_t Reference::getFromTargetOffset() const { return fFromTargetOffset; } void Reference::setLazy(bool lazy) { fLazy = lazy; } void Reference::setWeak(bool weak) { fWeak = weak; } const char* Reference::getDescription() const { static char temp[256]; if ( fKind == pointer32Difference ) { // by-name references have quoted names bool targetByName = ( &(this->getTarget()) == NULL ); bool fromByName = ( &(this->getFromTarget()) == NULL ); const char* targetQuotes = targetByName ? "\"" : ""; const char* fromQuotes = fromByName ? "\"" : ""; sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); } else if ( fKind == pointer64Difference ) { // by-name references have quoted names bool targetByName = ( &(this->getTarget()) == NULL ); bool fromByName = ( &(this->getFromTarget()) == NULL ); const char* targetQuotes = targetByName ? "\"" : ""; const char* fromQuotes = fromByName ? "\"" : ""; sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); } else { switch( fKind ) { case noFixUp: sprintf(temp, "reference to "); break; case pointer: { const char* weak = ""; if ( fWeak ) weak = "weak "; const char* lazy = ""; if ( fLazy ) lazy = "lazy "; sprintf(temp, "offset 0x%04llX, %s%spointer to ", this->getFixUpOffset(), weak, lazy); } break; case ppcFixupBranch14: case ppcFixupBranch24: sprintf(temp, "offset 0x%04llX, bl pc-rel fixup to ", this->getFixUpOffset()); break; case ppcFixupPicBaseLow16: sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); break; case ppcFixupPicBaseLow14: sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); break; case ppcFixupPicBaseHigh16: sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); break; case ppcFixupAbsLow16: sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", this->getFixUpOffset()); break; case ppcFixupAbsLow14: sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", this->getFixUpOffset()); break; case ppcFixupAbsHigh16: sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); break; case ppcFixupAbsHigh16AddLow: sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); break; case pointer32Difference: case pointer64Difference: // handled above break; case x86FixupBranch32: sprintf(temp, "offset 0x%04llX, call pc-rel fixup to ", this->getFixUpOffset()); break; } // always quote by-name references if ( fTargetName != NULL ) { strcat(temp, "\""); strcat(temp, fTargetName); strcat(temp, "\""); } else if ( fTarget != NULL ) { strcat(temp, fTarget->getDisplayName()); } else { strcat(temp, "NULL target"); } if ( fTargetOffset != 0 ) sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getTargetOffset()); if ( (fKind==pointer) && fLazy ) { strcat(temp, " initially bound to \""); strcat(temp, this->getFromTargetName()); strcat(temp, "\""); } } return temp; } };