X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/d696c285d331ab577dcabd00419d8c30336673da..183622f619ad30b834c5e6a73fa00244d5b035c0:/src/ld.cpp?ds=sidebyside diff --git a/src/ld.cpp b/src/ld.cpp index e98df10..a2a56b9 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -1,6 +1,5 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,21 +27,25 @@ #include #include #include +#include #include #include #include #include #include #include - +#include #include +#include #include #include #include #include #include #include +#include +#include #include "Options.h" @@ -53,93 +56,14 @@ #include "MachOReaderDylib.hpp" #include "MachOWriterExecutable.hpp" -#include "SectCreate.h" - -#if 0 -static void dumpAtom(ObjectFile::Atom* atom) -{ - //printf("atom: %p\n", atom); - - // name - printf("name: %s\n", atom->getDisplayName()); - - // scope - 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"); - } - - // kind - switch ( atom->getDefinitinonKind() ) { - case ObjectFile::Atom::kRegularDefinition: - printf("kind: regular\n"); - break; - case ObjectFile::Atom::kWeakDefinition: - printf("kind: weak\n"); - break; - case ObjectFile::Atom::kTentativeDefinition: - printf("kind: tentative\n"); - break; - case ObjectFile::Atom::kExternalDefinition: - printf("kind: import\n"); - break; - case ObjectFile::Atom::kExternalWeakDefinition: - printf("kind: weak import\n"); - break; - default: - printf("scope: unknown\n"); - } - - // segment and section - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); - - // attributes - printf("attrs: "); - if ( atom->dontDeadStrip() ) - printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); - printf("\n"); - - // size - printf("size: 0x%012llX\n", atom->getSize()); - - // content - 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"); - - // references - std::vector& 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()); - } - - // attributes +#define LLVM_SUPPORT 0 + +#if LLVM_SUPPORT +#include "LLVMReader.hpp" +#endif -} +#include "OpaqueSection.hpp" -#endif class CStringComparor { @@ -158,7 +82,7 @@ class Section : public ObjectFile::Section public: static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); static void assignIndexes(); - + const char* getName() { return fSectionName; } private: Section(const char* sectionName, const char* segmentName, bool zeroFill); @@ -167,6 +91,7 @@ private: bool operator()(Section* left, Section* right); }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; //typedef std::map NameToSection; @@ -176,15 +101,18 @@ private: static NameToSection fgMapping; static std::vector fgSections; + static NameToOrdinal fgSegmentDiscoverOrder; }; Section::NameToSection Section::fgMapping; std::vector Section::fgSections; +Section::NameToOrdinal Section::fgSegmentDiscoverOrder; Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) { - //fprintf(stderr, "new Section(%s, %s)\n", sectionName, segmentName); + this->fIndex = fgSections.size(); + //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); } Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) @@ -202,7 +130,6 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze // does not exist, so make a new one Section* sect = new Section(sectionName, segmentName, zeroFill); - sect->fIndex = fgMapping.size(); fgMapping[sectionName] = sect; fgSections.push_back(sect); @@ -211,6 +138,10 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze find("__textcoal_nt", "__TEXT", false); } + // remember segment discovery order + if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) + fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); + return sect; } @@ -224,29 +155,24 @@ int Section::Sorter::segmentOrdinal(const char* segName) return 3; if ( strcmp(segName, "__OBJC") == 0 ) return 4; + if ( strcmp(segName, "__OBJC2") == 0 ) + return 5; if ( strcmp(segName, "__LINKEDIT") == 0 ) return INT_MAX; // linkedit segment should always sort last else - return 5; + return fgSegmentDiscoverOrder[segName]+6; } bool Section::Sorter::operator()(Section* left, Section* right) { // Segment is primary sort key - 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; + int leftSegOrdinal = segmentOrdinal(left->fSegmentName); + int rightSegOrdinal = segmentOrdinal(right->fSegmentName); + if ( leftSegOrdinal < rightSegOrdinal ) + return true; + if ( leftSegOrdinal > rightSegOrdinal ) return false; - } // zerofill section sort to the end if ( !left->fZeroFill && right->fZeroFill ) @@ -259,8 +185,8 @@ bool Section::Sorter::operator()(Section* left, Section* right) } void Section::assignIndexes() -{ - //printf("unsorted:\n"); +{ + //printf("unsorted sections:\n"); //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); //} @@ -273,63 +199,89 @@ void Section::assignIndexes() for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) (*it)->fIndex = newOrder++; - //printf("sorted:\n"); + //printf("sorted sections:\n"); //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: name=%s\n", (*it)->fSectionName); + // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); //} } -class Linker { +class Linker : public ObjectFile::Reader::DylibHander { public: Linker(int argc, const char* argv[]); + const char* getArchPrefix(); + const char* architectureName(); + bool showArchitectureInErrors(); + bool isInferredArchitecture(); void createReaders(); void createWriter(); - void addInputFile(ObjectFile::Reader* reader); + void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); void setOutputFile(ExecutableFile::Writer* writer); void link(); - + void optimize(); + + // implemenation from ObjectFile::Reader::DylibHander + virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const char* name; + }; + ObjectFile::Reader* createReader(const Options::FileInfo&); void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); + void processDylibs(); + void updateContraints(ObjectFile::Reader* reader); + void loadAndResolve(); + void processDTrace(); + void checkObjC(); void loadUndefines(); + void checkUndefines(); void addWeakAtomOverrides(); void resolveReferences(); - void deadStrip(); + void deadStripResolve(); + void addLiveRoot(const char* name); + ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); + void logArchive(ObjectFile::Reader* reader); + void sortSections(); void sortAtoms(); void tweakLayout(); void writeDotOutput(); static bool minimizeStab(ObjectFile::Reader::Stab& stab); static const char* truncateStabString(const char* str); - void collectStabs(); + void collectDebugInfo(); void writeOutput(); ObjectFile::Atom* entryPoint(); ObjectFile::Atom* dyldHelper(); const char* assureFullPath(const char* path); - void markLive(ObjectFile::Atom* atom, std::set& liveAtoms); - void collectStabs(ObjectFile::Reader* reader); - void synthesizeStabs(ObjectFile::Reader* reader); + void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); + void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); + void synthesizeDebugNotes(std::vector& allAtomsByReader); void printStatistics(); void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); char* commatize(uint64_t in, char* out); void getVMInfo(vm_statistics_data_t& info); cpu_type_t inferArchitecture(); - - void resolve(ObjectFile::Reference* reference); - void resolveFrom(ObjectFile::Reference* reference); - void addJustInTimeAtoms(const char* name); + void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName); + void checkDylibClientRestrictions(ObjectFile::Reader* reader); + void logDylib(ObjectFile::Reader* reader, bool indirect); + + void resolve(ObjectFile::Reference* reference); + void resolveFrom(ObjectFile::Reference* reference); + std::vector* addJustInTimeAtoms(const char* name); + void addJustInTimeAtomsAndMarkLive(const char* name); ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - void addIndirectLibraries(ObjectFile::Reader* reader); - bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader); - bool haveDirectLibrary(const char* path); void logTraceInfo(const char* format, ...); + + class SymbolTable { public: @@ -339,20 +291,34 @@ private: ObjectFile::Atom* find(const char* name); unsigned int getRequireCount() { return fRequireCount; } void getNeededNames(bool andWeakDefintions, std::vector& undefines); - private: typedef __gnu_cxx::hash_map, CStringEquals> Mapper; + private: Linker& fOwner; Mapper fTable; unsigned int fRequireCount; }; - struct AtomSorter + class AtomSorter { - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right); + public: + AtomSorter(std::map* map) : fOverriddenOrdinalMap(map) {} + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); + private: + std::map* fOverriddenOrdinalMap; }; typedef std::map SectionOrder; + struct DTraceProbeInfo { + DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} + const ObjectFile::Atom* atom; + uint32_t offset; + const char* probeName; + }; + typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToReader; + struct IndirectLibrary { const char* path; uint64_t fileLen; @@ -365,29 +331,44 @@ private: Options fOptions; SymbolTable fGlobalSymbolTable; - unsigned int fWeakSymbolsAddedCount; + uint32_t fNextInputOrdinal; std::vector fInputFiles; ExecutableFile::Writer* fOutputFile; - std::vector fDynamicLibraries; - std::list fIndirectDynamicLibraries; + InstallNameToReader fDylibMap; + std::map fDylibOptionsMap; + std::set fDylibsProcessed; + ObjectFile::Reader* fBundleLoaderReader; std::vector fReadersThatHaveSuppliedAtoms; std::vector fAllAtoms; + std::set fArchiveReaders; + std::set fArchiveReadersLogged; std::set fDeadAtoms; + std::set fLiveAtoms; + std::set fLiveRootAtoms; std::vector fStabs; + std::vector fAtomsWithUnresolvedReferences; + std::vector fDtraceProbes; + std::vector fDtraceProbeSites; + std::vector fDtraceIsEnabledSites; + std::map fDtraceAtomToTypes; bool fCreateUUID; + bool fCanScatter; SectionOrder fSectionOrder; - unsigned int fNextSortOrder; - unsigned int fNextObjectFileOrder; cpu_type_t fArchitecture; + const char* fArchitectureName; + bool fArchitectureInferred; bool fDirectLibrariesComplete; + bool fBiggerThanTwoGigOutput; uint64_t fOutputFileSize; + uint64_t fTotalZeroFillSize; + uint64_t fTotalSize; uint64_t fStartTime; uint64_t fStartCreateReadersTime; uint64_t fStartCreateWriterTime; uint64_t fStartBuildAtomsTime; - uint64_t fStartLoadUndefinesTime; - uint64_t fStartResolveTime; + uint64_t fStartLoadAndResolveTime; uint64_t fStartSortTime; + uint64_t fStartDebugTime; uint64_t fStartWriteTime; uint64_t fEndTime; uint64_t fTotalObjectSize; @@ -396,28 +377,69 @@ private: uint32_t fTotalArchivesLoaded; uint32_t fTotalDylibsLoaded; vm_statistics_data_t fStartVMInfo; + ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; + ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; + bool fObjcReplacmentClasses; + bool fAllDirectDylibsLoaded; }; Linker::Linker(int argc, const char* argv[]) - : fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fCreateUUID(false), fNextSortOrder(1), - fNextObjectFileOrder(1), fArchitecture(0), fDirectLibrariesComplete(false), fOutputFileSize(0), fTotalObjectSize(0), - fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), + fCreateUUID(false), fCanScatter(true), + fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), + fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), + fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), + fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), + fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) { fStartTime = mach_absolute_time(); if ( fOptions.printStatistics() ) getVMInfo(fStartVMInfo); - + fArchitecture = fOptions.architecture(); if ( fArchitecture == 0 ) { // -arch not specified, scan .o files to figure out what it should be fArchitecture = inferArchitecture(); + fArchitectureInferred = true; + } + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + fArchitectureName = "ppc"; + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + break; + default: + fArchitectureName = "unknown architecture"; + break; } } +const char* Linker::architectureName() +{ + return fArchitectureName; +} + +bool Linker::showArchitectureInErrors() +{ + return fOptions.printArchPrefix(); +} + +bool Linker::isInferredArchitecture() +{ + return fArchitectureInferred; +} + cpu_type_t Linker::inferArchitecture() { - // scan all input files, looking for a thin .o file. + // scan all input files, looking for a thin .o file. // the first one found is presumably the architecture to link uint8_t buffer[sizeof(mach_header_64)]; std::vector& files = fOptions.getInputFiles(); @@ -428,39 +450,45 @@ cpu_type_t Linker::inferArchitecture() ::close(fd); if ( amount >= (ssize_t)sizeof(buffer) ) { if ( mach_o::relocatable::Reader::validFile(buffer) ) { - fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch ppc based on %s\n", it->path); return CPU_TYPE_POWERPC; } else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc64 based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch ppc64 based on %s\n", it->path); return CPU_TYPE_POWERPC64; } else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - fprintf(stderr, "ld64 warning: -arch not used, infering -arch i386 based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch i386 based on %s\n", it->path); return CPU_TYPE_I386; } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //fprintf(stderr, "ld: warning -arch not used, infering -arch x86_64 based on %s\n", it->path); + return CPU_TYPE_X86_64; + } } } } - + // no thin .o files found, so default to same architecture this was built as - fprintf(stderr, "ld64 warning: -arch not specified\n"); + fprintf(stderr, "ld: warning -arch not specified\n"); #if __ppc__ return CPU_TYPE_POWERPC; #elif __i386__ return CPU_TYPE_I386; #elif __ppc64__ return CPU_TYPE_POWERPC64; +#elif __x86_64__ + return CPU_TYPE_X86_64; #else #error unknown default architecture #endif } -void Linker::addInputFile(ObjectFile::Reader* reader) +void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) { - reader->setSortOrder(fNextObjectFileOrder++); fInputFiles.push_back(reader); + fDylibOptionsMap[reader] = info.options; } void Linker::setOutputFile(ExecutableFile::Writer* writer) @@ -468,16 +496,86 @@ void Linker::setOutputFile(ExecutableFile::Writer* writer) fOutputFile = writer; } +class InSet +{ +public: + InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fDeadAtoms.count(atom) != 0 ); + } + +private: + std::set& fDeadAtoms; +}; + +void Linker::loadAndResolve() +{ + fStartLoadAndResolveTime = mach_absolute_time(); + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // without dead-code-stripping: + // find atoms to resolve all undefines + this->loadUndefines(); + // verify nothing is missing + this->checkUndefines(); + // once all undefines fulfill, then bind all references + this->resolveReferences(); + // remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } + else { + // with dead code stripping: + // start binding references from roots, + this->deadStripResolve(); + // verify nothing is missing + this->checkUndefines(); + } +} + +void Linker::optimize() +{ + std::vector newAtoms; + + const int readerCount = fInputFiles.size(); + for (int i=0; i < readerCount; ++i) { + fInputFiles[i]->optimize(fAllAtoms, newAtoms, fNextInputOrdinal); + } + // note: When writer start generating stubs and non-lazy-pointers for all architecture, do not insert + // newAtoms into fGlobalSymbolTable. Instead directly insert them in fAllAtoms and set their order appropriately. + this->addAtoms(newAtoms); + + // Some of the optimized atoms may not have identified section properly, if they + // were created before optimizer produces corrosponding real atom. Here, input + // file readers are not able to patch it themselves because Section::find() is + // linker specific. + for(std::vector::iterator itr = fAllAtoms.begin(); + itr != fAllAtoms.end(); ++itr) { + + ObjectFile::Atom *atom = *itr; + if (atom->getSectionName() && !atom->getSection()) + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + } + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + fLiveAtoms.clear(); + deadStripResolve(); + } + else + resolveReferences(); +} + void Linker::link() { this->buildAtomList(); - this->loadUndefines(); - this->resolveReferences(); - this->deadStrip(); - this->sortAtoms(); + this->loadAndResolve(); + this->optimize(); + this->checkObjC(); + this->processDTrace(); this->tweakLayout(); + this->sortSections(); + this->sortAtoms(); this->writeDotOutput(); - this->collectStabs(); + this->collectDebugInfo(); this->writeOutput(); this->printStatistics(); @@ -491,7 +589,7 @@ void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) if ( sUnitsPerSecond == 0 ) { struct mach_timebase_info timeBaseInfo; if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000LL * timeBaseInfo.denom / timeBaseInfo.numer; + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); } } @@ -545,14 +643,14 @@ void Linker::printStatistics() getVMInfo(endVMInfo); uint64_t totalTime = fEndTime - fStartTime; - printTime("ld64 total time", totalTime, totalTime); + printTime("ld total time", totalTime, totalTime); printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); - printTime(" build atom list", fStartLoadUndefinesTime - fStartBuildAtomsTime, totalTime); - printTime(" load undefines", fStartResolveTime - fStartLoadUndefinesTime, totalTime); - printTime(" resolve references", fStartSortTime - fStartResolveTime, totalTime); - printTime(" sort output", fStartWriteTime - fStartSortTime, totalTime); + printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); + printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); + printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); + printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); printTime(" write output", fEndTime - fStartWriteTime, totalTime); fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); @@ -569,23 +667,34 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) // add to list of all atoms fAllAtoms.push_back(&atom); - // add atom's references's names to symbol table as to-be-resolved-later - std::vector& references = atom.getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->isTargetUnbound() ) { - fGlobalSymbolTable.require(reference->getTargetName()); + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getTargetName()); + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getFromTargetName()); + if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind ) + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + } + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); } - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) - fGlobalSymbolTable.require(reference->getFromTargetName()); + } + else { + if ( atom.dontDeadStrip() ) + fLiveRootAtoms.insert(&atom); } // if in global namespace, add atom itself to symbol table ObjectFile::Atom::Scope scope = atom.getScope(); const char* name = atom.getName(); if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { - fGlobalSymbolTable.add(atom); - // update scope based on export list (possible that globals are downgraded to private_extern) if ( (scope == ObjectFile::Atom::scopeGlobal) && fOptions.hasExportRestrictList() ) { bool doExport = fOptions.shouldExport(name); @@ -593,33 +702,107 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) atom.setScope(ObjectFile::Atom::scopeLinkageUnit); } } + // add to symbol table + fGlobalSymbolTable.add(atom); } // record section orders so output file can have same order - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); + if (atom.getSectionName()) + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); +} + +void Linker::updateContraints(ObjectFile::Reader* reader) +{ + // check objc objects were compiled compatibly + ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); + if ( reader->getInstallPath() == NULL ) { + // adding a .o file + switch ( fCurrentObjCConstraint ) { + case ObjectFile::Reader::kObjcNone: + fCurrentObjCConstraint = objcAddition; + break; + case ObjectFile::Reader::kObjcRetainRelease: + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + case ObjectFile::Reader::kObjcGC: + if ( (fCurrentObjCConstraint != objcAddition) && (objcAddition != ObjectFile::Reader::kObjcNone) ) + throwf("%s built with different Garbage Collection settings", reader->getPath()); + break; + } + } + if ( reader->objcReplacementClasses() ) + fObjcReplacmentClasses = true; - // assign order in which this atom was originally seen - if ( atom.getSortOrder() == 0 ) - fNextSortOrder = atom.setSortOrder(fNextSortOrder); + // check cpu sub-types + ObjectFile::Reader::CpuConstraint cpuAddition = reader->getCpuConstraint(); + switch ( fCurrentCpuConstraint ) { + case ObjectFile::Reader::kCpuAny: + fCurrentCpuConstraint = cpuAddition; + break; + case ObjectFile::Reader::kCpuG3: + switch ( cpuAddition ) { + case ObjectFile::Reader::kCpuAny: + case ObjectFile::Reader::kCpuG3: + break; + case ObjectFile::Reader::kCpuG4: + case ObjectFile::Reader::kCpuG5: + // previous file for G3 this one is more contrained, use it + fCurrentCpuConstraint = cpuAddition; + break; + } + break; + case ObjectFile::Reader::kCpuG4: + switch ( cpuAddition ) { + case ObjectFile::Reader::kCpuAny: + case ObjectFile::Reader::kCpuG3: + case ObjectFile::Reader::kCpuG4: + break; + case ObjectFile::Reader::kCpuG5: + // previous file for G5 this one is more contrained, use it + fCurrentCpuConstraint = cpuAddition; + break; + } + break; + case ObjectFile::Reader::kCpuG5: + // G5 can run everything + break; + } } inline void Linker::addAtoms(std::vector& atoms) { - bool first = true; // assume all atoms are from same reader + bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; + bool first = true; for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { - if ( first ) { + // usually we only need to get the first atom's reader, but + // with -all_load all atoms from all .o files come come back together + // so we need to scan all atoms + if ( first || scanAll ) { // update fReadersThatHaveSuppliedAtoms ObjectFile::Reader* reader = (*it)->getFile(); if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) == fReadersThatHaveSuppliedAtoms.end() ) { fReadersThatHaveSuppliedAtoms.push_back(reader); - } + updateContraints(reader); + } } this->addAtom(**it); first = false; } } +void Linker::logArchive(ObjectFile::Reader* reader) +{ + if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { + fArchiveReadersLogged.insert(reader); + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } +} + + void Linker::buildAtomList() { fStartBuildAtomsTime = mach_absolute_time(); @@ -633,15 +816,19 @@ void Linker::buildAtomList() this->addAtoms(fOutputFile->getAtoms()); // each reader contributes atoms - const int readerCount = fInputFiles.size(); - for (int i=0; i < readerCount; ++i) { - this->addAtoms(fInputFiles[i]->getAtoms()); + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + std::vector& atoms = reader->getAtoms(); + this->addAtoms(atoms); + if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) + logArchive(reader); } // extra command line section always at end std::vector& extraSections = fOptions.extraSections(); for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { - this->addAtoms(SectCreate::MakeReader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)->getAtoms()); + this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); + fNextInputOrdinal += it->dataLen; } } @@ -656,7 +843,6 @@ static const char* pathLeafName(const char* path) void Linker::loadUndefines() { - fStartLoadUndefinesTime = mach_absolute_time(); // keep looping until no more undefines were added in last loop unsigned int undefineCount = 0xFFFFFFFF; while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { @@ -667,133 +853,199 @@ void Linker::loadUndefines() const char* name = *it; ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); if ( (possibleAtom == NULL) - || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) && (fOptions.outputKind() != Options::kObjectFile) && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) - this->addJustInTimeAtoms(name); + || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) + && (fOptions.outputKind() != Options::kObjectFile) + && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) { + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) + delete atoms; + } } } +} - if ( fOptions.outputKind() != Options::kObjectFile ) { - // error out on any remaining undefines - 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; +// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names +class ExportedObjcClass +{ +public: + ExportedObjcClass(Options& opt) : fOptions(opt) {} + + bool operator()(const char* name) const { + if ( fOptions.shouldExport(name) ) { + if ( strncmp(name, ".objc_class_name_", 17) == 0 ) + return true; + if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) + return true; + if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) + return true; } - std::vector 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]; - fprintf(stderr, " %s, referenced from:\n", name); - // scan all atoms for references - bool foundAtomReference = false; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->isTargetUnbound() ) { - if ( strcmp(reference->getTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } + //fprintf(stderr, "%s is not exported\n", name); + return false; + } +private: + Options& fOptions; +}; + + +void Linker::checkUndefines() +{ + // error out on any remaining undefines + 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 unresolvableUndefines; + fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); + + // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names + // ignore unresolved references to Objc class names that are listed in -exported_symbols_list + if ( fOptions.hasExportRestrictList() ) + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); + + const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + if ( fOptions.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); + else + fprintf(stderr, "Undefined symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + fprintf(stderr, " \"%s\", referenced from:\n", name); + // scan all atoms for references + bool foundAtomReference = false; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; } - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) { - if ( strcmp(reference->getFromTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getFromTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; } } } - // scan command line options - if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbols_list command line option\n"); - ++unresolvableExportsCount; - } + } + // scan command line options + if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbols_list command line option\n"); + ++unresolvableExportsCount; } } - if ( doError && (unresolvableCount > unresolvableExportsCount) ) // last check should be removed. It exists so broken projects still build - throw "symbol(s) not found"; - } - - // now verify that -init routine exists - if ( fOptions.initFunctionName() != NULL ) { - if ( fGlobalSymbolTable.find(fOptions.initFunctionName()) == NULL ) - throwf("symbol %s not found for -init", fOptions.initFunctionName()); } + if ( doError ) + throw "symbol(s) not found"; } } -void Linker::addJustInTimeAtoms(const char* name) +std::vector* Linker::addJustInTimeAtoms(const char* name) { // when creating final linked image, writer gets first chance if ( fOptions.outputKind() != Options::kObjectFile ) { std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); - delete atoms; //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); - return; // found a definition, no need to search anymore + return atoms; // found a definition, no need to search anymore } } - // give direct readers a chance + // give readers a chance for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { ObjectFile::Reader* reader = *it; if ( reader != NULL ) { // if this reader is a static archive that has the symbol we need, pull in all atoms in that module // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); - delete atoms; - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fInputFiles[i]->getPath() ); - return; // found a definition, no need to search anymore + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + if ( fOptions.readerOptions().fTraceArchives ) { + logArchive(reader); + } + // if this is a weak definition in a dylib + if ( (atoms->size() == 1) && (reader->getInstallPath() != NULL) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } } } } - // give indirect readers a chance - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - ObjectFile::Reader* reader = it->reader; - if ( reader != NULL ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - delete atoms; - return; // found a definition, no need to search anymore + // for two level namesapce, give all implicitly link dylibs a chance + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } + } + } + } + } + + // for flat namespace, give indirect dylibs + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( ! it->second->explicitlyLinked() ) { + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + return atoms; // found a definition, no need to search anymore + } } } } // when creating .o file, writer goes last (this is so any static archives will be searched above) - if ( (fOptions.outputKind() == Options::kObjectFile) || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) { + if ( (fOptions.outputKind() == Options::kObjectFile) + || (fOptions.undefinedTreatment() != Options::kUndefinedError) + || fOptions.someAllowedUndefines() ) { ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); if ( atom != NULL ) { this->addAtom(*atom); - return; + return NULL; } } //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); + return NULL; } void Linker::resolve(ObjectFile::Reference* reference) @@ -802,7 +1054,7 @@ void Linker::resolve(ObjectFile::Reference* reference) const char* targetName = reference->getTargetName(); ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); if ( target == NULL ) { - fprintf(stderr, "can't resolve: %s\n", targetName); + fprintf(stderr, "Undefined symbol: %s\n", targetName); } reference->setTarget(*target, reference->getTargetOffset()); } @@ -813,7 +1065,7 @@ 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); + fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); } reference->setFromTarget(*fromTarget); } @@ -821,33 +1073,20 @@ void Linker::resolveFrom(ObjectFile::Reference* reference) void Linker::resolveReferences() { - fStartResolveTime = mach_absolute_time(); // note: the atom list may grow during this loop as libraries supply needed atoms for (unsigned int j=0; j < fAllAtoms.size(); ++j) { ObjectFile::Atom* atom = fAllAtoms[j]; std::vector& references = atom->getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - if ( reference->isTargetUnbound() ) + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) this->resolve(reference); - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) this->resolveFrom(reference); } } } -class InSet -{ -public: - InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fDeadAtoms.count(atom) != 0 ); - } - -private: - std::set& fDeadAtoms; -}; // used to remove stabs associated with atoms that won't be in output file class NotInSet @@ -867,98 +1106,822 @@ private: }; -class DeadStrippable +class NotLive { public: - DeadStrippable() {} + NotLive(std::set& set) : fLiveAtoms(set) {} bool operator()(ObjectFile::Atom*& atom) const { - //if ( ! atom->dontDeadStrip() ) + //if ( fLiveAtoms.count(atom) == 0 ) // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); - return ( ! atom->dontDeadStrip() ); + return ( fLiveAtoms.count(atom) == 0 ); } +private: + std::set& fLiveAtoms; }; -void Linker::markLive(ObjectFile::Atom* atom, std::set& liveAtoms) + +void Linker::addJustInTimeAtomsAndMarkLive(const char* name) { - if ( liveAtoms.count(atom) == 0 ) { - // this atom is live - liveAtoms.insert(atom); - // so don't dead strip - atom->setDontDeadStrip(); + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) { + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = atom->getDisplayName(); + this->markLive(*atom, &rootChain); + } + } + } + delete atoms; + } +} +void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) +{ + if ( fLiveAtoms.count(&atom) == 0 ) { + // if -whylive cares about this symbol, then dump chain + if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { + int depth = 0; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", p->name); + } + } + // set up next chain + WhyLiveBackChain thisChain; + thisChain.previous = previous; + // this atom is live + fLiveAtoms.insert(&atom); + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); + } // and all atoms it references - std::vector& references = atom->getReferences(); + std::vector& references = atom.getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - // mark target of reference as live - this->markLive(&reference->getTarget(), liveAtoms); - // mark from-target (if it exists) as live - if ( reference->hasFromTarget() ) - this->markLive(&reference->getFromTarget(), liveAtoms); + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + } + else { + // mark as undefined, for later error processing + fAtomsWithUnresolvedReferences.push_back(&atom); + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getTargetName(); + markLive(reference->getTarget(), &thisChain); + break; + case ObjectFile::Reference::kDontBind: + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + break; + case ObjectFile::Reference::kUnboundByName: + // do nothing + break; + } + // do the same as above, for "from target" + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getFromTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setFromTarget(*target); + } + else { + // mark as undefined, for later error processing + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getFromTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getFromTargetName(); + markLive(reference->getFromTarget(), &thisChain); + break; + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + // do nothing + break; + } } } } -void Linker::deadStrip() -{ - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - std::set liveAtoms; - - // mark main() and all atoms reachable from it as live - ObjectFile::Atom* entryPoint = this->entryPoint(); - if ( entryPoint != NULL ) - markLive(entryPoint, liveAtoms); - - ObjectFile::Atom* dyldHelper = this->dyldHelper(); - if ( dyldHelper != NULL ) - markLive(dyldHelper, liveAtoms); - - // mark initializers and all atoms reachable from them as live - // mark all exported atoms and atoms reachable from them as live - Section* initSection = Section::find("__mod_init_func", "__DATA", false); - Section* termSection = Section::find("__mod_term_func", "__DATA", false); - const bool globalsAreRoots = ( fOptions.outputKind() != Options::kDynamicExecutable ); - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - //fprintf(stderr,"%d %s\n", atom->dontDeadStrip(), atom->getDisplayName()); - if ( (globalsAreRoots && (atom->getScope() == ObjectFile::Atom::scopeGlobal)) - || (atom->getSection() == initSection) || (atom->getSection() == termSection) ) - markLive(atom, liveAtoms); - } - // now remove dead strippable atoms from fAllAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), DeadStrippable()), fAllAtoms.end()); +void Linker::addLiveRoot(const char* name) +{ + ObjectFile::Atom* target = fGlobalSymbolTable.find(name); + if ( target == NULL ) { + this->addJustInTimeAtomsAndMarkLive(name); + target = fGlobalSymbolTable.find(name); } - else { - //printf("Stripping atoms:\n"); - //for (std::set::iterator it=fDeadAtoms.begin(); it != fDeadAtoms.end(); it++) { - // printf("\t%s\n", (*it)->getDisplayName()); - //} + if ( target != NULL ) + fLiveRootAtoms.insert(target); +} - // if not dead stripping, just remove atoms weak atoms that have been overridden - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + +void Linker::deadStripResolve() +{ + // add main() to live roots + ObjectFile::Atom* entryPoint = this->entryPoint(); + if ( entryPoint != NULL ) + fLiveRootAtoms.insert(entryPoint); + + // add dyld_stub_binding_helper() to live roots + ObjectFile::Atom* dyldHelper = this->dyldHelper(); + if ( dyldHelper != NULL ) + fLiveRootAtoms.insert(dyldHelper); + + // add -exported_symbols_list, -init, and -u entries to live roots + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) + addLiveRoot(*it); + + // in some cases, every global scope atom in initial .o files is a root + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) + fLiveRootAtoms.insert(atom); + } + } + + // mark all roots as live, and all atoms they reference + for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = (*it)->getDisplayName(); + markLive(**it, &rootChain); + } + + // it is possible that there are unresolved references that can be resolved now + // this can happen if the first reference to a common symbol in an archive. + // common symbols are not in the archive TOC, but the .o could have been pulled in later. + // ld64 while linking cc1 [ when dead_strip is ON] + for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { + std::vector& references = (*it)->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", target->getDisplayName()); + } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); + if ( target != NULL ) { + reference->setFromTarget(*target); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", target->getDisplayName()); + } + } + } + } + + // now remove all non-live atoms from fAllAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); +} + +void Linker::checkObjC() +{ + // check dylibs + switch ( fCurrentObjCConstraint ) { + case ObjectFile::Reader::kObjcNone: + // can link against any dylib + break; + case ObjectFile::Reader::kObjcRetainRelease: + // cannot link against GC-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) + throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); + } + } + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + // can link against GC or RR dylibs + break; + case ObjectFile::Reader::kObjcGC: + // cannot link against RR-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) + throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); + } + } + break; + } + + // synthesize __OBJC __image_info atom if needed + if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { + this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); + } +} + +void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName) +{ + if ( probeName != NULL ) { + if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) + fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) + fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) + fDtraceAtomToTypes[&atom].insert(probeName); + else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) ) + fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); } } +static uint8_t pointerKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointer; + case CPU_TYPE_POWERPC64: + return ppc64::kPointer; + case CPU_TYPE_I386: + return x86::kPointer; + case CPU_TYPE_X86_64: + return x86_64::kPointer; + } + throw "uknown architecture"; +} +static uint8_t pcRelKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointerDiff32; + case CPU_TYPE_POWERPC64: + return ppc64::kPointerDiff32; + case CPU_TYPE_I386: + return x86::kPointerDiff; + case CPU_TYPE_X86_64: + return x86_64::kPointerDiff32; + } + throw "uknown architecture"; +} + +typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); +typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); + +void Linker::processDTrace() +{ + // handle dtrace 2.0 static probes + if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) { + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector emptyList; + for(std::vector::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) { + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + for(std::vector::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) { + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector& probes = pit->second; + + // open library and find dtrace_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map::iterator pos = fDtraceAtomToTypes.find(it->atom); + if ( pos != fDtraceAtomToTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); + uint32_t index = 0; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + // call dtrace library to create DOF section + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + char sectionName[18]; + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) + ++sectionName[15]; + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, + "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error creating dtrace DOF section"; + } + } + } + // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files + else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) { + const uint32_t probeCount = fDtraceProbes.size(); + const char* labels[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + + // open libray and find dtrace_ld64_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); + oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); + + // build argument list + uint32_t index = 0; + for(std::vector::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) { + labels[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + size_t dofSectionSize; + // call dtrace library to create DOF section + uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error created dtrace DOF section"; + } + } +} + + +static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) +{ + if ( objectFileLeafName == NULL ) + return true; + const char* atomFullPath = atom->getFile()->getPath(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) + return true; + } + else { + if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) + return true; + } + return false; +} + + +static bool usesAnonymousNamespace(const char* symbol) +{ + return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) ); +} + + +// +// convert: +// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv +// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv +// +static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[]) +{ + const char* globPtr = strstr(inSymbol, "_GLOBAL__N_"); + while ( isdigit(*(--globPtr)) ) + ; // loop + char* endptr; + unsigned long length = strtoul(globPtr+1, &endptr, 10); + const char* globEndPtr = endptr + length; + int startLen = globPtr-inSymbol+1; + memcpy(outSymbol, inSymbol, startLen); + outSymbol[startLen] = '-'; + strcpy(&outSymbol[startLen+1], globEndPtr); +} + + +ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) +{ + ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); + if ( atom != NULL ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) + return atom; + } + else { + // slow case. The requested symbol is not in symbol table, so might be static function + static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; + static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace; + static bool built = false; + // build a hash_map the first time + if ( !built ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( name != NULL) { + if ( usesAnonymousNamespace(name) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalName); + const char* hashName = strdup(canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName); + if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() ) + hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom; + else + hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL + } + else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // static function or data + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); + if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) + hashTableOfTranslationUnitScopedSymbols[name] = atom; + else + hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL + } + } + } + //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); + built = true; + } + + // look for name in hashTableOfTranslationUnitScopedSymbols + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); + if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + const char* name = atom->getName(); + if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate\n", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + + // look for name in hashTableOfSymbolsWithAnonymousNamespace + if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(orderedSymbol.symbolName)+2]; + canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName); + if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( (name != NULL) && usesAnonymousNamespace(name) ) { + char canonicalAtomName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalAtomName); + if ( strcmp(canonicalAtomName, canonicalName) == 0 ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate\n", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + } + } + return NULL; +} + + +void Linker::sortSections() +{ + Section::assignIndexes(); +} + + +// +// Linker::sortAtoms() +// +// The purpose of this method is to take the graph of all Atoms and produce an ordered +// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must +// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified +// in an order_file are seqenced as in the order_file and before Atoms not specified, +// 4) Atoms in the same section from the same .o file should be contiguous and sequenced +// in the same order they were in the .o file, 5) Atoms in the same Section but which came +// from different .o files should be sequenced in the same order that the .o files +// were passed to the linker (i.e. command line order). +// +// The way this is implemented is that the linker passes a "base ordinal" to each Reader +// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() +// on its atoms returns a contiguous range of values starting at the base ordinal. Then +// sorting is just sorting by section, then by ordinal. +// +// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// is created. It causes the sort routine to ignore the value returned by getOrdinal() and +// use the override value instead. Next some Atoms must be layed out consecutively +// (e.g. hand written assembly that does not end with return, but rather falls into +// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of +// kFollowOn refernces produces "clusters" of atoms that must stay together. +// If an order_file tries to move one atom, it may need to move a whole cluster. The +// algorithm to do this models clusters using two maps. The "starts" maps maps any +// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a +// cluster to the next Atom in the cluster. With this in place, while processing an +// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is +// given ordinal overrides. +// void Linker::sortAtoms() { fStartSortTime = mach_absolute_time(); - Section::assignIndexes(); - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter()); + // if -order_file is used, build map of atom ordinal overrides + std::map* ordinalOverrideMap = NULL; + std::map theOrdinalOverrideMap; + const bool log = false; + if ( fOptions.orderedSymbols().size() != 0 ) { + // first make a pass to find all follow-on references and build start/next maps + // which are a way to represent clusters of atoms that must layout together + std::map followOnStarts; + std::map followOnNexts; + for (std::vector::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { + ObjectFile::Atom* atom = *ait; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == 1 ) { // FIX FIX + ObjectFile::Atom* targetAtom = &ref->getTarget(); + if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName()); + std::map::iterator startFrom = followOnStarts.find(atom); + std::map::iterator startTo = followOnStarts.find(targetAtom); + if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { + // this is first time we've seen either atom, make simple cluster of the two + if ( log ) fprintf(stderr, " new cluster\n"); + followOnStarts[atom] = atom; + followOnStarts[targetAtom] = atom; + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + } + else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { + // atom is at end of an existing cluster, so append target to end of cluster + if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + followOnStarts[targetAtom] = followOnStarts[atom]; + } + else { + // gerneral case of inserting into an existing cluster + if ( followOnNexts[atom] != NULL ) { + // an atom with two follow-ons is illegal + fprintf(stderr, "ld: warning can't order %s because both %s and %s must follow it\n", + atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); + } + else { + // there already exists an atom that says target must be its follow-on + const ObjectFile::Atom* originalStart = startTo->second; + const ObjectFile::Atom* originalPrevious = originalStart; + while ( followOnNexts[originalPrevious] != targetAtom ) + originalPrevious = followOnNexts[originalPrevious]; + bool otherIsAlias = (originalPrevious->getSize() == 0); + bool thisIsAlias = (atom->getSize() == 0); + if ( !otherIsAlias && !thisIsAlias ) { + fprintf(stderr, "ld: warning can't order %s because both %s and %s must preceed it\n", + targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); + } + else if ( otherIsAlias ) { + if ( originalPrevious == originalStart ) { + // other is alias at start of cluster, make this the new start of cluster + if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) + followOnStarts[nextAtom] = atom; + } + else { + // other is alias in middle of cluster, insert new atom before it + if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { + if ( followOnNexts[a] == originalPrevious ) { + followOnNexts[a] = atom; + break; + } + } + } + } + else { + // this is alias, so it can go inbetween originalPrevious and targetAtom + if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = followOnNexts[originalPrevious]; + followOnNexts[originalPrevious] = atom; + } + } + } + } + } + } + + if ( log ) { + for(std::map::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) + fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); + + for(std::map::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it) + fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null"); + } + + // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals + ordinalOverrideMap = &theOrdinalOverrideMap; + uint32_t index = 0; + uint32_t matchCount = 0; + std::vector& orderedSymbols = fOptions.orderedSymbols(); + for(std::vector::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { + ObjectFile::Atom* atom = this->findAtom(*it); + if ( atom != NULL ) { + std::map::iterator start = followOnStarts.find(atom); + if ( start != followOnStarts.end() ) { + // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together + for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) { + std::map::iterator pos = theOrdinalOverrideMap.find(nextAtom); + if ( pos == theOrdinalOverrideMap.end() ) { + theOrdinalOverrideMap[nextAtom] = index++; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); + } + else { + if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", + atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); + } + } + } + else { + theOrdinalOverrideMap[atom] = index; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + ++matchCount; + //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName); + } + ++index; + } + if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { + fprintf(stderr, "ld: warning only %u out of %lu order_file symbols were applicable\n", matchCount, fOptions.orderedSymbols().size() ); + } + } + + // sort atoms + std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap)); + //fprintf(stderr, "Sorted atoms:\n"); //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%s\n", (*it)->getDisplayName()); + // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); //} } - // make sure given addresses are within reach of branches, etc void Linker::tweakLayout() { + // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB + if ( fTotalSize > 0x7F000000 ) { + fBiggerThanTwoGigOutput = true; + + if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) + throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); + + // move very large (>1MB) zero fill atoms to a new section at very end + Section* hugeZeroFills = Section::find("__huge", "__DATA", true); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && atom->getSegment().isContentReadable() ) + atom->setSection(hugeZeroFills); + } + } } @@ -978,18 +1941,9 @@ void Linker::writeDotOutput() ObjectFile::Atom* atom = *it; if ( atom->getFile() != fOutputFile ) { const char* name = atom->getDisplayName(); - // don't create nodes for stubs, lazy pointers or non-lazy pointers - const char* lastDollar = strrchr(name, '$'); - if ( lastDollar != NULL ) { - if ( (strcmp(lastDollar, "$stub") == 0) || (strcmp(lastDollar, "$lazy_ptr") == 0) || (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) - continue; - } if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - char temp[strlen(name)+2]; - strcpy(temp, name); - temp[strlen(name)-7] = '\0'; // strip trailing "$import" - fprintf(out, "\t%s [ shape = plaintext ];\n", temp); + fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); } else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { char cstring[atom->getSize()+2]; @@ -1021,37 +1975,7 @@ void Linker::writeDotOutput() ObjectFile::Atom* toAtom = &(reference->getTarget()); if ( seenTargets.count(toAtom) == 0 ) { seenTargets.insert(toAtom); - const char* toName = toAtom->getDisplayName(); - const char* lastDollar = strrchr(toName, '$'); - if ( (lastDollar != NULL) && (strcmp(lastDollar, "$stub") == 0) ) { - char temp[strlen(toName)+2]; - strcpy(temp, toName); - temp[strlen(toName)-5] = '\0'; // strip trailing "$stub" - fprintf(out, "\taddr%p -> %s;\n", fromAtom, temp); - } - else if ( lastDollar != NULL && (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) { - ObjectFile::Atom* nonLazyTargetAtom = &(toAtom->getReferences()[0]->getTarget()); - if ( (nonLazyTargetAtom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (nonLazyTargetAtom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - char temp[strlen(toName)+2]; - strcpy(temp, toName); - temp[strlen(toName)-13] = '\0'; // strip trailing "$non_lazy_ptr" - fprintf(out, "\taddr%p -> %s;\n", fromAtom, temp); - } - else { - fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, nonLazyTargetAtom); - } - } - else { - // don't list references from stubs, lazy pointers or non-lazy pointers - const char* name = fromAtom->getDisplayName(); - const char* lastDollar = strrchr(name, '$'); - if ( lastDollar != NULL ) { - if ( (strcmp(lastDollar, "$stub") == 0) || (strcmp(lastDollar, "$lazy_ptr") == 0) || (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) - continue; - } - fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); - } + fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); } } } @@ -1065,11 +1989,7 @@ void Linker::writeDotOutput() if ( atom->getFile() != fOutputFile ) if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - const char* name = atom->getDisplayName(); - char temp[strlen(name)+2]; - strcpy(temp, name); - temp[strlen(name)-7] = '\0'; // strip trailing "$import" - fprintf(out, "%s; ", temp); + fprintf(out, "addr%p; ", atom); } } fprintf(out, "};\n "); @@ -1079,7 +1999,7 @@ void Linker::writeDotOutput() fclose(out); } else { - fprintf(stderr, "ld64 warning: could not write dot output file: %s\n", dotOutFilePath); + fprintf(stderr, "ld: warning could not write dot output file: %s\n", dotOutFilePath); } } } @@ -1094,14 +2014,14 @@ ObjectFile::Atom* Linker::entryPoint() case Options::kDyld: entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); if ( entryPoint == NULL ) { - throwf("could not find entry point: %s", fOptions.entryName()); + throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", 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()); + throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); } } break; @@ -1136,7 +2056,7 @@ const char* Linker::assureFullPath(const char* path) // // The stab strings are of the form: // ':' -// but the contain a colon. +// but the contain a colon. // For C++ may contain a double colon (e.g. std::string:f(0,1) ) // For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) // @@ -1200,8 +2120,9 @@ bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) } } -struct HeaderRange { - std::vector::iterator begin; + +struct HeaderRange { + std::vector::iterator begin; std::vector::iterator end; int parentRangeIndex; uint32_t sum; @@ -1217,19 +2138,25 @@ typedef __gnu_cxx::hash_map, __gnu_cxx::hash< static PathToSums sKnownBINCLs; -void Linker::collectStabs(ObjectFile::Reader* reader) +void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) { bool log = false; bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); std::vector* readerStabs = reader->getStabs(); if ( readerStabs == NULL ) return; - + if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); - // Find all (possibly nested) BINCL/EINCL ranges and their checksums std::vector ranges; int curRangeIndex = -1; int count = 0; + ObjectFile::Atom* atomWithLowestOrdinal = NULL; + ObjectFile::Atom* atomWithHighestOrdinal = NULL; + uint32_t highestOrdinal = 0; + uint32_t lowestOrdinal = UINT_MAX; + std::vector > soRanges; + // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums + // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { ++count; switch ( it->type ) { @@ -1239,7 +2166,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader) range.begin = it; range.end = readerStabs->end(); range.parentRangeIndex = curRangeIndex; - range.sum = it->value; + range.sum = it->value; range.sumPrecomputed = (range.sum != 0); range.useEXCL = false; range.cannotEXCL = false; @@ -1250,15 +2177,30 @@ void Linker::collectStabs(ObjectFile::Reader* reader) break; case N_EINCL: if ( curRangeIndex == -1 ) { - fprintf(stderr, "ld64 warning: EINCL missing BINCL in %s\n", reader->getPath()); + fprintf(stderr, "ld: warning EINCL missing BINCL in %s\n", reader->getPath()); } else { ranges[curRangeIndex].end = it+1; if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); curRangeIndex = ranges[curRangeIndex].parentRangeIndex; } - break; + break; case N_FUN: + { + std::map::iterator pos = atomOrdinals.find(it->atom); + if ( pos != atomOrdinals.end() ) { + uint32_t ordinal = pos->second; + if ( ordinal > highestOrdinal ) { + highestOrdinal = ordinal; + atomWithHighestOrdinal = it->atom; + } + if ( ordinal < lowestOrdinal ) { + lowestOrdinal = ordinal; + atomWithLowestOrdinal = it->atom; + } + } + } + // fall through case N_BNSYM: case N_ENSYM: case N_LBRAC: @@ -1269,9 +2211,22 @@ void Linker::collectStabs(ObjectFile::Reader* reader) if ( curRangeIndex != -1 ) { ranges[curRangeIndex].cannotEXCL = true; if ( fOptions.warnStabs() ) - fprintf(stderr, "ld64: cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + fprintf(stderr, "ld: cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); } break; + case N_SO: + if ( (it->string != NULL) && (strlen(it->string) > 0) ) { + // start SO, reset hi/low FUN tracking + atomWithLowestOrdinal = NULL; + atomWithHighestOrdinal = NULL; + highestOrdinal = 0; + lowestOrdinal = UINT_MAX; + } + else { + // end SO, record hi/low atoms for this SO range + soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); + } + // fall through default: if ( curRangeIndex != -1 ) { if ( ! ranges[curRangeIndex].sumPrecomputed ) { @@ -1289,34 +2244,42 @@ void Linker::collectStabs(ObjectFile::Reader* reader) ranges[curRangeIndex].sum += sum; } } - + } } if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); if ( curRangeIndex != -1 ) - fprintf(stderr, "ld64 warning: BINCL (%s) missing EINCL in %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); - + fprintf(stderr, "ld: warning BINCL (%s) missing EINCL in %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + // if no BINCLs if ( ranges.size() == 0 ) { - if ( minimal ) { - // only copy minimal stabs - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - if ( minimizeStab(*it) ) - fStabs.push_back(*it); + int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + // copy minimal or all stabs + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); } } - else { - // copy all stabs - fStabs.insert(fStabs.end(), readerStabs->begin(), readerStabs->end()); - } return; } - + //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); //} - + // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { if ( ! it->cannotEXCL ) { @@ -1347,10 +2310,11 @@ void Linker::collectStabs(ObjectFile::Reader* reader) } } } - + // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs curRangeIndex = -1; const int maxRangeIndex = ranges.size(); + int soIndex = 0; for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { switch ( it->type ) { case N_BINCL: @@ -1360,7 +2324,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader) HeaderRange& range = ranges[curRangeIndex]; ObjectFile::Reader::Stab stab = *it; stab.value = range.sum; // BINCL and EXCL have n_value set to checksum - if ( range.useEXCL ) + if ( range.useEXCL ) stab.type = N_EXCL; // transform BINCL into EXCL if ( !minimal ) fStabs.push_back(stab); @@ -1374,11 +2338,24 @@ void Linker::collectStabs(ObjectFile::Reader* reader) fStabs.push_back(*it); curRangeIndex = ranges[curRangeIndex].parentRangeIndex; } - break; + break; default: if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { - if ( !minimal || minimizeStab(*it) ) - fStabs.push_back(*it); + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); + } } } } @@ -1386,163 +2363,209 @@ void Linker::collectStabs(ObjectFile::Reader* reader) } -void Linker::synthesizeStabs(ObjectFile::Reader* reader) +// used to prune out atoms that don't need debug notes generated +class NoDebugNoteAtom { - // synthesize "debug notes" and add them to master stabs vector - const char* dirPath = NULL; - const char* filename = NULL; - bool wroteStartSO = false; - std::vector seenFiles; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() == reader ) { - const char* name = atom->getName(); - if ( (name != NULL) && (name[0] == '_' ) && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) { - const char* newDirPath; - const char* newFilename; - if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { - // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - if ( filename != NULL ) { - // translation unit change, emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } - // new translation unit, emit start SO's - ObjectFile::Reader::Stab dirPathStab; - dirPathStab.atom = NULL; - dirPathStab.type = N_SO; - dirPathStab.other = 0; - dirPathStab.desc = 0; - dirPathStab.value = 0; - dirPathStab.string = newDirPath; - fStabs.push_back(dirPathStab); - ObjectFile::Reader::Stab fileStab; - fileStab.atom = NULL; - fileStab.type = N_SO; - fileStab.other = 0; - fileStab.desc = 0; - fileStab.value = 0; - fileStab.string = newFilename; - fStabs.push_back(fileStab); - // Synthesize OSO for start of file - ObjectFile::Reader::Stab objStab; - objStab.atom = NULL; - objStab.type = N_OSO; - objStab.other = 0; - objStab.desc = 1; - objStab.value = reader->getModificationTime(); - objStab.string = assureFullPath(reader->getPath()); - fStabs.push_back(objStab); - wroteStartSO = true; - } - filename = newFilename; - dirPath = newDirPath; - seenFiles.push_back(filename); - if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - // Synthesize BNSYM and start FUN stabs - ObjectFile::Reader::Stab beginSym; - beginSym.atom = atom; - beginSym.type = N_BNSYM; - beginSym.other = 1; - beginSym.desc = 0; - beginSym.value = 0; - beginSym.string = ""; - fStabs.push_back(beginSym); - ObjectFile::Reader::Stab startFun; - startFun.atom = atom; - startFun.type = N_FUN; - startFun.other = 1; - startFun.desc = 0; - startFun.value = 0; - startFun.string = name; - fStabs.push_back(startFun); - // Synthesize any SOL stabs needed - std::vector* lineInfo = atom->getLineInfo(); - if ( lineInfo != NULL ) { - // might be nice to set the source file path to seenFiles so it does not show up in SOLs - const char* curFile = NULL; - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - if ( it->fileName != curFile ) { - bool alreadySeen = false; - for (std::vector::iterator sit = seenFiles.begin(); sit != seenFiles.end(); ++sit) { - if ( strcmp(it->fileName, *sit) == 0 ) { - alreadySeen = true; - break; - } - } - if ( ! alreadySeen ) { - seenFiles.push_back(it->fileName); - ObjectFile::Reader::Stab sol; - sol.atom = 0; - sol.type = N_SOL; - sol.other = 0; - sol.desc = 0; - sol.value = 0; - sol.string = it->fileName; - fStabs.push_back(sol); - } - curFile = it->fileName; - } +public: + NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} + + bool operator()(const ObjectFile::Atom* atom) const { + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + return true; + if ( atom->getName() == NULL ) + return true; + if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) + return true; + return false; + } + +private: + const std::map& fReadersWithDwarfOrdinals; +}; + +// used to sort atoms with debug notes +class ReadersWithDwarfSorter +{ +public: + ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, + const std::map& atomOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} + + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const + { + // first sort by reader + unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; + unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; + if ( leftReaderIndex != rightReaderIndex ) + return (leftReaderIndex < rightReaderIndex); + + // then sort by atom ordinal + unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; + unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; + return leftAtomIndex < rightAtomIndex; + } + +private: + const std::map& fReadersWithDwarfOrdinals; + const std::map& fAtomOrdinals; +}; + + + + + +void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) +{ + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); + __gnu_cxx::hash_set, CStringEquals> seenFiles; + for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { + ObjectFile::Atom* atom = *it; + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ObjectFile::Reader::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + fStabs.push_back(dirPathStab); + ObjectFile::Reader::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + fStabs.push_back(fileStab); + // Synthesize OSO for start of file + ObjectFile::Reader::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + objStab.other = 0; + objStab.desc = 1; + objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); + objStab.string = assureFullPath(atom->getFile()->getPath()); + fStabs.push_back(objStab); + wroteStartSO = true; + // add the source file path to seenFiles so it does not show up in SOLs + seenFiles.insert(newFilename); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + // Synthesize BNSYM and start FUN stabs + ObjectFile::Reader::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + fStabs.push_back(beginSym); + ObjectFile::Reader::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->getName(); + fStabs.push_back(startFun); + // Synthesize any SOL stabs needed + std::vector* lineInfo = atom->getLineInfo(); + if ( lineInfo != NULL ) { + const char* curFile = NULL; + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( it->fileName != curFile ) { + if ( seenFiles.count(it->fileName) == 0 ) { + seenFiles.insert(it->fileName); + ObjectFile::Reader::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = it->fileName; + fStabs.push_back(sol); } + curFile = it->fileName; } - // Synthesize end FUN and ENSYM stabs - ObjectFile::Reader::Stab endFun; - endFun.atom = atom; - endFun.type = N_FUN; - endFun.other = 0; - endFun.desc = 0; - endFun.value = 0; - endFun.string = ""; - fStabs.push_back(endFun); - ObjectFile::Reader::Stab endSym; - endSym.atom = atom; - endSym.type = N_ENSYM; - endSym.other = 1; - endSym.desc = 0; - endSym.value = 0; - endSym.string = ""; - fStabs.push_back(endSym); } - else { - ObjectFile::Reader::Stab globalsStab; - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // Synthesize STSYM stab for statics - const char* name = atom->getName(); - if ( name[0] == '_' ) { - globalsStab.atom = atom; - globalsStab.type = N_STSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } - else { - // Synthesize GSYM stab for other globals (but not .eh exception frame symbols) - const char* name = atom->getName(); - if ( (name[0] == '_') && (strcmp(atom->getSectionName(), "__eh_frame") != 0) ) { - globalsStab.atom = atom; - globalsStab.type = N_GSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } + } + // Synthesize end FUN and ENSYM stabs + ObjectFile::Reader::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + fStabs.push_back(endFun); + ObjectFile::Reader::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + fStabs.push_back(endSym); + } + else { + ObjectFile::Reader::Stab globalsStab; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + const char* name = atom->getName(); + if ( name[0] == '_' ) { + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + else { + // Synthesize GSYM stab for other globals (but not .eh exception frame symbols) + const char* name = atom->getName(); + if ( (name[0] == '_') && (strcmp(atom->getSectionName(), "__eh_frame") != 0) ) { + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); } } } } } + if ( wroteStartSO ) { // emit ending SO ObjectFile::Reader::Stab endFileStab; @@ -1556,11 +2579,18 @@ void Linker::synthesizeStabs(ObjectFile::Reader* reader) } } -void Linker::collectStabs() + + + +void Linker::collectDebugInfo() { + std::map atomOrdinals; + fStartDebugTime = mach_absolute_time(); if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { - fStabs.reserve(1024); // try to minimize re-allocations - // get stabs from each reader, in command line order + + // determine mixture of stabs and dwarf + bool someStabs = false; + bool someDwarf = false; for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); it != fReadersThatHaveSuppliedAtoms.end(); it++) { @@ -1568,17 +2598,16 @@ void Linker::collectStabs() if ( reader != NULL ) { switch ( reader->getDebugInfoKind() ) { case ObjectFile::Reader::kDebugInfoNone: - // do nothing break; case ObjectFile::Reader::kDebugInfoStabs: - collectStabs(reader); + someStabs = true; break; case ObjectFile::Reader::kDebugInfoDwarf: - synthesizeStabs(reader); + someDwarf = true; fCreateUUID = true; break; case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader); + someStabs = true; fCreateUUID = true; break; default: @@ -1586,18 +2615,83 @@ void Linker::collectStabs() } } } - // remove stabs associated with atoms that won't be in output - std::set allAtomsSet; - allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); - fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + + if ( someDwarf || someStabs ) { + // try to minimize re-allocations + fStabs.reserve(1024); + + // make mapping from atoms to ordinal + uint32_t ordinal = 1; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atomOrdinals[*it] = ordinal++; + } + } + + // process all dwarf .o files as a batch + if ( someDwarf ) { + // make mapping from readers with dwarf to ordinal + std::map readersWithDwarfOrdinals; + uint32_t readerOrdinal = 1; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { + readersWithDwarfOrdinals[reader] = readerOrdinal++; + } + } + + // make a vector of atoms + std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); + // remove those not from a reader that has dwarf + allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), + NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); + // sort by reader then atom ordinal + std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); + // add debug notes for each atom + this->synthesizeDebugNotes(allAtomsByReader); + } + + // process all stabs .o files one by one + if ( someStabs ) { + // get stabs from each reader, in command line order + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoDwarf: + case ObjectFile::Reader::kDebugInfoNone: + // do nothing + break; + case ObjectFile::Reader::kDebugInfoStabs: + case ObjectFile::Reader::kDebugInfoStabsUUID: + collectStabs(reader, atomOrdinals); + break; + default: + throw "Unhandled type of debug information"; + } + } + } + // remove stabs associated with atoms that won't be in output + std::set allAtomsSet; + allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); + fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + } } } void Linker::writeOutput() { + if ( fOptions.forceCpuSubtypeAll() ) + fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; + fStartWriteTime = mach_absolute_time(); // tell writer about each segment's atoms - fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(), this->dyldHelper(), (fCreateUUID && fOptions.emitUUID())); + fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(), this->dyldHelper(), + fCreateUUID, fCanScatter, + fCurrentCpuConstraint, fBiggerThanTwoGigOutput); } ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) @@ -1624,7 +2718,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); len = OSSwapBigToHostInt32(archs[i].size); // if requested architecture is page aligned within fat file, then remap just that portion of file - if ( (fileOffset && 0x00000FFF) == 0 ) { + if ( (fileOffset & 0x00000FFF) == 0 ) { // unmap whole file munmap((caddr_t)p, info.fileLen); // re-map just part we need @@ -1644,51 +2738,167 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) switch (fArchitecture) { case CPU_TYPE_POWERPC: if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); - else if ( mach_o::dylib::Reader::validFile(p) ) - return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; case CPU_TYPE_POWERPC64: if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); - else if ( mach_o::dylib::Reader::validFile(p) ) - return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; case CPU_TYPE_I386: if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); - else if ( mach_o::dylib::Reader::validFile(p) ) - return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::archive::Reader::validFile(p, len) ) + return this->addArchive(new mach_o::archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; } +#if LLVM_SUPPORT + if ( LLVMReader::validFile(p, info.path, fArchitecture, fOptions) ) { + return this->addObject(LLVMReader::make(p, info.path, info.modTime, fOptions), info, len); + } +#endif // error handling if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - const char* archName = "unknown"; - switch (fArchitecture) { - 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); + throwf("missing required architecture %s in file", fArchitectureName); } else { throw "file is not of required architecture"; } } +void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) +{ + if ( fOptions.readerOptions().fTraceDylibs ) { + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + if ( indirect ) + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } +} + + + +ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) +{ + //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos != fDylibMap.end() ) { + return pos->second; + } + else { + // allow -dylib_path option to override indirect library to use + for (std::vector::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { + if ( strcmp(dit->installName,installPath) == 0 ) {\ + try { + Options::FileInfo info = fOptions.findFile(dit->useInstead); + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + fprintf(stderr, "ld: warning ignoring -dylib_file option, %s\n", msg); + } + } + } + char newPath[MAXPATHLEN]; + // handle @loader_path + if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { + strcpy(newPath, fromPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &installPath[13]); + else + strcpy(newPath, &installPath[13]); + installPath = newPath; + } + // note: @executable_path case is handled inside findFileUsingPaths() + // search for dylib using -F and -L paths + Options::FileInfo info = fOptions.findFileUsingPaths(installPath); + try { + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + throwf("in %s, %s", info.path, msg); + } + } +} + + +void Linker::processDylibs() +{ + fAllDirectDylibsLoaded = true; + + // mark all dylibs initially specified as required and check if they can be used + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + it->second->setExplicitlyLinked(); + this->checkDylibClientRestrictions(it->second); + } + + // keep processing dylibs until no more dylibs are added + unsigned long lastMapSize = 0; + while ( lastMapSize != fDylibMap.size() ) { + lastMapSize = fDylibMap.size(); + // can't iterator fDylibMap while modifying it, so use temp buffer + std::vector currentUnprocessedReaders; + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( fDylibsProcessed.count(it->second) == 0 ) + currentUnprocessedReaders.push_back(it->second); + } + for (std::vector::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) { + fDylibsProcessed.insert(*it); + (*it)->processIndirectLibraries(this); + } + } + + // go back over original dylibs and mark sub frameworks as re-exported + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + const char* myLeaf = strrchr(fOptions.installPath(), '/'); + if ( myLeaf != NULL ) { + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + const char* childParent = reader->parentUmbrella(); + if ( childParent != NULL ) { + if ( strcmp(childParent, &myLeaf[1]) == 0 ) { + // set re-export bit of info + std::map::iterator pos = fDylibOptionsMap.find(reader); + if ( pos != fDylibOptionsMap.end() ) { + pos->second.fReExport = true; + } + } + } + } + } + } + +} + + void Linker::createReaders() { @@ -1703,7 +2913,7 @@ void Linker::createReaders() // ignore /usr/lib/dyld on command line in crt.o build if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { try { - this->addInputFile(this->createReader(entry)); + this->addInputFile(this->createReader(entry), entry); } catch (const char* msg) { if ( strstr(msg, "architecture") != NULL ) { @@ -1711,7 +2921,7 @@ void Linker::createReaders() // ignore, because this is about an architecture not in use } else { - fprintf(stderr, "ld64 warning: in %s, %s\n", entry.path, msg); + fprintf(stderr, "ld: warning in %s, %s\n", entry.path, msg); } } else { @@ -1721,115 +2931,16 @@ void Linker::createReaders() } } - // add first level of indirect dylibs - fDirectLibrariesComplete = true; - for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { - this->addIndirectLibraries(it->reader); - } - - // indirect handling depends on namespace - switch ( fOptions.nameSpace() ) { - case Options::kFlatNameSpace: - case Options::kForceFlatNameSpace: - // with flat namespace, blindly load all indirect libraries - // the indirect list will grow as indirect libraries are loaded - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - try { - it->reader = this->createReader(fOptions.findFile(it->path)); - it->reader->setSortOrder(fNextObjectFileOrder++); - } - catch (const char* msg) { - fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); - } - } - break; - - case Options::kTwoLevelNameSpace: - // with two-level namespace we only want to use indirect libraries that are re-exported through a library that is used - { - bool indirectAdded = true; - while ( indirectAdded ) { - indirectAdded = false; - // instantiate a reader for each indirect library and try to find parent that re-exports it - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( it->reader == NULL ) { - try { - it->reader = this->createReader(fOptions.findFile(it->path)); - it->reader->setSortOrder(fNextObjectFileOrder++); - indirectAdded = true; - } - catch (const char* msg) { - fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); - } - } - // if an indirect library does not have an assigned parent, look for one - if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary == NULL) ) { - it->reExportedViaDirectLibrary = this->findDirectLibraryWhichReExports(*it); - } - } - } - } - break; - } - - // add relevant indirect libraries to the end of fDynamicLibraries - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary != 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->reExportedViaDirectLibrary; - fDynamicLibraries.push_back(dylibInfo); - if ( fOptions.readerOptions().fTraceIndirectDylibs ) { - const char* fullPath = it->reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - } - } - } -} - - -ObjectFile::Reader* Linker::findDirectLibraryWhichReExports(IndirectLibrary& indirectLib) -{ - // ask each parent if they re-export this dylib - for (std::set::iterator pit=indirectLib.parents.begin(); pit != indirectLib.parents.end(); pit++) { - if ( (*pit)->reExports(indirectLib.reader) ) { - ObjectFile::Reader* lib = *pit; - // first check if we found a direct library, if so return it - for (std::vector::iterator dit=fDynamicLibraries.begin(); dit != fDynamicLibraries.end(); dit++) { - if ( dit->reader == lib && dit->indirect == false ) - return lib; - } - // otherwise search indirects for parent and see how it is reexported - for (std::list::iterator iit=fIndirectDynamicLibraries.begin(); iit != fIndirectDynamicLibraries.end(); iit++) { - if ( iit->reader == lib ) { - ObjectFile::Reader* lib2 = this->findDirectLibraryWhichReExports(*iit); - if ( lib2 != NULL ) - return lib2; - } - } - } - } - return NULL; + this->processDylibs(); } ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { - if (fOptions.readerOptions().fTraceArchives) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); - } + fNextInputOrdinal += mappedLen; + // remember which readers are archives because they are logged differently + fArchiveReaders.insert(reader); // update stats fTotalArchiveSize += mappedLen; @@ -1839,57 +2950,49 @@ ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { + fNextInputOrdinal += mappedLen; + // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't + if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) + fCanScatter = false; + // update stats fTotalObjectSize += mappedLen; ++fTotalObjectLoaded; return reader; } -ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - if ( reader->getInstallPath() == NULL ) { - // this is a "blank" stub - // silently ignore it - return reader; - } - - if ( fDirectLibrariesComplete ) { - this->addIndirectLibraries(reader); - } - else { - if ( fOptions.readerOptions().fTraceDylibs ) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); - } - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = reader; - dylibInfo.options = info.options; - dylibInfo.indirect = false; - dylibInfo.directReader = NULL; - fDynamicLibraries.push_back(dylibInfo); - - // Verify that a client is allowed to link to this dylib. There are three cases. - bool okToLink = true; - const char* outputFilePath = fOptions.installPath(); - const char* outputFilePathLastSlash = strrchr(outputFilePath, '/'); - if ( reader->parentUmbrella() != NULL ) { +void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) +{ + // Check for any restrictions on who can link with this dylib + const char* readerParentName = reader->parentUmbrella() ; + std::vector* clients = reader->getAllowableClients(); + if ( (readerParentName != NULL) || (clients != NULL) ) { + // only dylibs that are in an umbrella or have a client list need verification + const char* installName = fOptions.installPath(); + const char* installNameLastSlash = strrchr(installName, '/'); + bool isParent = false; + bool isSibling = false; + bool isAllowableClient = false; + // There are three cases: + if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella - okToLink = ( (outputFilePathLastSlash != NULL) && (strcmp(&outputFilePathLastSlash[1], reader->parentUmbrella()) == 0) ); - } - - if ( !okToLink && (reader->parentUmbrella() != NULL) ) { + isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); + + // hack to support umbrella variants that encode the variant name in the install name + // e.g. CoreServices_profile + if ( !isParent ) { + const char* underscore = strchr(&installNameLastSlash[1], '_'); + if ( underscore != NULL ) { + isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); + } + } + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent - okToLink = ( (outputFilePathLastSlash != NULL) - && (fOptions.umbrellaName() != NULL) - && (strcmp(fOptions.umbrellaName(), reader->parentUmbrella()) == 0) ); + isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); } - std::vector* clients = reader->getAllowableClients(); - if ( !okToLink && (clients != NULL) ) { + if ( !isParent && !isSibling && (clients != NULL) ) { // case 3) the dylib has a list of allowable clients, and we are creating one of them const char* clientName = fOptions.clientName(); int clientNameLen = 0; @@ -1898,96 +3001,82 @@ ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options:: clientNameLen = strlen(clientName); } else { - // infer client name from output path (e.g. xxx/libfoo.A.dylib --> foo, Bar.framework/Bar --> Bar) - clientName = outputFilePath; + // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) + clientName = installName; + clientNameLen = strlen(clientName); // starts after last slash - if ( outputFilePathLastSlash != NULL ) - clientName = &outputFilePathLastSlash[1]; + if ( installNameLastSlash != NULL ) + clientName = &installNameLastSlash[1]; if ( strncmp(clientName, "lib", 3) == 0 ) clientName = &clientName[3]; // up to first dot const char* firstDot = strchr(clientName, '.'); - if ( firstDot == NULL ) - clientNameLen = strlen(clientName); - else + if ( firstDot != NULL ) clientNameLen = firstDot - clientName; + // up to first underscore + const char* firstUnderscore = strchr(clientName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) + clientNameLen = firstUnderscore - clientName; } - + // Use clientName to check if this dylib is able to link against the allowable clients. for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { if ( strncmp(*it, clientName, clientNameLen) == 0 ) - okToLink = true; + isAllowableClient = true; } } - // error out if we are not allowed to link - if ( ! okToLink ) - //throwf("'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", - fprintf(stderr, "'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", - reader->getPath(), reader->parentUmbrella()); - } - - // update stats - ++fTotalDylibsLoaded; - - return reader; -} - - -void Linker::addIndirectLibraries(ObjectFile::Reader* reader) -{ - std::vector* dependentLibs = reader->getDependentLibraryPaths(); - if ( dependentLibs != NULL ) { - for (std::vector::iterator it=dependentLibs->begin(); it != dependentLibs->end(); it++) { - if ( this->haveDirectLibrary(*it) ) { - // do nothing, direct library already exists - } - else if ( this->haveIndirectLibrary(*it, reader) ) { - // side effect of haveIndirectLibrary() added reader to parent list + if ( !isParent && !isSibling && !isAllowableClient ) { + if ( readerParentName != NULL ) { + throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", + reader->getPath(), readerParentName); } else { - // add to list of indirect libraries - IndirectLibrary indirectLib; - indirectLib.path = *it; - indirectLib.fileLen = 0; - indirectLib.reader = NULL; - indirectLib.parents.insert(reader); - indirectLib.reExportedViaDirectLibrary = NULL; - fIndirectDynamicLibraries.push_back(indirectLib); - //fprintf(stderr, "add indirect library: %s\n", *it); + throwf("cannot link directly with %s", reader->getPath()); } } } + + } -bool Linker::haveIndirectLibrary(const char* path, ObjectFile::Reader* parentReader) +ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( strcmp(path, it->path) == 0 ) { - it->parents.insert(parentReader); - return true; + fNextInputOrdinal += mappedLen; + if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + // add to map of loaded dylibs + const char* installPath = reader->getInstallPath(); + if ( installPath != NULL ) { + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos == fDylibMap.end() ) { + fDylibMap[strdup(installPath)] = reader; } - if ( it->reader != NULL ) { - const char* installPath = it->reader->getInstallPath(); - if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) - return true; + else { + InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); + if ( pos2 == fDylibMap.end() ) + fDylibMap[strdup(reader->getPath())] = reader; + else + fprintf(stderr, "ld: warning, duplicate dylib %s\n", reader->getPath()); } } - return false; -} + else if ( info.options.fBundleLoader ) + fBundleLoaderReader = reader; -bool Linker::haveDirectLibrary(const char* path) -{ - for (std::vector::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; + // log direct readers + if ( !fAllDirectDylibsLoaded ) + this->logDylib(reader, false); + + // update stats + ++fTotalDylibsLoaded; + + return reader; } + void Linker::logTraceInfo (const char* format, ...) { static int trace_file = -1; @@ -2029,16 +3118,71 @@ void Linker::logTraceInfo (const char* format, ...) void Linker::createWriter() { fStartCreateWriterTime = mach_absolute_time(); + + // make a vector out of all required dylibs in fDylibMap + std::vector dynamicLibraries; + // need to preserve command line order + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) { + if ( reader == mit->second ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = reader; + dylibInfo.options = fDylibOptionsMap[reader]; + dynamicLibraries.push_back(dylibInfo); + break; + } + } + } + // then add any other dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + // if not already in dynamicLibraries + bool alreadyInDynamicLibraries = false; + for (std::vector::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) { + if ( dit->reader == it->second ) { + alreadyInDynamicLibraries = true; + break; + } + } + if ( ! alreadyInDynamicLibraries ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = it->second; + std::map::iterator pos = fDylibOptionsMap.find(it->second); + if ( pos != fDylibOptionsMap.end() ) { + dylibInfo.options = pos->second; + } + else { + dylibInfo.options.fWeakImport = false; // FIX ME + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = false; + } + dynamicLibraries.push_back(dylibInfo); + } + } + } + if ( fBundleLoaderReader != NULL ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = fBundleLoaderReader; + dylibInfo.options.fWeakImport = false; + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = true; + dynamicLibraries.push_back(dylibInfo); + } + const char* path = fOptions.getOutputFilePath(); switch ( fArchitecture ) { case CPU_TYPE_POWERPC: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; case CPU_TYPE_POWERPC64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; case CPU_TYPE_I386: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_X86_64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; default: throw "unknown architecture"; @@ -2062,32 +3206,43 @@ void Linker::SymbolTable::require(const char* name) } // convenience labels for 2-dimensional switch statement -enum { +enum AllDefinitionCombinations { kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition + kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, + kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, + kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, + kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, + kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol }; bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) @@ -2101,7 +3256,7 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) existingAtom = pos->second; if ( existingAtom != NULL ) { // already have atom with same name in symbol table - switch ( (existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind() ) { + switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { case kRegAndReg: throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); case kRegAndWeak: @@ -2111,6 +3266,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) case kRegAndTent: // ignore new tentative atom, because we already have a regular one useNew = false; + if ( newAtom.getSize() > existingAtom->getSize() ) { + fprintf(stderr, "ld: warning for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s\n", + newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), + existingAtom->getSize(), existingAtom->getFile()->getPath()); + } break; case kRegAndExtern: // ignore external atom, because we already have a one @@ -2120,13 +3281,16 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // ignore external atom, because we already have a one useNew = false; break; + case kRegAndAbsolute: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; case kWeakAndReg: // replace existing weak atom with regular one break; case kWeakAndWeak: // have another weak atom, use whichever has largest alignment requirement // because codegen of some client may require alignment - useNew = ( newAtom.getAlignment() > existingAtom->getAlignment() ); + useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); break; case kWeakAndTent: // replace existing weak atom with tentative one ??? @@ -2139,8 +3303,17 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // keep weak atom, at runtime external one may override useNew = false; break; + case kWeakAndAbsolute: + // replace existing weak atom with absolute one + break; case kTentAndReg: // replace existing tentative atom with regular one + if ( newAtom.getSize() < existingAtom->getSize() ) { + fprintf(stderr, "ld: warning for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s\n", + newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), + newAtom.getSize(), newAtom.getFile()->getPath()); + } break; case kTentAndWeak: // replace existing tentative atom with weak one ??? @@ -2149,6 +3322,10 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // use largest if ( newAtom.getSize() < existingAtom->getSize() ) { useNew = false; + } + else { + if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) + fprintf(stderr, "ld: warning alignment lost in merging tentative definition %s\n", newAtom.getDisplayName()); } break; case kTentAndExtern: @@ -2157,13 +3334,13 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) 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", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from dylib %s\n", + fprintf(stderr, "ld: replacing common symbol %s from %s with true definition from dylib %s\n", existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); break; case Options::kCommonsConflictsDylibsError: @@ -2171,6 +3348,9 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); } break; + case kTentAndAbsolute: + // replace tentative with absolute (can't size check because absolutes have no size) + break; case kExternAndReg: // replace external atom with regular one break; @@ -2182,12 +3362,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) 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", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + fprintf(stderr, "ld: replacing defintion of %s from dylib %s with common symbol from %s\n", newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; @@ -2202,6 +3382,9 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // keep strong dylib atom, ignore weak one useNew = false; break; + case kExternAndAbsolute: + // replace external atom with absolute one + break; case kExternWeakAndReg: // replace existing weak external with regular break; @@ -2213,12 +3396,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) 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", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + fprintf(stderr, "ld: replacing defintion of %s from dylib %s with common symbol from %s\n", newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; @@ -2234,8 +3417,36 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // keep existing external weak useNew = false; break; + case kExternWeakAndAbsolute: + // replace existing weak external with absolute + break; + case kAbsoluteAndReg: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kAbsoluteAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kAbsoluteAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case kAbsoluteAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndAbsolute: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; } } + if ( (existingAtom != NULL) && (newAtom.getScope() != existingAtom->getScope()) ) { + fprintf(stderr, "ld: warning %s has different visibility (%d) in %s and (%d) in %s\n", + newAtom.getDisplayName(), newAtom.getScope(), newAtom.getFile()->getPath(), existingAtom->getScope(), existingAtom->getFile()->getPath()); + } if ( useNew ) { fTable[name] = &newAtom; if ( existingAtom != NULL ) @@ -2271,31 +3482,70 @@ void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vectorgetSection()->getIndex(); unsigned int rightSectionIndex = right->getSection()->getIndex(); if ( leftSectionIndex != rightSectionIndex) return (leftSectionIndex < rightSectionIndex); - // then sort by .o file order - ObjectFile::Reader* leftReader = left->getFile(); - ObjectFile::Reader* rightReader = right->getFile(); - if ( leftReader != rightReader ) - return leftReader->getSortOrder() < rightReader->getSortOrder(); - - // lastly sort by atom within a .o file - return left->getSortOrder() < right->getSortOrder(); + // if a -order_file is specified, then sorting is altered to sort those symbols first + if ( fOverriddenOrdinalMap != NULL ) { + std::map::iterator leftPos = fOverriddenOrdinalMap->find(left); + std::map::iterator rightPos = fOverriddenOrdinalMap->find(right); + std::map::iterator end = fOverriddenOrdinalMap->end(); + if ( leftPos != end ) { + if ( rightPos != end ) { + // both left and right are overridden, so compare overridden ordinals + return leftPos->second < rightPos->second; + } + else { + // left is overridden and right is not, so left < right + return true; + } + } + else { + if ( rightPos != end ) { + // right is overridden and left is not, so right < left + return false; + } + else { + // neither are overridden, do default sort + // fall into default sorting below + } + } + } + + // the __common section can have real or tentative definitions + // we want the real ones to sort before tentative ones + bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + if ( leftIsTent != rightIsTent ) + return rightIsTent; + + // lastly sort by atom ordinal. this is already sorted by .o order + return left->getOrdinal() < right->getOrdinal(); } int main(int argc, const char* argv[]) { + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; try { // create linker object given command line arguments Linker ld(argc, argv); + // save error message prefix + archName = ld.architectureName(); + archInferred = ld.isInferredArchitecture(); + showArch = ld.showArchitectureInErrors(); + // open all input files ld.createReaders(); @@ -2306,10 +3556,14 @@ int main(int argc, const char* argv[]) ld.link(); } catch (const char* msg) { - fprintf(stderr, "ld64 failed: %s\n", msg); + if ( archInferred ) + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); + else if ( showArch ) + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); + else + fprintf(stderr, "ld: %s\n", msg); return 1; } - return 0; }