-/* -*- 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@
*
#include <sys/sysctl.h>
#include <fcntl.h>
#include <errno.h>
+#include <limits.h>
#include <unistd.h>
#include <mach/mach_time.h>
#include <mach/vm_statistics.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>
#include <mach-o/fat.h>
-
+#include <dlfcn.h>
#include <string>
+#include <map>
#include <set>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <ext/hash_map>
+#include <dlfcn.h>
+#include <AvailabilityMacros.h>
#include "Options.h"
#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<ObjectFile::Reference*>& references = atom->getReferences();
- const int refCount = references.size();
- printf("references: (%u)\n", refCount);
- for (int i=0; i < refCount; ++i) {
- ObjectFile::Reference* ref = references[i];
- printf(" %s\n", ref->getDescription());
- }
-
- // attributes
+#define LLVM_SUPPORT 0
+
+#if LLVM_SUPPORT
+#include "LLVMReader.hpp"
+#endif
-}
+#include "OpaqueSection.hpp"
-#endif
class CStringComparor
{
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);
bool operator()(Section* left, Section* right);
};
+ typedef __gnu_cxx::hash_map<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEquals> NameToOrdinal;
typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection;
//typedef std::map<const char*, class Section*, CStringComparor> NameToSection;
static NameToSection fgMapping;
static std::vector<Section*> fgSections;
+ static NameToOrdinal fgSegmentDiscoverOrder;
};
Section::NameToSection Section::fgMapping;
std::vector<Section*> 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)
// 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);
find("__textcoal_nt", "__TEXT", false);
}
+ // remember segment discovery order
+ if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() )
+ fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size();
+
return sect;
}
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 )
}
void Section::assignIndexes()
-{
- //printf("unsorted:\n");
+{
+ //printf("unsorted sections:\n");
//for (std::vector<Section*>::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);
//}
for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++)
(*it)->fIndex = newOrder++;
- //printf("sorted:\n");
+ //printf("sorted sections:\n");
//for (std::vector<Section*>::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<class ObjectFile::Atom*>& 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<ObjectFile::Atom*>& 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<const class ObjectFile::Atom*, uint32_t>& atomOrdinals);
+ void synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& 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<class ObjectFile::Atom*>* 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:
ObjectFile::Atom* find(const char* name);
unsigned int getRequireCount() { return fRequireCount; }
void getNeededNames(bool andWeakDefintions, std::vector<const char*>& undefines);
- private:
typedef __gnu_cxx::hash_map<const char*, ObjectFile::Atom*, __gnu_cxx::hash<const char*>, 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<const ObjectFile::Atom*, uint32_t>* map) : fOverriddenOrdinalMap(map) {}
+ bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right);
+ private:
+ std::map<const ObjectFile::Atom*, uint32_t>* fOverriddenOrdinalMap;
};
typedef std::map<const char*, uint32_t, CStringComparor> 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<const char*, std::vector<DTraceProbeInfo>, __gnu_cxx::hash<const char*>, CStringEquals> ProviderToProbes;
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
+ typedef __gnu_cxx::hash_map<const char*, ObjectFile::Reader*, __gnu_cxx::hash<const char*>, CStringEquals> InstallNameToReader;
+
struct IndirectLibrary {
const char* path;
uint64_t fileLen;
Options fOptions;
SymbolTable fGlobalSymbolTable;
- unsigned int fWeakSymbolsAddedCount;
+ uint32_t fNextInputOrdinal;
std::vector<class ObjectFile::Reader*> fInputFiles;
ExecutableFile::Writer* fOutputFile;
- std::vector<ExecutableFile::DyLibUsed> fDynamicLibraries;
- std::list<IndirectLibrary> fIndirectDynamicLibraries;
+ InstallNameToReader fDylibMap;
+ std::map<ObjectFile::Reader*,DynamicLibraryOptions> fDylibOptionsMap;
+ std::set<ObjectFile::Reader*> fDylibsProcessed;
+ ObjectFile::Reader* fBundleLoaderReader;
std::vector<class ObjectFile::Reader*> fReadersThatHaveSuppliedAtoms;
std::vector<class ObjectFile::Atom*> fAllAtoms;
+ std::set<class ObjectFile::Reader*> fArchiveReaders;
+ std::set<class ObjectFile::Reader*> fArchiveReadersLogged;
std::set<class ObjectFile::Atom*> fDeadAtoms;
+ std::set<ObjectFile::Atom*> fLiveAtoms;
+ std::set<ObjectFile::Atom*> fLiveRootAtoms;
std::vector<class ObjectFile::Reader::Stab> fStabs;
+ std::vector<class ObjectFile::Atom*> fAtomsWithUnresolvedReferences;
+ std::vector<DTraceProbeInfo> fDtraceProbes;
+ std::vector<DTraceProbeInfo> fDtraceProbeSites;
+ std::vector<DTraceProbeInfo> fDtraceIsEnabledSites;
+ std::map<const ObjectFile::Atom*,CStringSet> 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;
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<Options::FileInfo>& files = fOptions.getInputFiles();
::close(fd);
if ( amount >= (ssize_t)sizeof(buffer) ) {
if ( mach_o::relocatable::Reader<ppc>::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<ppc64>::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<x86>::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<x86_64>::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)
fOutputFile = writer;
}
+class InSet
+{
+public:
+ InSet(std::set<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {}
+
+ bool operator()(ObjectFile::Atom*& atom) const {
+ return ( fDeadAtoms.count(atom) != 0 );
+ }
+
+private:
+ std::set<ObjectFile::Atom*>& fDeadAtoms;
+};
+
+void Linker::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<class ObjectFile::Atom*> 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<class ObjectFile::Atom*>::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();
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);
}
}
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);
// 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<class ObjectFile::Reference*>& references = atom.getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
- ObjectFile::Reference* reference = *it;
- if ( reference->isTargetUnbound() ) {
- fGlobalSymbolTable.require(reference->getTargetName());
+ if ( 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<class ObjectFile::Reference*>& references = atom.getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
+ ObjectFile::Reference* reference = *it;
+ if ( reference->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);
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<class ObjectFile::Atom*>& 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<ObjectFile::Atom*>::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();
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<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ ObjectFile::Reader* reader = *it;
+ std::vector<class ObjectFile::Atom*>& atoms = reader->getAtoms();
+ this->addAtoms(atoms);
+ if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) )
+ logArchive(reader);
}
// extra command line section always at end
std::vector<Options::ExtraSection>& extraSections = fOptions.extraSections();
for( std::vector<Options::ExtraSection>::iterator it=extraSections.begin(); it != extraSections.end(); ++it) {
- this->addAtoms(SectCreate::MakeReader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)->getAtoms());
+ this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms());
+ fNextInputOrdinal += it->dataLen;
}
}
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() ) {
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<class ObjectFile::Atom*>* 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<const char*> unresolvableUndefines;
- fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines);
- const int unresolvableCount = unresolvableUndefines.size();
- int unresolvableExportsCount = 0;
- if ( unresolvableCount != 0 ) {
- if ( doPrint ) {
- fprintf(stderr, "can't resolve symbols:\n");
- for (int i=0; i < unresolvableCount; ++i) {
- const char* name = unresolvableUndefines[i];
- fprintf(stderr, " %s, referenced from:\n", name);
- // scan all atoms for references
- bool foundAtomReference = false;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* reference = *rit;
- if ( reference->isTargetUnbound() ) {
- if ( 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<const char*> 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<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* reference = *rit;
+ if ( reference->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<class ObjectFile::Atom*>* Linker::addJustInTimeAtoms(const char* name)
{
// when creating final linked image, writer gets first chance
if ( fOptions.outputKind() != Options::kObjectFile ) {
std::vector<class ObjectFile::Atom*>* 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<class ObjectFile::Reader*>::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<class ObjectFile::Atom*>* 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<IndirectLibrary>::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) {
- ObjectFile::Reader* reader = it->reader;
- if ( reader != NULL ) {
- std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- this->addAtoms(*atoms);
- //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<class ObjectFile::Atom*>* 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<class ObjectFile::Atom*>* 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)
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());
}
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);
}
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<class ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
ObjectFile::Reference* reference = *it;
- if ( reference->isTargetUnbound() )
+ 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<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {}
-
- bool operator()(ObjectFile::Atom*& atom) const {
- return ( fDeadAtoms.count(atom) != 0 );
- }
-
-private:
- std::set<ObjectFile::Atom*>& fDeadAtoms;
-};
// used to remove stabs associated with atoms that won't be in output file
class NotInSet
};
-class DeadStrippable
+class NotLive
{
public:
- DeadStrippable() {}
+ NotLive(std::set<ObjectFile::Atom*>& 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<ObjectFile::Atom*>& fLiveAtoms;
};
-void Linker::markLive(ObjectFile::Atom* atom, std::set<ObjectFile::Atom*>& 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<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(name);
+ if ( atoms != NULL ) {
+ if ( fOptions.allGlobalsAreDeadStripRoots() ) {
+ for (std::vector<ObjectFile::Atom*>::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<class ObjectFile::Reference*>& references = atom->getReferences();
+ std::vector<class ObjectFile::Reference*>& references = atom.getReferences();
for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
ObjectFile::Reference* reference = *it;
- // 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<ObjectFile::Atom*> 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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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<const char*>& initialUndefines = fOptions.initialUndefines();
+ for (std::vector<const char*>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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.
+ // <rdar://problem/4654131> ld64 while linking cc1 [ when dead_strip is ON]
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) {
+ std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences();
+ for (std::vector<ObjectFile::Reference*>::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<DTraceProbeInfo> emptyList;
+ for(std::vector<DTraceProbeInfo>::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<DTraceProbeInfo>::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<DTraceProbeInfo>& 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<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
+ std::map<const ObjectFile::Atom*,CStringSet>::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<DTraceProbeInfo>::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<DTraceProbeInfo>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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<const ObjectFile::Atom*, uint32_t>* ordinalOverrideMap = NULL;
+ std::map<const ObjectFile::Atom*, uint32_t> 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<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnStarts;
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnNexts;
+ for (std::vector<ObjectFile::Atom*>::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) {
+ ObjectFile::Atom* atom = *ait;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::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<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startFrom = followOnStarts.find(atom);
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::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<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it)
+ fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName());
+
+ for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::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<Options::OrderedSymbol>& orderedSymbols = fOptions.orderedSymbols();
+ for(std::vector<Options::OrderedSymbol>::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) {
+ ObjectFile::Atom* atom = this->findAtom(*it);
+ if ( atom != NULL ) {
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::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<const ObjectFile::Atom*, uint32_t>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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);
+ }
+ }
}
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];
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);
}
}
}
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 ");
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);
}
}
}
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;
//
// The stab strings are of the form:
// <name> ':' <type-code> <number-pari>
-// but the <name> contain a colon.
+// but the <name> contain a colon.
// For C++ <name> 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) )
//
}
}
-struct HeaderRange {
- std::vector<ObjectFile::Reader::Stab>::iterator begin;
+
+struct HeaderRange {
+ std::vector<ObjectFile::Reader::Stab>::iterator begin;
std::vector<ObjectFile::Reader::Stab>::iterator end;
int parentRangeIndex;
uint32_t sum;
static PathToSums sKnownBINCLs;
-void Linker::collectStabs(ObjectFile::Reader* reader)
+void Linker::collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals)
{
bool log = false;
bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal );
std::vector<class ObjectFile::Reader::Stab>* 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<HeaderRange> 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<std::pair<ObjectFile::Atom*,ObjectFile::Atom*> > 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<class ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
++count;
switch ( it->type ) {
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;
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<const class ObjectFile::Atom*, uint32_t>::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:
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<ObjectFile::Atom*,ObjectFile::Atom*>(atomWithLowestOrdinal, atomWithHighestOrdinal));
+ }
+ // fall through
default:
if ( curRangeIndex != -1 ) {
if ( ! ranges[curRangeIndex].sumPrecomputed ) {
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<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
- if ( minimizeStab(*it) )
- fStabs.push_back(*it);
+ int soIndex = 0;
+ for(std::vector<ObjectFile::Reader::Stab>::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<HeaderRange>::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<HeaderRange>::iterator it=ranges.begin(); it != ranges.end(); ++it) {
if ( ! it->cannotEXCL ) {
}
}
}
-
+
// 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<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
switch ( it->type ) {
case N_BINCL:
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);
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);
+ }
}
}
}
}
-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<const char*> seenFiles;
- for (std::vector<class ObjectFile::Atom*>::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<ObjectFile::LineInfo>* 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<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
- if ( it->fileName != curFile ) {
- bool alreadySeen = false;
- for (std::vector<const char*>::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<class ObjectFile::Reader*, uint32_t>& 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<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
+};
+
+// used to sort atoms with debug notes
+class ReadersWithDwarfSorter
+{
+public:
+ ReadersWithDwarfSorter(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals,
+ const std::map<const class ObjectFile::Atom*, uint32_t>& 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<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
+ const std::map<const class ObjectFile::Atom*, uint32_t>& fAtomOrdinals;
+};
+
+
+
+
+
+void Linker::synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& 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<const char*, __gnu_cxx::hash<const char*>, CStringEquals> seenFiles;
+ for (std::vector<ObjectFile::Atom*>::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<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
+ if ( lineInfo != NULL ) {
+ const char* curFile = NULL;
+ for (std::vector<ObjectFile::LineInfo>::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;
}
}
-void Linker::collectStabs()
+
+
+
+void Linker::collectDebugInfo()
{
+ std::map<const class ObjectFile::Atom*, uint32_t> 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<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
it != fReadersThatHaveSuppliedAtoms.end();
it++) {
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:
}
}
}
- // remove stabs associated with atoms that won't be in output
- std::set<class ObjectFile::Atom*> 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<ObjectFile::Atom*>::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<class ObjectFile::Reader*, uint32_t> readersWithDwarfOrdinals;
+ uint32_t readerOrdinal = 1;
+ for (std::vector<class ObjectFile::Reader*>::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<class ObjectFile::Atom*> 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<class ObjectFile::Reader*>::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<class ObjectFile::Atom*> 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)
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
switch (fArchitecture) {
case CPU_TYPE_POWERPC:
if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
- return this->addObject(mach_o::relocatable::Reader<ppc>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<ppc>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<ppc>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ return this->addObject(new mach_o::relocatable::Reader<ppc>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<ppc>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<ppc>::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len);
else if ( mach_o::archive::Reader<ppc>::validFile(p, len) )
- return this->addArchive(mach_o::archive::Reader<ppc>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
+ return this->addArchive(new mach_o::archive::Reader<ppc>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
break;
case CPU_TYPE_POWERPC64:
if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
- return this->addObject(mach_o::relocatable::Reader<ppc64>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<ppc64>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<ppc64>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ return this->addObject(new mach_o::relocatable::Reader<ppc64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<ppc64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<ppc64>::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len);
else if ( mach_o::archive::Reader<ppc64>::validFile(p, len) )
- return this->addArchive(mach_o::archive::Reader<ppc64>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
+ return this->addArchive(new mach_o::archive::Reader<ppc64>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
break;
case CPU_TYPE_I386:
if ( mach_o::relocatable::Reader<x86>::validFile(p) )
- return this->addObject(mach_o::relocatable::Reader<x86>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<x86>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<x86>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ return this->addObject(new mach_o::relocatable::Reader<x86>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<x86>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<x86>::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len);
else if ( mach_o::archive::Reader<x86>::validFile(p, len) )
- return this->addArchive(mach_o::archive::Reader<x86>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
+ return this->addArchive(new mach_o::archive::Reader<x86>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ break;
+ case CPU_TYPE_X86_64:
+ if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return this->addObject(new mach_o::relocatable::Reader<x86_64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<x86_64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<x86_64>::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::archive::Reader<x86_64>::validFile(p, len) )
+ return this->addArchive(new mach_o::archive::Reader<x86_64>::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<Options::DylibOverride>::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<ObjectFile::Reader*> 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<ObjectFile::Reader*>::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<class ObjectFile::Reader*>::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<ObjectFile::Reader*,DynamicLibraryOptions>::iterator pos = fDylibOptionsMap.find(reader);
+ if ( pos != fDylibOptionsMap.end() ) {
+ pos->second.fReExport = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
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 ) {
// 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 {
}
}
- // add first level of indirect dylibs
- fDirectLibrariesComplete = true;
- for (std::vector<ExecutableFile::DyLibUsed>::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<IndirectLibrary>::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<IndirectLibrary>::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<IndirectLibrary>::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<ObjectFile::Reader*>::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<ExecutableFile::DyLibUsed>::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<IndirectLibrary>::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;
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<const char*>* 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<const char*>* 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;
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<const char*>::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<const char*>* dependentLibs = reader->getDependentLibraryPaths();
- if ( dependentLibs != NULL ) {
- for (std::vector<const char*>::iterator it=dependentLibs->begin(); it != dependentLibs->end(); it++) {
- if ( this->haveDirectLibrary(*it) ) {
- // 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<IndirectLibrary>::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<ExecutableFile::DyLibUsed>::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) {
- if ( strcmp(path, it->reader->getPath()) == 0 )
- return true;
- const char* installPath = it->reader->getInstallPath();
- if ( (installPath != NULL) && (strcmp(path, installPath) == 0) )
- return true;
- }
- return false;
+ // 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;
void Linker::createWriter()
{
fStartCreateWriterTime = mach_absolute_time();
+
+ // make a vector out of all required dylibs in fDylibMap
+ std::vector<ExecutableFile::DyLibUsed> dynamicLibraries;
+ // need to preserve command line order
+ for (std::vector<class ObjectFile::Reader*>::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<ExecutableFile::DyLibUsed>::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<ObjectFile::Reader*,DynamicLibraryOptions>::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<ppc>(path, fOptions, fDynamicLibraries));
+ this->setOutputFile(new mach_o::executable::Writer<ppc>(path, fOptions, dynamicLibraries));
break;
case CPU_TYPE_POWERPC64:
- this->setOutputFile(new mach_o::executable::Writer<ppc64>(path, fOptions, fDynamicLibraries));
+ this->setOutputFile(new mach_o::executable::Writer<ppc64>(path, fOptions, dynamicLibraries));
break;
case CPU_TYPE_I386:
- this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, fDynamicLibraries));
+ this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, dynamicLibraries));
+ break;
+ case CPU_TYPE_X86_64:
+ this->setOutputFile(new mach_o::executable::Writer<x86_64>(path, fOptions, dynamicLibraries));
break;
default:
throw "unknown architecture";
}
// 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)
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:
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
// 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 ???
// 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 ???
// 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:
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:
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;
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;
// 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;
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;
// 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 )
-bool Linker::AtomSorter::operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
+bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
{
+ if ( left == right )
+ return false;
+
// first sort by section order (which is already sorted by segment)
unsigned int leftSectionIndex = left->getSection()->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<const ObjectFile::Atom*, uint32_t>::iterator leftPos = fOverriddenOrdinalMap->find(left);
+ std::map<const ObjectFile::Atom*, uint32_t>::iterator rightPos = fOverriddenOrdinalMap->find(right);
+ std::map<const ObjectFile::Atom*, uint32_t>::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();
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;
}