/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
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;
- const char* fSectionName;
- const char* fSegmentName;
+ char fSectionName[18];
+ char fSegmentName[18];
bool fZeroFill;
bool fUntrustedZeroFill;
Section::NameToOrdinal Section::fgSegmentDiscoverOrder;
Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill)
- : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill)
+ : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill)
{
+ strlcpy(fSectionName, sectionName, sizeof(fSectionName));
+ strlcpy(fSegmentName, segmentName, sizeof(fSegmentName));
+
this->fIndex = fgSections.size() + 20; // room for 20 standard sections
// special placement of some sections
if ( strcmp(segmentName, "__TEXT") == 0 ) {
+ // sort mach header and load commands to start of TEXT
+ if ( strcmp(sectionName, "._mach_header") == 0 )
+ this->fIndex = 1;
+ else if ( strcmp(sectionName, "._load_commands") == 0 )
+ this->fIndex = 2;
+ else if ( strcmp(sectionName, "._load_cmds_pad") == 0 )
+ this->fIndex = 3;
+ // sort __text after load commands
+ else if ( strcmp(sectionName, "__text") == 0 )
+ this->fIndex = 10;
+ // sort arm/ppc stubs after text to make branch islands feasible
+ else if ( strcmp(sectionName, "__picsymbolstub4") == 0 )
+ this->fIndex = 11;
+ else if ( strcmp(sectionName, "__symbol_stub4") == 0 )
+ this->fIndex = 11;
+ else if ( strcmp(sectionName, "__picsymbolstub1") == 0 )
+ this->fIndex = 11;
+ else if ( strcmp(sectionName, "__symbol_stub1") == 0 )
+ this->fIndex = 11;
+ // sort fast arm stubs to end of __TEXT to be close to lazy pointers
+ else if ( strcmp(sectionName, "__symbolstub1") == 0 )
+ this->fIndex = INT_MAX;
// sort unwind info to end of segment
- if ( strcmp(sectionName, "__eh_frame") == 0 )
- this->fIndex = INT_MAX;
- else if ( strcmp(sectionName, "__unwind_info") == 0 )
+ else if ( strcmp(sectionName, "__eh_frame") == 0 )
this->fIndex = INT_MAX-1;
+ else if ( strcmp(sectionName, "__unwind_info") == 0 )
+ this->fIndex = INT_MAX-2;
else if ( strcmp(sectionName, "__gcc_except_tab") == 0 )
- this->fIndex = INT_MAX-2;
+ this->fIndex = INT_MAX-3;
}
else if ( strcmp(segmentName, "__DATA") == 0 ) {
+ // sort arm lazy symbol pointers that must be at start of __DATA
+ if ( strcmp(sectionName, "__lazy_symbol") == 0 )
+ this->fIndex = 0;
// sort sections dyld will touch to start of segment
- if ( strcmp(sectionName, "__dyld") == 0 )
+ else if ( strcmp(sectionName, "__dyld") == 0 )
this->fIndex = 1;
else if ( strcmp(sectionName, "__program_vars") == 0 )
this->fIndex = 1;
this->fIndex = 18;
else if ( strcmp(sectionName, "__objc_imageinfo") == 0 )
this->fIndex = 19;
+ else if ( strcmp(sectionName, "__huge") == 0 )
+ this->fIndex = INT_MAX;
}
};
ObjectFile::Reader* createReader(const Options::FileInfo&);
+ const char* fileArch(const void* p);
void addAtom(ObjectFile::Atom& atom);
void addAtoms(std::vector<class ObjectFile::Atom*>& atoms);
void buildAtomList();
void loadAndResolve();
void processDTrace();
void checkObjC();
+ void addSynthesizedAtoms();
void loadUndefines();
void checkUndefines();
void resolveReferences();
static const char* truncateStabString(const char* str);
void collectDebugInfo();
void writeOutput();
- ObjectFile::Atom* entryPoint(bool orInit);
+ ObjectFile::Atom* entryPoint(bool orInit, bool searchArchives=false);
ObjectFile::Atom* dyldClassicHelper();
ObjectFile::Atom* dyldCompressedHelper();
ObjectFile::Atom* dyldLazyLibraryHelper();
void require(const char* name);
bool add(ObjectFile::Atom& atom);
ObjectFile::Atom* find(const char* name);
+ void erase(const char* name);
unsigned int getRequireCount() { return fRequireCount; }
void getUndefinesNames(std::vector<const char*>& undefines);
void getTentativesNames(std::vector<const char*>& tents);
bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; }
bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; }
void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; }
+ uint32_t dylibSymbolCount() { return fDylibSymbolCount; }
Mapper::iterator begin() { return fTable.begin(); }
Mapper::iterator end() { return fTable.end(); }
unsigned int fRequireCount;
bool fHasExternalTentativeDefinitions;
bool fHasExternalWeakDefinitions;
+ uint32_t fDylibSymbolCount;
};
class AtomSorter
break;
case CPU_TYPE_ARM:
fArchitectureName = "arm";
+ if ( fOptions.preferSubArchitecture() ) {
+ switch ( fOptions.subArchitecture() ) {
+ case CPU_SUBTYPE_ARM_V4T:
+ fArchitectureName = "armv4t";
+ break;
+ case CPU_SUBTYPE_ARM_V5TEJ:
+ fArchitectureName = "armv5";
+ break;
+ case CPU_SUBTYPE_ARM_V6:
+ fArchitectureName = "armv6";
+ break;
+ case CPU_SUBTYPE_ARM_V7:
+ fArchitectureName = "armv7";
+ break;
+ }
+ }
break;
default:
fArchitectureName = "unknown architecture";
}
}
+void Linker::addSynthesizedAtoms()
+{
+ // give write a chance to synthesize stub, GOT, and lazy pointer atoms
+ std::vector<class ObjectFile::Atom*> newAtoms;
+ fOutputFile->addSynthesizedAtoms(fAllAtoms, this->dyldClassicHelper(),
+ this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(),
+ fBiggerThanTwoGigOutput,
+ fGlobalSymbolTable.dylibSymbolCount(),
+ newAtoms);
+
+ // add all newly created atoms to fAllAtoms and update symbol table
+ this->addAtoms(newAtoms);
+}
+
void Linker::optimize()
{
// give each reader a chance to do any optimizations
// only do next steps if some optimization was actually done
if ( didSomething ) {
+
+ if ( fOptions.deadStrip() != Options::kDeadStripOff ) {
+ for(std::vector<class ObjectFile::Atom*>::iterator itr = newAtoms.begin(); itr != newAtoms.end(); ++itr) {
+ ObjectFile::Atom* atom = *itr;
+ const char* name = atom->getName();
+ if ( name != NULL ) {
+ ObjectFile::Atom* existingAtom = fGlobalSymbolTable.find(name);
+ if ( (existingAtom != NULL) && fLiveAtoms.count(existingAtom) == 0 ) {
+ // While dead code stripping, the atoms were not removed from fGlobalSymbolTable
+ // for performance reasons. Normally, libLTO will never recreate an atom
+ // that was previously dead stripped away, but if it does remove
+ // the remnents of the previous so the new one can be added
+ fGlobalSymbolTable.erase(name);
+ }
+ }
+ }
+ }
+
// add all newly created atoms to fAllAtoms and update symbol table
this->addAtoms(newAtoms);
// LTO may optimize away some atoms, so dead stripping must be redone
fLiveAtoms.clear();
this->deadStripResolve();
+ this->checkUndefines();
}
else {
// LTO may require new library symbols to be loaded, so redo
this->checkObjC();
this->processDTrace();
this->tweakLayout();
+ this->addSynthesizedAtoms();
this->sortSections();
this->sortAtoms();
this->writeDotOutput();
}
}
// scan command line options
- if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) {
- fprintf(stderr, " -exported_symbols_list command line option\n");
+ if ( !foundAtomReference ) {
+ // might be from -init command line option
+ if ( (fOptions.initFunctionName() != NULL) && (strcmp(name, fOptions.initFunctionName()) == 0) ) {
+ fprintf(stderr, " -init command line option\n");
+ }
+ // or might be from exported symbol option
+ else if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) {
+ fprintf(stderr, " -exported_symbol[s_list] command line option\n");
+ }
+ else {
+ bool isInitialUndefine = false;
+ std::vector<const char*>& clundefs = fOptions.initialUndefines();
+ for (std::vector<const char*>::iterator uit = clundefs.begin(); uit != clundefs.end(); ++uit) {
+ if ( strcmp(*uit, name) == 0 ) {
+ isInitialUndefine = true;
+ break;
+ }
+ }
+ if ( isInitialUndefine )
+ fprintf(stderr, " -u command line option\n");
+ }
++unresolvableExportsCount;
}
+ // be helpful and check for typos
+ bool printedStart = false;
+ for (SymbolTable::Mapper::iterator sit=fGlobalSymbolTable.begin(); sit != fGlobalSymbolTable.end(); ++sit) {
+ if ( (sit->second != NULL) && (strstr(sit->first, name) != NULL) ) {
+ if ( ! printedStart ) {
+ fprintf(stderr, " (maybe you meant: %s", sit->first);
+ printedStart = true;
+ }
+ else {
+ fprintf(stderr, ", %s ", sit->first);
+ }
+ }
+ }
+ if ( printedStart )
+ fprintf(stderr, ")\n");
}
}
if ( doError )
void Linker::deadStripResolve()
{
// add main() to live roots
- ObjectFile::Atom* entryPoint = this->entryPoint(false);
+ ObjectFile::Atom* entryPoint = this->entryPoint(false, true);
if ( entryPoint != NULL )
fLiveRootAtoms.insert(entryPoint);
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);
+ throwf("offsetsInDOF[%d]=%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());
theOrdinalOverrideMap[atom] = index;
if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath());
}
+ ++matchCount;
}
else {
- ++matchCount;
- //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName);
+ if ( fOptions.printOrderFileStatistics() ) {
+ if ( it->objectFileName == NULL )
+ warning("can't find match for order_file entry: %s", it->symbolName);
+ else
+ warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName);
+ }
}
++index;
}
//fprintf(stderr, "Sorted atoms:\n");
//for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- // fprintf(stderr, "\t%p, %u %s\t%s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath());
+ // fprintf(stderr, "\t%s, %u %s\t%s\n", (*it)->getSectionName(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath());
//}
}
}
}
-ObjectFile::Atom* Linker::entryPoint(bool orInit)
+ObjectFile::Atom* Linker::entryPoint(bool orInit, bool searchArchives)
{
// if main executable, find entry point atom
ObjectFile::Atom* entryPoint = NULL;
case Options::kDyld:
case Options::kPreload:
entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
+ if ( (entryPoint == NULL) && searchArchives ) {
+ // <rdar://problem/7043256> ld64 can not find a -e entry point from an archive
+ this->addJustInTimeAtoms(fOptions.entryName(), false, true, false);
+ entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
+ }
if ( entryPoint == NULL ) {
throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName());
}
void Linker::writeOutput()
{
+ // <rdar://problem/6933931> ld -r of empty .o file should preserve sub-type
+ // <rdar://problem/7049478> empty dylib should have subtype from command line
+ if ( fOptions.preferSubArchitecture() && (fOptions.architecture() == CPU_TYPE_ARM) ) {
+ fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)fOptions.subArchitecture();
+ }
+
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(true),
- this->dyldClassicHelper(),this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(),
fCreateUUID, fCanScatter,
- fCurrentCpuConstraint, fBiggerThanTwoGigOutput,
+ fCurrentCpuConstraint,
fRegularDefAtomsThatOverrideADylibsWeakDef,
fGlobalSymbolTable.hasExternalWeakDefinitions());
}
+const char* Linker::fileArch(const void* p)
+{
+ const uint8_t* bytes = (uint8_t*)p;
+ const char* result;
+ result = mach_o::relocatable::Reader<ppc>::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+ result = mach_o::relocatable::Reader<ppc64>::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+ result = mach_o::relocatable::Reader<x86>::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+ result = mach_o::relocatable::Reader<x86_64>::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+ result = mach_o::relocatable::Reader<arm>::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+
+ result = lto::Reader::fileKind(bytes);
+ if ( result != NULL )
+ return result;
+
+ return "unsupported file format";
+}
+
ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info)
{
// map in whole file
// if fat file, skip to architecture we want
// Note: fat header is always big-endian
+ bool isFatFile = false;
const fat_header* fh = (fat_header*)p;
if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ isFatFile = true;
const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
uint32_t sliceToUse;
bool sliceFound = false;
if ( lto::Reader::validFile(p, len, fArchitecture) ) {
return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len);
}
- else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) {
- throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded";
+ else if ( lto::Reader::fileKind((uint8_t*)p) != NULL ) {
+ if ( lto::Reader::loaded() ) {
+ throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName);
+ }
+ else {
+ const char* libLTO = "libLTO.dylib";
+ char ldPath[PATH_MAX];
+ char tmpPath[PATH_MAX];
+ char libLTOPath[PATH_MAX];
+ uint32_t bufSize = PATH_MAX;
+ if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) {
+ if ( realpath(ldPath, tmpPath) != NULL ) {
+ char* lastSlash = strrchr(tmpPath, '/');
+ if ( lastSlash != NULL )
+ strcpy(lastSlash, "/../lib/libLTO.dylib");
+ libLTO = tmpPath;
+ if ( realpath(tmpPath, libLTOPath) != NULL )
+ libLTO = libLTOPath;
+ }
+ }
+ throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO);
+ }
}
#endif
// error handling
throwf("missing required architecture %s in file", fArchitectureName);
}
else {
- throw "file is not of required architecture";
+ if ( isFatFile )
+ throwf("file is universal but does not contain a(n) %s slice", fArchitectureName);
+ else
+ throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName);
}
}
case ObjectFile::Atom::kWeakDefinition:
fHasExternalWeakDefinitions = true;
break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ ++fDylibSymbolCount;
+ break;
default:
break;
}
return NULL;
}
+void Linker::SymbolTable::erase(const char* name) {
+ fTable.erase(name);
+}
void Linker::SymbolTable::getUndefinesNames(std::vector<const char*>& undefines)
{
}
}
+ // magic section$end symbol always sorts to the end of its section
+ if ( left->getContentType() == ObjectFile::Atom::kSectionEnd )
+ return false;
+ if ( right->getContentType() == ObjectFile::Atom::kSectionEnd )
+ return true;
+
// 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);
if ( leftIsTent != rightIsTent )
return rightIsTent;
- // magic section$end symbol always sorts to the end of its section
- if ( left->getContentType() == ObjectFile::Atom::kSectionEnd )
- return false;
- if ( right->getContentType() == ObjectFile::Atom::kSectionEnd )
- return true;
-
// initializers are auto sorted to start of section
if ( !fInitializerSet.empty() ) {
bool leftFirst = (fInitializerSet.count(left) != 0);