#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <string>
#include <set>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <ext/hash_map>
#include "Options.h"
#include "ObjectFile.h"
#include "ObjectFileMachO-all.h"
#include "ExecutableFile.h"
#include "ExecutableFileMachO-all.h"
#include "SectCreate.h"
#if 0
static void dumpAtom(ObjectFile::Atom* atom)
{
printf("name: %s\n", atom->getDisplayName());
switch ( atom->getScope() ) {
case ObjectFile::Atom::scopeTranslationUnit:
printf("scope: translation unit\n");
break;
case ObjectFile::Atom::scopeLinkageUnit:
printf("scope: linkage unit\n");
break;
case ObjectFile::Atom::scopeGlobal:
printf("scope: global\n");
break;
default:
printf("scope: unknown\n");
}
printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName());
printf("attrs: ");
if ( atom->isTentativekDefinition() )
printf("tentative ");
else if ( atom->isWeakDefinition() )
printf("weak ");
if ( atom->isCoalesableByName() )
printf("coalesce-by-name ");
if ( atom->isCoalesableByValue() )
printf("coalesce-by-value ");
if ( atom->dontDeadStrip() )
printf("dont-dead-strip ");
if ( atom->isZeroFill() )
printf("zero-fill ");
printf("\n");
printf("size: 0x%012llX\n", atom->getSize());
uint8_t content[atom->getSize()];
atom->copyRawContent(content);
printf("content: ");
if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) {
printf("\"%s\"", content);
}
else {
for (unsigned int i=0; i < sizeof(content); ++i)
printf("%02X ", content[i]);
}
printf("\n");
std::vector<ObjectFile::Reference*>& references = atom->getReferences();
const int refCount = references.size();
printf("references: (%u)\n", refCount);
for (int i=0; i < refCount; ++i) {
ObjectFile::Reference* ref = references[i];
printf(" %s\n", ref->getDescription());
}
}
#endif
class CStringComparor
{
public:
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); }
};
class CStringEquals
{
public:
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
};
class Section : public ObjectFile::Section
{
public:
static Section* find(const char* sectionName, const char* segmentName, bool zeroFill);
static void assignIndexes();
private:
Section(const char* sectionName, const char* segmentName, bool zeroFill);
struct Sorter {
static int segmentOrdinal(const char* segName);
bool operator()(Section* left, Section* right);
};
typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection;
const char* fSectionName;
const char* fSegmentName;
bool fZeroFill;
static NameToSection fgMapping;
static std::vector<Section*> fgSections;
};
Section::NameToSection Section::fgMapping;
std::vector<Section*> Section::fgSections;
Section::Section(const char* sectionName, const char* segmentName, bool zeroFill)
: fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill)
{
}
Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill)
{
#if 0
std::pair<NameToSection::iterator, NameToSection::iterator> range = fgMapping.equal_range(sectionName);
for (NameToSection::iterator it=range.first; it != range.second; it++) {
if ( strcmp(it->second->fSegmentName, segmentName) == 0 )
return it->second;
}
#endif
NameToSection::iterator pos = fgMapping.find(sectionName);
if ( pos != fgMapping.end() ) {
if ( strcmp(pos->second->fSegmentName, segmentName) == 0 )
return pos->second;
for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) )
return *it;
}
}
Section* sect = new Section(sectionName, segmentName, zeroFill);
sect->fIndex = fgMapping.size();
fgMapping[sectionName] = sect;
fgSections.push_back(sect);
return sect;
}
int Section::Sorter::segmentOrdinal(const char* segName)
{
if ( strcmp(segName, "__PAGEZERO") == 0 )
return 1;
if ( strcmp(segName, "__TEXT") == 0 )
return 2;
if ( strcmp(segName, "__DATA") == 0 )
return 3;
if ( strcmp(segName, "__OBJC") == 0 )
return 4;
if ( strcmp(segName, "__LINKEDIT") == 0 )
return INT_MAX; else
return 5;
}
bool Section::Sorter::operator()(Section* left, Section* right)
{
const char* leftSegName = left->fSegmentName;
const char* rightSegName = right->fSegmentName;
int segNameCmp = strcmp(leftSegName, rightSegName);
if ( segNameCmp != 0 )
{
int leftSegOrdinal = segmentOrdinal(leftSegName);
int rightSegOrdinal = segmentOrdinal(rightSegName);
if ( leftSegOrdinal < rightSegOrdinal )
return true;
if ( leftSegOrdinal == rightSegOrdinal )
return segNameCmp < 0;
return false;
}
if ( !left->fZeroFill && right->fZeroFill )
return true;
if ( left->fZeroFill && !right->fZeroFill )
return false;
return left->fIndex < right->fIndex;
}
void Section::assignIndexes()
{
std::sort(fgSections.begin(), fgSections.end(), Section::Sorter());
unsigned int newOrder = 1;
for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++)
(*it)->fIndex = newOrder++;
}
class Linker {
public:
Linker(int argc, const char* argv[]);
void createReaders();
void createWriter();
void addInputFile(ObjectFile::Reader* reader);
void setOutputFile(ExecutableFile::Writer* writer);
void link();
private:
ObjectFile::Reader* createReader(const Options::FileInfo&);
void addAtom(ObjectFile::Atom& atom);
void addAtoms(std::vector<class ObjectFile::Atom*>& atoms);
void buildAtomList();
void loadUndefines();
void addWeakAtomOverrides();
void resolveReferences();
void deadStrip();
void sortAtoms();
void tweakLayout();
void writeOutput();
void resolve(ObjectFile::Reference* reference);
void resolveFrom(ObjectFile::Reference* reference);
void addJustInTimeAtoms(const char* name);
void addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info);
void addIndirectLibraries(ObjectFile::Reader* reader);
bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader);
bool haveDirectLibrary(const char* path);
class SymbolTable
{
public:
SymbolTable(Linker&);
void require(const char* name);
bool add(ObjectFile::Atom& atom);
ObjectFile::Atom* find(const char* name);
unsigned int getRequireCount() { return fRequireCount; }
void getNeededNames(bool andWeakDefintions, std::vector<const char*>& undefines);
private:
typedef std::map<const char*, ObjectFile::Atom*, CStringComparor> Mapper;
Linker& fOwner;
Mapper fTable;
unsigned int fRequireCount;
};
struct AtomSorter
{
bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right);
};
typedef std::map<const char*, uint32_t, CStringComparor> SectionOrder;
struct IndirectLibrary {
const char* path;
uint64_t fileLen;
ObjectFile::Reader* reader;
std::set<ObjectFile::Reader*> parents;
ObjectFile::Reader* reExportParent;
};
Options fOptions;
SymbolTable fGlobalSymbolTable;
unsigned int fWeakSymbolsAddedCount;
std::vector<class ObjectFile::Reader*> fInputFiles;
ExecutableFile::Writer* fOutputFile;
std::vector<ExecutableFile::DyLibUsed> fDynamicLibraries;
std::list<IndirectLibrary> fIndirectDynamicLibraries;
std::vector<class ObjectFile::Atom*> fAllAtoms;
std::set<class ObjectFile::Atom*> fDeadAtoms;
SectionOrder fSectionOrder;
unsigned int fNextSortOrder;
bool fDirectLibrariesComplete;
};
Linker::Linker(int argc, const char* argv[])
: fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fNextSortOrder(1), fDirectLibrariesComplete(false)
{
}
void Linker::addInputFile(ObjectFile::Reader* reader)
{
fInputFiles.push_back(reader);
}
void Linker::setOutputFile(ExecutableFile::Writer* writer)
{
fOutputFile = writer;
}
void Linker::link()
{
this->buildAtomList();
this->loadUndefines();
this->resolveReferences();
this->deadStrip();
this->sortAtoms();
this->tweakLayout();
this->writeOutput();
}
inline void Linker::addAtom(ObjectFile::Atom& atom)
{
fAllAtoms.push_back(&atom);
std::vector<class ObjectFile::Reference*>& references = atom.getReferences();
for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
ObjectFile::Reference* reference = *it;
if ( reference->isTargetUnbound() ) {
fGlobalSymbolTable.require(reference->getTargetName());
}
if ( reference->hasFromTarget() && reference->isFromTargetUnbound() )
fGlobalSymbolTable.require(reference->getFromTargetName());
}
ObjectFile::Atom::Scope scope = atom.getScope();
const char* name = atom.getName();
if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) {
fGlobalSymbolTable.add(atom);
if ( (scope == ObjectFile::Atom::scopeGlobal) && fOptions.hasExportRestrictList() ) {
bool doExport = fOptions.shouldExport(name);
if ( !doExport ) {
atom.setScope(ObjectFile::Atom::scopeLinkageUnit);
}
}
}
atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill()));
if ( atom.getSortOrder() == 0 )
fNextSortOrder = atom.setSortOrder(fNextSortOrder);
}
inline void Linker::addAtoms(std::vector<class ObjectFile::Atom*>& atoms)
{
for (std::vector<ObjectFile::Atom*>::iterator it=atoms.begin(); it != atoms.end(); it++) {
this->addAtom(**it);
}
}
void Linker::buildAtomList()
{
std::vector<const char*>& initialUndefines = fOptions.initialUndefines();
for (std::vector<const char*>::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) {
fGlobalSymbolTable.require(*it);
}
this->addAtoms(fOutputFile->getAtoms());
const int readerCount = fInputFiles.size();
for (int i=0; i < readerCount; ++i) {
this->addAtoms(fInputFiles[i]->getAtoms());
}
std::vector<Options::ExtraSection>& extraSections = fOptions.extraSections();
for( std::vector<Options::ExtraSection>::iterator it=extraSections.begin(); it != extraSections.end(); ++it) {
this->addAtoms(SectCreate::MakeReader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)->getAtoms());
}
}
void Linker::loadUndefines()
{
unsigned int undefineCount = 0xFFFFFFFF;
while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) {
undefineCount = fGlobalSymbolTable.getRequireCount();
std::vector<const char*> undefineNames;
fGlobalSymbolTable.getNeededNames(true, undefineNames);
const int undefineCount = undefineNames.size();
for (int i=0; i < undefineCount; ++i) {
const char* name = undefineNames[i];
ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name);
if ( (possibleAtom == NULL) || (possibleAtom->isWeakDefinition() && (fOptions.outputKind() != Options::kObjectFile)) )
this->addJustInTimeAtoms(name);
}
}
if ( fOptions.outputKind() != Options::kObjectFile ) {
bool doPrint = true;
bool doError = true;
switch ( fOptions.undefinedTreatment() ) {
case Options::kUndefinedError:
break;
case Options::kUndefinedDynamicLookup:
doError = false;
break;
case Options::kUndefinedWarning:
doError = false;
break;
case Options::kUndefinedSuppress:
doError = false;
doPrint = false;
break;
}
std::vector<const char*> unresolvableUndefines;
fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines);
const int unresolvableCount = unresolvableUndefines.size();
int unresolvableExportsCount = 0;
if ( unresolvableCount != 0 ) {
if ( doPrint ) {
fprintf(stderr, "can't resolve symbols:\n");
for (int i=0; i < unresolvableCount; ++i) {
const char* name = unresolvableUndefines[i];
const unsigned int nameLen = strlen(name);
fprintf(stderr, " %s, referenced from:\n", name);
char stubName[nameLen+6];
strcpy(stubName, name);
strcat(stubName, "$stub");
char nonLazyName[nameLen+16];
strcpy(nonLazyName, name);
strcat(nonLazyName, "$non_lazy_ptr");
ObjectFile::Atom* lastStubAtomWithUnresolved = NULL;
ObjectFile::Atom* lastNonLazyAtomWithUnresolved = NULL;
bool foundAtomReference = false;
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
ObjectFile::Atom* atom = *it;
std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
ObjectFile::Reference* reference = *rit;
if ( reference->isTargetUnbound() ) {
if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getTargetName(), stubName) == 0) ) {
const char* path = atom->getFile()->getPath();
const char* shortPath = strrchr(path, '/');
if ( shortPath == NULL )
shortPath = path;
else
shortPath = &shortPath[1];
fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath);
lastStubAtomWithUnresolved = atom;
foundAtomReference = true;
}
else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getTargetName(), nonLazyName) == 0) ) {
const char* path = atom->getFile()->getPath();
const char* shortPath = strrchr(path, '/');
if ( shortPath == NULL )
shortPath = path;
else
shortPath = &shortPath[1];
fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath);
lastNonLazyAtomWithUnresolved = atom;
foundAtomReference = true;
}
}
if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) {
if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), stubName) == 0) ) {
const char* path = atom->getFile()->getPath();
const char* shortPath = strrchr(path, '/');
if ( shortPath == NULL )
shortPath = path;
else
shortPath = &shortPath[1];
fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath);
lastStubAtomWithUnresolved = atom;
foundAtomReference = true;
}
else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), nonLazyName) == 0) ) {
const char* path = atom->getFile()->getPath();
const char* shortPath = strrchr(path, '/');
if ( shortPath == NULL )
shortPath = path;
else
shortPath = &shortPath[1];
fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath);
lastNonLazyAtomWithUnresolved = atom;
foundAtomReference = true;
}
}
}
}
if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) {
fprintf(stderr, " -exported_symbols_list command line option\n");
++unresolvableExportsCount;
}
}
}
if ( doError && (unresolvableCount > unresolvableExportsCount) ) throw "symbol(s) not found";
}
if ( fOptions.initFunctionName() != NULL ) {
if ( fGlobalSymbolTable.find(fOptions.initFunctionName()) == NULL )
throwf("symbol %s not found for -init", fOptions.initFunctionName());
}
}
}
void Linker::addJustInTimeAtoms(const char* name)
{
if ( fOptions.outputKind() != Options::kObjectFile ) {
ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name);
if ( atom != NULL ) {
this->addAtom(*atom);\
return;
}
}
const int readerCount = fInputFiles.size();
for (int i=0; i < readerCount; ++i) {
std::vector<class ObjectFile::Atom*>* atoms = fInputFiles[i]->getJustInTimeAtomsFor(name);
if ( atoms != NULL ) {
this->addAtoms(*atoms);
delete atoms;
return; }
}
for (std::list<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
ObjectFile::Reader* reader = it->reader;
if ( reader != NULL ) {
std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name);
if ( atoms != NULL ) {
this->addAtoms(*atoms);
delete atoms;
break;
}
}
}
if ( fOptions.outputKind() == Options::kObjectFile ) {
ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name);
if ( atom != NULL ) {
this->addAtom(*atom);
return;
}
}
}
void Linker::resolve(ObjectFile::Reference* reference)
{
ObjectFile::Atom* target = NULL;
const char* targetName = reference->getTargetName();
const int targetNameLen = strlen(targetName);
if ( (targetNameLen > 5) && (strcmp(&targetName[targetNameLen-5], "$stub") == 0) ) {
char nonStubTarget[targetNameLen+1];
strcpy(nonStubTarget, targetName);
nonStubTarget[targetNameLen-5] = '\0';
if ( !fOptions.interposable() || !fOptions.shouldExport(nonStubTarget) ) {
target = fGlobalSymbolTable.find(nonStubTarget);
if ( (target != NULL) && !target->isImportProxy() && (!target->isWeakDefinition() || (target->getScope() != ObjectFile::Atom::scopeGlobal)) ) {
reference->setTarget(*target, reference->getTargetOffset());
ObjectFile::Atom* stub = fGlobalSymbolTable.find(targetName);
if ( stub != NULL ) {
char lazySymbol[targetNameLen+8];
strcpy(lazySymbol, nonStubTarget);
strcat(lazySymbol, "$lazy_ptr");
ObjectFile::Atom* lazyPtr = fGlobalSymbolTable.find(lazySymbol);
fDeadAtoms.insert(stub);
if ( lazyPtr != NULL )
fDeadAtoms.insert(lazyPtr);
}
return;
}
}
}
target = fGlobalSymbolTable.find(targetName);
if ( target == NULL ) {
fprintf(stderr, "can't resolve: %s\n", targetName);
}
reference->setTarget(*target, reference->getTargetOffset());
if ( target->isImportProxy() ) {
bool mismatch = false;
if ( reference->isWeakReference() ) {
switch(target->getImportWeakness()) {
case ObjectFile::Atom::kWeakUnset:
target->setImportWeakness(true);
break;
case ObjectFile::Atom::kWeakImport:
break;
case ObjectFile::Atom::kNonWeakImport:
mismatch = true;
break;
}
}
else {
switch(target->getImportWeakness()) {
case ObjectFile::Atom::kWeakUnset:
target->setImportWeakness(false);
break;
case ObjectFile::Atom::kWeakImport:
mismatch = true;
break;
case ObjectFile::Atom::kNonWeakImport:
break;
}
}
if ( mismatch ) {
switch ( fOptions.weakReferenceMismatchTreatment() ) {
case Options::kWeakReferenceMismatchError:
throwf("mismatching weak references for symbol: %s", target->getName());
case Options::kWeakReferenceMismatchWeak:
target->setImportWeakness(true);
break;
case Options::kWeakReferenceMismatchNonWeak:
target->setImportWeakness(false);
break;
}
}
}
}
void Linker::resolveFrom(ObjectFile::Reference* reference)
{
const char* fromTargetName = reference->getFromTargetName();
ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName);
if ( fromTarget == NULL ) {
fprintf(stderr, "can't resolve: %s\n", fromTargetName);
}
reference->setFromTarget(*fromTarget);
}
void Linker::resolveReferences()
{
for (unsigned int j=0; j < fAllAtoms.size(); ++j) {
ObjectFile::Atom* atom = fAllAtoms[j];
std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
ObjectFile::Reference* reference = *it;
if ( reference->isTargetUnbound() )
this->resolve(reference);
if ( reference->hasFromTarget() && reference->isFromTargetUnbound() )
this->resolveFrom(reference);
}
}
}
class InSet
{
public:
InSet(std::set<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {}
bool operator()(ObjectFile::Atom*& atom) const {
return ( fDeadAtoms.count(atom) != 0 );
}
private:
std::set<ObjectFile::Atom*>& fDeadAtoms;
};
void Linker::deadStrip()
{
fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end());
}
void Linker::sortAtoms()
{
Section::assignIndexes();
std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter());
}
void Linker::tweakLayout()
{
}
void Linker::writeOutput()
{
ObjectFile::Atom* entryPoint = NULL;
switch ( fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kDyld:
entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
if ( entryPoint == NULL ) {
throwf("could not find entry point: %s", fOptions.entryName());
}
break;
case Options::kDynamicLibrary:
if ( fOptions.initFunctionName() != NULL ) {
entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName());
if ( entryPoint == NULL ) {
throwf("could not find -init function: %s", fOptions.initFunctionName());
}
}
break;
case Options::kObjectFile:
case Options::kDynamicBundle:
entryPoint = NULL;
break;
}
fOutputFile->write(fAllAtoms, entryPoint);
}
ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info)
{
uint64_t len = info.fileLen;
int fd = ::open(info.path, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open file, errno=%d", errno);
if ( info.fileLen < 20 )
throw "file too small";
char* p = (char*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if ( p == (char*)(-1) )
throwf("can't map file, errno=%d", errno);
::close(fd);
const mach_header* mh = (mach_header*)p;
if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
const struct fat_header* fh = (struct fat_header*)p;
const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fOptions.architecture() ) {
mh = (struct mach_header*)((char*)p + OSSwapBigToHostInt32(archs[i].offset));
len = OSSwapBigToHostInt32(archs[i].size);
break;
}
}
}
if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
const char* archName = "unknown";
switch (fOptions.architecture()) {
case CPU_TYPE_POWERPC:
archName = "ppc";
break;
case CPU_TYPE_POWERPC64:
archName = "ppc64";
break;
case CPU_TYPE_I386:
archName = "i386";
break;
}
throwf("missing required architecture %s in fat file", archName);
}
cpu_type_t cpuType = 0;
uint32_t fileType = 0;
if ( mh->magic == MH_MAGIC ) {
fileType = mh->filetype;
cpuType = mh->cputype;
}
else if ( mh->magic == OSSwapInt32(MH_MAGIC) ) {
fileType = OSSwapInt32(mh->filetype);
cpuType = OSSwapInt32(mh->cputype);
}
else if ( mh->magic == MH_MAGIC_64 ) {
fileType = ((mach_header_64*)mh)->filetype;
cpuType = ((mach_header_64*)mh)->cputype;
}
else if ( mh->magic == OSSwapInt32(MH_MAGIC_64) ) {
fileType = OSSwapInt32(((mach_header_64*)mh)->filetype);
cpuType = OSSwapInt32(((mach_header_64*)mh)->cputype);
}
else if ( strncmp((const char*)mh, "!<arch>\n", 8) == 0 ) {
switch ( fOptions.architecture() ) {
case CPU_TYPE_POWERPC:
return ppc::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions());
case CPU_TYPE_POWERPC64:
return ppc64::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions());
case CPU_TYPE_I386:
return i386::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions());
}
throw "no matching archive reader";
}
else {
throw "unknown file type";
}
if ( fOptions.architecture() == cpuType ) {
if ( fileType == MH_OBJECT ) {
switch ( cpuType ) {
case CPU_TYPE_POWERPC:
return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions());
case CPU_TYPE_POWERPC64:
return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions());
case CPU_TYPE_I386:
return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions());
default:
throw "wrong architecture in object file";
}
}
else if ( (fileType == MH_DYLIB) || (fileType == MH_DYLIB_STUB) ) {
ObjectFile::Reader* dylibReader = NULL;
switch ( cpuType ) {
case CPU_TYPE_POWERPC:
dylibReader = ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions());
break;
case CPU_TYPE_POWERPC64:
dylibReader = ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions());
break;
case CPU_TYPE_I386:
dylibReader = i386::ObjectFileDylibMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions());
break;
default:
throw "wrong architecture in dylib";
}
this->addDylib(dylibReader, info);
return dylibReader;
}
throw "unknown mach-o file type";
}
else {
throw "file does not contain requested architecture";
}
}
void Linker::createReaders()
{
std::vector<Options::FileInfo>& files = fOptions.getInputFiles();
const int count = files.size();
if ( count == 0 )
throw "no object files specified";
for (int i=0; i < count; ++i) {
Options::FileInfo& entry = files[i];
if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) {
try {
this->addInputFile(this->createReader(entry));
}
catch (const char* msg) {
if ( strstr(msg, "architecture") != NULL ) {
if ( fOptions.ignoreOtherArchInputFiles() ) {
}
else {
fprintf(stderr, "ld64 warning: in %s, %s\n", entry.path, msg);
}
}
else {
throwf("in %s, %s", entry.path, msg);
}
}
}
}
fDirectLibrariesComplete = true;
for (std::vector<ExecutableFile::DyLibUsed>::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) {
this->addIndirectLibraries(it->reader);
}
switch ( fOptions.nameSpace() ) {
case Options::kFlatNameSpace:
case Options::kForceFlatNameSpace:
for (std::list<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
try {
it->reader = this->createReader(fOptions.findFile(it->path));
}
catch (const char* msg) {
fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg);
}
}
break;
case Options::kTwoLevelNameSpace:
{
bool indirectAdded = true;
while ( indirectAdded ) {
indirectAdded = false;
for (std::list<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
if ( it->reader == NULL ) {
try {
it->reader = this->createReader(fOptions.findFile(it->path));
indirectAdded = true;
}
catch (const char* msg) {
fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg);
}
}
if ( (it->reader != NULL) && (it->reExportParent == NULL) ) {
for (std::set<ObjectFile::Reader*>::iterator pit=it->parents.begin(); pit != it->parents.end(); pit++) {
if ( (*pit)->reExports(it->reader) ) {
it->reExportParent = *pit;
break;
}
}
}
}
}
}
break;
}
for (std::list<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
if ( (it->reExportParent != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace) ) {
ExecutableFile::DyLibUsed dylibInfo;
dylibInfo.reader = it->reader;
dylibInfo.options.fWeakImport = false;
dylibInfo.options.fReExport = false;
dylibInfo.options.fInstallPathOverride = NULL;
dylibInfo.indirect = true;
dylibInfo.directReader = it->reExportParent;
fDynamicLibraries.push_back(dylibInfo);
if ( fOptions.readerOptions().fTraceIndirectDylibs )
printf("[Logging for Build & Integration] Used indirect dynamic library: %s\n", it->reader->getPath());
}
}
}
void Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info)
{
if ( fDirectLibrariesComplete ) {
this->addIndirectLibraries(reader);
}
else {
if ( fOptions.readerOptions().fTraceDylibs )
printf("[Logging for Build & Integration] Used dynamic library: %s\n", reader->getPath());
ExecutableFile::DyLibUsed dylibInfo;
dylibInfo.reader = reader;
dylibInfo.options = info.options;
dylibInfo.indirect = false;
dylibInfo.directReader = NULL;
fDynamicLibraries.push_back(dylibInfo);
}
}
void Linker::addIndirectLibraries(ObjectFile::Reader* reader)
{
std::vector<const char*>* dependentLibs = reader->getDependentLibraryPaths();
if ( dependentLibs != NULL ) {
for (std::vector<const char*>::iterator it=dependentLibs->begin(); it != dependentLibs->end(); it++) {
if ( this->haveDirectLibrary(*it) ) {
}
else if ( this->haveIndirectLibrary(*it, reader) ) {
}
else {
IndirectLibrary indirectLib;
indirectLib.path = *it;
indirectLib.fileLen = 0;
indirectLib.reader = NULL;
indirectLib.parents.insert(reader);
indirectLib.reExportParent = NULL;
fIndirectDynamicLibraries.push_back(indirectLib);
}
}
}
}
bool Linker::haveIndirectLibrary(const char* path, ObjectFile::Reader* parentReader)
{
for (std::list<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
if ( strcmp(path, it->path) == 0 ) {
it->parents.insert(parentReader);
return true;
}
if ( it->reader != NULL ) {
const char* installPath = it->reader->getInstallPath();
if ( (installPath != NULL) && (strcmp(path, installPath) == 0) )
return true;
}
}
return false;
}
bool Linker::haveDirectLibrary(const char* path)
{
for (std::vector<ExecutableFile::DyLibUsed>::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) {
if ( strcmp(path, it->reader->getPath()) == 0 )
return true;
const char* installPath = it->reader->getInstallPath();
if ( (installPath != NULL) && (strcmp(path, installPath) == 0) )
return true;
}
return false;
}
void Linker::createWriter()
{
const char* path = fOptions.getOutputFilePath();
switch ( fOptions.architecture() ) {
case CPU_TYPE_POWERPC:
this->setOutputFile(ppc::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries));
break;
case CPU_TYPE_POWERPC64:
this->setOutputFile(ppc64::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries));
break;
case CPU_TYPE_I386:
this->setOutputFile(i386::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries));
break;
default:
throw "unknown architecture";
}
}
Linker::SymbolTable::SymbolTable(Linker& owner)
: fOwner(owner), fRequireCount(0)
{
}
void Linker::SymbolTable::require(const char* name)
{
Mapper::iterator pos = fTable.find(name);
if ( pos == fTable.end() ) {
fTable[name] = NULL;
++fRequireCount;
}
}
bool Linker::SymbolTable::add(ObjectFile::Atom& atom)
{
const bool log = false;
const char* name = atom.getName();
Mapper::iterator pos = fTable.find(name);
if ( pos != fTable.end() ) {
ObjectFile::Atom* existingAtom = pos->second;
if ( existingAtom != NULL ) {
if ( existingAtom->isTentativeDefinition() ) {
if ( atom.isTentativeDefinition() ) {
if ( atom.getSize() > existingAtom->getSize() ) {
if ( fOwner.fOptions.warnCommons() )
fprintf(stderr, "ld64: replacing common symbol %s size %lld from %s with larger symbol size %lld from %s\n",
existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath());
fOwner.fDeadAtoms.insert(existingAtom);
fTable[name] = &atom;
return true;
}
else {
if ( fOwner.fOptions.warnCommons() ) {
if ( atom.getSize() == existingAtom->getSize() )
fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have common from %s with same size\n",
atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath());
else
fprintf(stderr, "ld64: ignoring common symbol %s size %lld from %s because already have larger symbol size %lld from %s\n",
atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getSize(), existingAtom->getFile()->getPath());
}
fOwner.fDeadAtoms.insert(&atom);
return false;
}
}
else {
if ( atom.isImportProxy() ) {
switch ( fOwner.fOptions.commonsMode() ) {
case Options::kCommonsIgnoreDylibs:
if ( fOwner.fOptions.warnCommons() )
fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n",
existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath());
fOwner.fDeadAtoms.insert(&atom);
return false;
case Options::kCommonsOverriddenByDylibs:
if ( fOwner.fOptions.warnCommons() )
fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n",
existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath());
fOwner.fDeadAtoms.insert(existingAtom);
fTable[name] = &atom;
return true;
case Options::kCommonsConflictsDylibsError:
throwf("common symbol %s from %s conflicts with defintion from dylib %s",
existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath());
}
}
else {
if ( fOwner.fOptions.warnCommons() ) {
if ( atom.getSize() < existingAtom->getSize() )
fprintf(stderr, "ld64: warning: replacing common symbol %s size %lld from %s with smaller true definition size %lld from %s\n",
existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath());
else
fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n",
existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath());
}
fOwner.fDeadAtoms.insert(existingAtom);
fTable[name] = &atom;
return true;
}
}
}
else if ( atom.isTentativeDefinition() ) {
if ( fOwner.fOptions.warnCommons() ) {
if ( atom.getSize() > existingAtom->getSize() )
fprintf(stderr, "ld64: warning: ignoring common symbol %s size %lld from %s because already have definition from %s size %lld, even though definition is smaller\n",
atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getFile()->getPath(), existingAtom->getSize());
else
fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have definition from %s\n",
atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath());
}
fOwner.fDeadAtoms.insert(&atom);
return false;
}
else {
if ( existingAtom->isWeakDefinition() ) {
if ( atom.isImportProxy() ) {
if ( log ) fprintf(stderr, "keep weak atom even though also in a dylib: %s\n", atom.getName());
fOwner.fDeadAtoms.insert(&atom);
return false;
}
else if ( atom.isWeakDefinition() ) {
if ( log ) fprintf(stderr, "already have weak atom: %s\n", atom.getName());
fOwner.fDeadAtoms.insert(&atom);
return false;
}
else {
if ( log ) fprintf(stderr, "replacing weak atom %p from %s with %p from %s: %s\n", existingAtom, existingAtom->getFile()->getPath(), &atom, atom.getFile()->getPath(), atom.getName());
fOwner.fDeadAtoms.insert(existingAtom);
fTable[name] = &atom;
return true;
}
}
}
if ( atom.isWeakDefinition() ) {
return false;
}
if ( atom.isCoalesableByName() && existingAtom->isCoalesableByName() ) {
return false;
}
fprintf(stderr, "duplicate symbol %s in %s and %s\n", name, atom.getFile()->getPath(), existingAtom->getFile()->getPath());
}
}
fTable[name] = &atom;
return true;
}
ObjectFile::Atom* Linker::SymbolTable::find(const char* name)
{
Mapper::iterator pos = fTable.find(name);
if ( pos != fTable.end() ) {
return pos->second;
}
return NULL;
}
void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector<const char*>& undefines)
{
for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) {
if ( (it->second == NULL) || (andWeakDefintions && it->second->isWeakDefinition()) ) {
undefines.push_back(it->first);
}
}
}
bool Linker::AtomSorter::operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
{
unsigned int leftSectionIndex = left->getSection()->getIndex();
unsigned int rightSectionIndex = right->getSection()->getIndex();
if ( leftSectionIndex != rightSectionIndex)
return (leftSectionIndex < rightSectionIndex);
return left->getSortOrder() < right->getSortOrder();
}
int main(int argc, const char* argv[])
{
try {
Linker ld(argc, argv);
ld.createReaders();
ld.createWriter();
ld.link();
}
catch (const char* msg) {
fprintf(stderr, "ld64 failed: %s\n", msg);
return 1;
}
return 0;
}