ObjectFileMachO.cpp [plain text]
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 isTargetUnbound() const;
virtual bool isFromTargetUnbound() const;
virtual bool isWeakReference() const;
virtual bool requiresRuntimeFixUp(bool slideable) 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 bool hasFromTarget() const;
virtual ObjectFile::Atom& getFromTarget() const;
virtual const char* getFromTargetName() const;
virtual void setTarget(ObjectFile::Atom&, uint64_t offset);
virtual void setFromTarget(ObjectFile::Atom&);
virtual void setFromTargetName(const char*);
virtual void setFromTargetOffset(uint64_t);
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<class ObjectFile::Atom*>& getAtoms();
virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
virtual std::vector<ObjectFile::StabsInfo>* getStabsDebugInfo();
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<uint32_t>& offsets, std::set<uint32_t>& 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<uint32_t>& offsets, uint32_t value);
void insertOffsetIfNotText(std::set<uint32_t>& 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<class Atom*> fAtoms;
std::vector<class Segment*> fSegments;
std::set<class ObjectFile::Atom*> fDeadAtoms;
uint32_t fNonAtomStabsStartIndex;
uint32_t fNonAtomStabsCount;
std::vector<uint32_t> 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; virtual bool isImportProxy() const;
virtual uint64_t getSize() const;
virtual std::vector<ObjectFile::Reference*>& 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<ObjectFile::StabsInfo>* 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(uint32_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<class Reference*> fReferences;
ObjectFile::Atom::Scope fScope;
uint32_t fStabsStartIndex;
uint32_t fStabsCount;
static macho_section fgCommonsSection; };
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),
fIndirectTable(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)
{
#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
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());
}
std::set<uint32_t> 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)) ) {
Atom* newAtom = new Atom(*this, &sym);
fAtoms.push_back(newAtom);
symbolAtomOffsets.insert(newAtom->fOffset);
}
}
}
const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command));
const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()];
std::set<uint32_t> cleavePoints;
std::set<uint32_t> 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);
}
}
std::set<uint32_t> 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();
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;
}
if ( sect->size() != 0 )
cleavePoints.insert(sect->addr());
}
for (std::set<uint32_t>::iterator it=cleavePoints.begin(); it != cleavePoints.end(); it++) {
uint32_t cleavePoint = *it;
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 ) {
std::sort(fAtoms.begin(), fAtoms.end(), Atom::atomCompare);
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);
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]);
}
}
for (std::set<uint32_t>::iterator it=deadStubOffsets.begin(); it != deadStubOffsets.end(); it++) {
Atom* deadStub = findAtomCoveringOffset(*it);
this->deadStub(*deadStub);
}
for (std::set<ObjectFile::Atom*>::iterator deadIt=fDeadAtoms.begin(); deadIt != fDeadAtoms.end(); deadIt++) {
for (std::vector<Atom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
if ( *deadIt == *it ) {
fAtoms.erase(it);
break;
}
}
}
}
if ( ! fOptions.fStripDebugInfo ) {
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 ) {
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 ) {
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:
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);
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;
if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
std::vector<ObjectFile::Reference*>& refs = srcAtom->getReferences();
if ( refs.size() > 0 ) {
Reference* ref = (Reference*)refs[0];
#if defined(ARCH_PPC64)
targetName = "dyld_stub_binding_helper";
#endif
ref->setFromTargetName(targetName);
}
else {
fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size());
}
}
#if defined(ARCH_PPC64)
else if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
}
#endif
else {
srcAtom->addByNameReference(offsetInSrcAtom, Reference::pointer, targetName, addend, 0);
}
}
else {
dstAddr = targetSymbol->n_value();
Atom* dstAtom = findAtomCoveringOffset(dstAddr);
macho_uintptr_t addend = pointerValue;
if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
std::vector<ObjectFile::Reference*>& refs = srcAtom->getReferences();
if ( refs.size() > 0 ) {
Reference* ref = (Reference*)refs[0];
ref->setFromTarget(*dstAtom);
ref->setFromTargetOffset(dstAddr - dstAtom->fOffset);
}
else {
fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size());
}
}
else {
srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, addend, 0);
}
}
}
else {
Atom* dstAtom = findAtomCoveringOffset(pointerValue);
if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
std::vector<ObjectFile::Reference*>& refs = srcAtom->getReferences();
if ( refs.size() > 0 ) {
Reference* ref = (Reference*)refs[0];
ref->setFromTarget(*dstAtom);
ref->setFromTargetOffset(pointerValue - dstAtom->fOffset);
}
else {
fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size());
}
}
else {
srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, pointerValue-dstAtom->fOffset, 0);
}
}
}
break;
case PPC_RELOC_JBSR:
break;
#endif
#if defined(ARCH_I386)
case GENERIC_RELOC_VANILLA:
{
srcAddr = sect->addr() + reloc->r_address();
src = findAtomCoveringOffset(srcAddr);
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 += srcAddr + 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);
if ( (src->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
std::vector<ObjectFile::Reference*>& refs = src->getReferences();
if ( refs.size() == 1 ) {
Reference* ref = (Reference*)refs[0];
ref->setFromTarget(*dst);
ref->setFromTargetOffset(pointerValue - dst->fOffset);
}
else {
fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", src->getDisplayName(), refs.size());
}
}
else if ( ((uint8_t*)fixUpPtr)[-1] == 0xE8 ) this->addCallSiteReference(*src, offsetInSrcAtom, kind, *dst, 0, pointerValue - dst->fOffset);
else
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];
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));
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);
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:
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
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;
base = &pivot[1];
--n;
}
else {
}
}
Atom* lastAtom = fAtoms.back();
if ( (lastAtom->fOffset == offset) && (lastAtom->fSize == 0) )
return lastAtom;
#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
throwf("address 0x%08X is not in any atom", offset);
}
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<uint32_t>& offsets, uint32_t value)
{
offsets.insert(value);
}
void Reader::insertOffsetIfNotText(std::set<uint32_t>& 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<uint32_t>& 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);
}
}
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<uint32_t>& cleavePoints, std::set<uint32_t>& 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:
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;
insertOffset(cleavePoints, sect->addr() + reloc->r_address() + displacement);
}
}
}
break;
case PPC_RELOC_PAIR:
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);
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);
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);
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;
insertOffset(cleavePoints, targetAddr);
}
}
break;
case PPC_RELOC_JBSR:
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
if ( !reloc->r_extern() ) {
macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr));
#if defined(ARCH_I386)
if ( (sect->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS )
break;
#endif
if ( reloc->r_pcrel() )
pointerValue += reloc->r_address() + sect->addr() + sizeof(macho_uintptr_t);
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:
insertOffset(cleavePoints, sreloc->r_value());
insertOffsetIfText(dontCleavePoints, sreloc->r_value());
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;
}
insertOffsetIfText(dontCleavePoints, sreloc->r_value());
insertOffsetIfText(dontCleavePoints, nextReloc->r_value());
}
break;
case PPC_RELOC_PAIR:
break;
#endif
#if defined(ARCH_I386)
case GENERIC_RELOC_SECTDIFF:
case GENERIC_RELOC_LOCAL_SECTDIFF:
{
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:
break;
#endif
default:
printf("unknown relocation type %d\n", sreloc->r_type());
}
}
}
Segment* Reader::makeSegmentFromSection(const macho_section* sect)
{
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<class ObjectFile::Atom*>& Reader::getAtoms()
{
return (std::vector<class ObjectFile::Atom*>&)(fAtoms);
}
std::vector<class ObjectFile::Atom*>* Reader::getJustInTimeAtomsFor(const char* name)
{
return NULL;
}
std::vector<ObjectFile::StabsInfo>* Reader::getStabsDebugInfo()
{
if ( fNonAtomStabsCount == 0 )
return NULL;
std::vector<ObjectFile::StabsInfo>* stabs = new std::vector<ObjectFile::StabsInfo>();
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();
if ( stab.type == N_SO )
stab.atomOffset = 0;
stabs->push_back(stab);
}
}
for (std::vector<uint32_t>::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)
{
fDeadAtoms.insert(&target);
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)
{
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) ) {
src.addDirectReference(offsetInSrcAtom, kind, *betterTarget, offsetInTargetAtom, picBaseOffset);
this->deadStub(target);
return;
}
}
}
}
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 ) {
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:
{
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);
}
}
break;
}
}
else if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) {
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);
}
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 fSize;
}
std::vector<ObjectFile::Reference*>& Atom::getReferences() const
{
return (std::vector<ObjectFile::Reference*>&)(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
{
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<ObjectFile::StabsInfo>* Atom::getStabsDebugInfo() const
{
if ( fStabsCount == 0 )
return NULL;
std::vector<ObjectFile::StabsInfo>* stabs = new std::vector<ObjectFile::StabsInfo>();
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;
case N_BNSYM:
case N_ENSYM:
case N_LBRAC:
case N_RBRAC:
case N_SLINE:
case N_STSYM:
case N_LCSYM:
stab.atomOffset -= fOffset;
break;
}
stabs->push_back(stab);
}
}
return stabs;
}
uint8_t Atom::getAlignment() const
{
if ( fSection != NULL ) {
if ( isTentativeDefinition() ) {
uint8_t alignment = (uint8_t)ceil(log2(this->getSize()));
if ( alignment < 15 )
return alignment;
else
return 15;
}
else {
return fSection->align();
}
}
else {
return 2;
}
}
void Atom::copyRawContent(uint8_t buffer[]) const
{
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();
if ( referencesCount == 0 ) {
uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset;
writer.write(0, (char*)(fOwner.fHeader)+fileOffset, fSize);
return;
}
uint8_t buffer[this->getSize()];
this->copyRawContent(buffer);
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:
{
if ( ref->isLazyReference() && finalLinkedImage ) {
*((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress());
}
else if ( target.isImportProxy() ) {
*((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset());
}
else {
if ( finalLinkedImage || (strcmp(target.getSectionName(), "__common") != 0) ) {
*((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(target.getAddress() + ref->getTargetOffset());
}
else {
*((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset());
}
}
}
break;
case Reference::ppcFixupBranch24:
{
int64_t displacement = (target.getAddress() + ref->getTargetOffset() ) - (this->getAddress() + offset);
if ( !finalLinkedImage && target.isImportProxy() ) {
displacement -= target.getAddress();
}
else {
const int64_t bl_eightMegLimit = 0x00FFFFFF;
if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) {
throwf("bl out of range (%lld max is +/-16M) from %s in %s to %s in %s", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
}
}
instruction = OSReadBigInt32(instructionPtr, 0);
newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
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)) ) {
throw "call out of range";
}
}
OSWriteLittleInt32(instructionPtr, 0, (int32_t)displacement);
}
break;
}
}
writer.write(0, buffer, getSize());
}
const macho_section* Atom::findSectionFromOffset(uint32_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;
}
throwf("address 0x%08X is not in any section", offset);
}
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);
for (std::vector<Reference*>::iterator it=fReferences.begin(); it != fReferences.end(); it++) {
ObjectFile::Reference* aRef = *it;
if ( (aRef->getFixUpOffset() == offsetInSrcAtom) && (aRef->getKind() == kind) ) {
*it = ref;
return ref;
}
}
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);
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);
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)
{
if ( (target.isWeakDefinition() || target.isCoalesableByName()) && (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (target.getName() != NULL) ) {
fTargetName = target.getName();
fTarget = NULL;
}
}
Reference::~Reference()
{
}
bool Reference::isTargetUnbound() const
{
return ( fTarget == NULL );
}
bool Reference::isFromTargetUnbound() const
{
return ( fFromTarget == NULL );
}
bool Reference::isWeakReference() const
{
return fWeak;
}
bool Reference::requiresRuntimeFixUp(bool slideable) const
{
#if defined(ARCH_PPC) || defined(ARCH_PPC64)
return ( (fKind == Reference::pointer) && (fTarget->isImportProxy() || fTarget->isWeakDefinition() || slideable) );
#elif defined(ARCH_I386)
if ( fKind == Reference::pointer ) {
if ( fTarget->isImportProxy() )
return true;
else
return slideable;
}
return false;
#else
#error
#endif
}
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, uint64_t offset)
{
fTarget = ⌖
fTargetOffset = offset;
}
ObjectFile::Atom& Reference::getFromTarget() const
{
return *fFromTarget;
}
bool Reference::hasFromTarget() const
{
return ( (fFromTarget != NULL) || (fFromTargetName != NULL) );
}
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;
}
void Reference::setFromTargetOffset(uint64_t offset)
{
fFromTargetOffset = offset;
}
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 ) {
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 ) {
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:
break;
case x86FixupBranch32:
sprintf(temp, "offset 0x%04llX, pc-rel fixup to ", this->getFixUpOffset());
break;
}
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 \"");
if ( (fFromTarget != NULL) || (fFromTargetName != NULL) ) {
strcat(temp, this->getFromTargetName());
strcat(temp, "\"");
if ( this->getFromTargetOffset() != 0 )
sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getFromTargetOffset());
}
else {
strcat(temp, "\" << missing >>");
}
}
}
return temp;
}
};