+
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
*
- * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <mach-o/fat.h>
+#include <sys/sysctl.h>
+#include <libkern/OSAtomic.h>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <algorithm>
-#include <ext/hash_map>
-#include <ext/hash_set>
#include <dlfcn.h>
#include <AvailabilityMacros.h>
#include "InputFiles.h"
#include "macho_relocatable_file.h"
#include "macho_dylib_file.h"
+#include "textstub_dylib_file.hpp"
#include "archive_file.h"
#include "lto_file.h"
#include "opaque_section_file.h"
+#include "MachOFileAbstraction.hpp"
+#include "Snapshot.h"
+const bool _s_logPThreads = false;
namespace ld {
namespace tool {
-
+class IgnoredFile : public ld::File {
+public:
+ IgnoredFile(const char* pth, time_t modTime, Ordinal ord, Type type) : ld::File(pth, modTime, ord, type) {};
+ virtual bool forEachAtom(AtomHandler&) const { return false; };
+ virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; };
+};
class DSOHandleAtom : public ld::Atom {
public:
DSOHandleAtom(const char* nm, ld::Atom::Scope sc,
- ld::Atom::SymbolTableInclusion inc, bool preload=false)
- : ld::Atom(preload ? _s_section_preload : _s_section,
- ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::SymbolTableInclusion inc, ld::Section& sect=_s_section)
+ : ld::Atom(sect, ld::Atom::definitionRegular,
+ (sect == _s_section_text) ? ld::Atom::combineByName : ld::Atom::combineNever,
+ // make "weak def" so that link succeeds even if app defines __dso_handle
sc, ld::Atom::typeUnclassified, inc, true, false, false,
ld::Atom::Alignment(1)), _name(nm) {}
virtual ld::File* file() const { return NULL; }
- virtual bool translationUnitSource(const char** dir, const char** ) const
- { return false; }
- virtual const char* name() const { return _name; }
+ virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 0; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const
static ld::Section _s_section;
static ld::Section _s_section_preload;
+ static ld::Section _s_section_text;
static DSOHandleAtom _s_atomAll;
static DSOHandleAtom _s_atomExecutable;
static DSOHandleAtom _s_atomDylib;
static DSOHandleAtom _s_atomDyld;
static DSOHandleAtom _s_atomObjectFile;
static DSOHandleAtom _s_atomPreload;
+ static DSOHandleAtom _s_atomPreloadDSO;
private:
const char* _name;
};
ld::Section DSOHandleAtom::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true);
ld::Section DSOHandleAtom::_s_section_preload("__HEADER", "__mach_header", ld::Section::typeMachHeader, true);
+ld::Section DSOHandleAtom::_s_section_text("__TEXT", "__text", ld::Section::typeCode, false);
DSOHandleAtom DSOHandleAtom::_s_atomAll("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn);
DSOHandleAtom DSOHandleAtom::_s_atomExecutable("__mh_execute_header", ld::Atom::scopeGlobal, ld::Atom::symbolTableInAndNeverStrip);
DSOHandleAtom DSOHandleAtom::_s_atomDylib("__mh_dylib_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn);
DSOHandleAtom DSOHandleAtom::_s_atomBundle("__mh_bundle_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn);
DSOHandleAtom DSOHandleAtom::_s_atomDyld("__mh_dylinker_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn);
DSOHandleAtom DSOHandleAtom::_s_atomObjectFile("__mh_object_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn);
-DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, true);
+DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, _s_section_preload);
+DSOHandleAtom DSOHandleAtom::_s_atomPreloadDSO("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, _s_section_text);
_size(sz) {}
virtual ld::File* file() const { return NULL; }
- virtual bool translationUnitSource(const char** dir, const char** ) const
- { return false; }
virtual const char* name() const { return "page zero"; }
virtual uint64_t size() const { return _size; }
virtual uint64_t objectAddress() const { return 0; }
_size(sz) {}
virtual ld::File* file() const { return NULL; }
- virtual bool translationUnitSource(const char** dir, const char** ) const
- { return false; }
virtual const char* name() const { return "custom stack"; }
virtual uint64_t size() const { return _size; }
virtual uint64_t objectAddress() const { return 0; }
const char* result = mach_o::relocatable::archName(p);
if ( result != NULL )
return result;
-
+
+ result = mach_o::dylib::archName(p);
+ if ( result != NULL )
+ return result;
+
result = lto::archName(p, len);
if ( result != NULL )
return result;
if ( strncmp((const char*)p, "!<arch>\n", 8) == 0 )
return "archive";
- return "unsupported file format";
+ char *unsupported = (char *)malloc(128);
+ strcpy(unsupported, "unsupported file format (");
+ for (unsigned i=0; i<len && i < 16; i++) {
+ char buf[8];
+ sprintf(buf, " 0x%02X", p[i]);
+ strcat(unsupported, buf);
+ }
+ strcat(unsupported, " )");
+ return unsupported;
}
-ld::File* InputFiles::makeFile(const Options::FileInfo& info)
+ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib)
{
// map in whole file
uint64_t len = info.fileLen;
if ( fd == -1 )
throwf("can't open file, errno=%d", errno);
if ( info.fileLen < 20 )
- throw "file too small";
+ throwf("file too small (length=%llu)", info.fileLen);
uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if ( p == (uint8_t*)(-1) )
// if fat file, skip to architecture we want
// Note: fat header is always big-endian
bool isFatFile = false;
+ uint32_t sliceToUse, sliceCount;
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;
+ sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
if ( _options.preferSubArchitecture() ) {
// first try to find a slice that match cpu-type and cpu-sub-type
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ for (uint32_t i=0; i < sliceCount; ++i) {
if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture())
&& (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) {
sliceToUse = i;
}
if ( !sliceFound ) {
// look for any slice that matches just cpu-type
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ for (uint32_t i=0; i < sliceCount; ++i) {
if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) {
sliceToUse = i;
sliceFound = true;
uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset);
len = OSSwapBigToHostInt32(archs[sliceToUse].size);
if ( fileOffset+len > info.fileLen ) {
- throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu",
+ // <rdar://problem/17593430> file size was read awhile ago. If file is being written, wait a second to see if big enough now
+ sleep(1);
+ uint64_t newFileLen = info.fileLen;
+ struct stat statBuffer;
+ if ( stat(info.path, &statBuffer) == 0 ) {
+ newFileLen = statBuffer.st_size;
+ }
+ if ( fileOffset+len > newFileLen ) {
+ throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu",
fileOffset, fileOffset+len, info.fileLen);
+ }
}
// if requested architecture is page aligned within fat file, then remap just that portion of file
if ( (fileOffset & 0x00000FFF) == 0 ) {
// see if it is an object file
mach_o::relocatable::ParserOptions objOpts;
objOpts.architecture = _options.architecture();
- objOpts.objSubtypeMustMatch = _options.preferSubArchitecture();
+ objOpts.objSubtypeMustMatch = !_options.allowSubArchitectureMismatches();
objOpts.logAllFiles = _options.logAllFiles();
- objOpts.convertUnwindInfo = _options.needsUnwindInfoSection();
+ objOpts.warnUnwindConversionProblems = _options.needsUnwindInfoSection();
+ objOpts.keepDwarfUnwind = _options.keepDwarfUnwind();
+ objOpts.forceDwarfConversion= (_options.outputKind() == Options::kDyld);
+ objOpts.neverConvertDwarf = !_options.needsUnwindInfoSection();
+ objOpts.verboseOptimizationHints = _options.verboseOptimizationHints();
+ objOpts.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions();
+ objOpts.simulator = _options.targetIOSSimulator();
+ objOpts.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable));
objOpts.subType = _options.subArchitecture();
- ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, _nextInputOrdinal, objOpts);
- if ( objResult != NULL )
- return this->addObject(objResult, info, len);
+ objOpts.platform = _options.platform();
+ objOpts.minOSVersion = _options.minOSversion();
+ objOpts.srcKind = ld::relocatable::File::kSourceObj;
+ objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData;
+ objOpts.usingBitcode = _options.bundleBitcode();
+
+ ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts);
+ if ( objResult != NULL ) {
+ OSAtomicAdd64(len, &_totalObjectSize);
+ OSAtomicIncrement32(&_totalObjectLoaded);
+ return objResult;
+ }
// see if it is an llvm object file
- objResult = lto::parse(p, len, info.path, info.modTime, _nextInputOrdinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles());
- if ( objResult != NULL )
- return this->addObject(objResult, info, len);
-
- // see if it is a dynamic library
- ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader);
- if ( dylibResult != NULL )
- return this->addDylib(dylibResult, info, len);
+ objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles(), _options.verboseOptimizationHints());
+ if ( objResult != NULL ) {
+ OSAtomicAdd64(len, &_totalObjectSize);
+ OSAtomicIncrement32(&_totalObjectLoaded);
+ return objResult;
+ }
+
+ // see if it is a dynamic library (or text-based dynamic library)
+ ld::dylib::File* dylibResult;
+ bool dylibsNotAllowed = false;
+ switch ( _options.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib);
+ if ( dylibResult != NULL ) {
+ return dylibResult;
+ }
+ dylibResult = textstub::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib);
+ if ( dylibResult != NULL ) {
+ return dylibResult;
+ }
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ dylibsNotAllowed = true;
+ break;
+ }
// see if it is a static library
- archive::ParserOptions archOpts;
+ ::archive::ParserOptions archOpts;
archOpts.objOpts = objOpts;
archOpts.forceLoadThisArchive = info.options.fForceLoad;
archOpts.forceLoadAll = _options.fullyLoadArchives();
archOpts.forceLoadObjC = _options.loadAllObjcObjectsFromArchives();
+ archOpts.objcABI2 = _options.objCABIVersion2POverride();
archOpts.verboseLoad = _options.whyLoad();
archOpts.logAllFiles = _options.logAllFiles();
- ld::File* archiveResult = archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts);
- if ( archiveResult != NULL )
- return this->addArchive(archiveResult, info, len);
-
+ // Set ObjSource Kind, libclang_rt is compiler static library
+ const char* libName = strrchr(info.path, '/');
+ if ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) )
+ archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive;
+ else
+ archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive;
+ archOpts.objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData;
+ archOpts.objOpts.usingBitcode = _options.bundleBitcode();
+
+ ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts);
+ if ( archiveResult != NULL ) {
+ OSAtomicAdd64(len, &_totalArchiveSize);
+ OSAtomicIncrement32(&_totalArchivesLoaded);
+ return archiveResult;
+ }
+
// does not seem to be any valid linker input file, check LTO misconfiguration problems
if ( lto::archName((uint8_t*)p, len) != NULL ) {
if ( lto::libLTOisLoaded() ) {
- throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName());
+ throwf("lto file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path);
}
else {
const char* libLTO = "libLTO.dylib";
char tmpPath[PATH_MAX];
char libLTOPath[PATH_MAX];
uint32_t bufSize = PATH_MAX;
- if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) {
+ if ( _options.overridePathlibLTO() != NULL ) {
+ libLTO = _options.overridePathlibLTO();
+ }
+ else if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) {
if ( realpath(ldPath, tmpPath) != NULL ) {
char* lastSlash = strrchr(tmpPath, '/');
if ( lastSlash != NULL )
}
}
+ if ( dylibsNotAllowed ) {
+ cpu_type_t dummy1;
+ cpu_type_t dummy2;
+ if ( mach_o::dylib::isDylibFile(p, &dummy1, &dummy2) )
+ throw "ignoring unexpected dylib file";
+ }
+
// error handling
if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- throwf("missing required architecture %s in file", _options.architectureName());
+ throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount);
}
else {
if ( isFatFile )
- throwf("file is universal but does not contain a(n) %s slice", _options.architectureName());
+ throwf("file is universal (%u slices) but does not contain a(n) %s slice: %s", sliceCount, _options.architectureName(), info.path);
else
- throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName());
+ throwf("file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path);
}
}
logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath);
}
else {
- if ( indirect )
+ if ( indirect )
logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
- else
+ else
logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
}
}
+
+ if ( _options.dumpDependencyInfo() ) {
+ const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(file);
+ if ( file == _bundleLoader ) {
+ _options.dumpDependency(Options::depBundleLoader, file->path());
+ }
+ else if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) {
+ if ( indirect )
+ _options.dumpDependency(Options::depUpwardIndirectDylib, file->path());
+ else
+ _options.dumpDependency(Options::depUpwardDirectDylib, file->path());
+ }
+ else {
+ if ( indirect )
+ _options.dumpDependency(Options::depIndirectDylib, file->path());
+ else
+ _options.dumpDependency(Options::depDirectDylib, file->path());
+ }
+ }
}
void InputFiles::logArchive(ld::File* file) const
if ( trace_file_path != NULL ) {
trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666);
if ( trace_file == -1 )
- throwf("Could not open or create trace file: %s", trace_file_path);
+ throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path);
}
else {
trace_file = fileno(stderr);
}
}
+
ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath)
{
//fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath);
if ( strcmp(dit->installName,installPath) == 0 ) {
try {
Options::FileInfo info = _options.findFile(dit->useInstead);
- ld::File* reader = this->makeFile(info);
+ _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
+ info.ordinal = _indirectDylibOrdinal;
+ info.options.fIndirectDylib = true;
+ ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
+ addDylib(dylibReader, info);
//_installPathToDylibs[strdup(installPath)] = dylibReader;
this->logDylib(dylibReader, true);
return dylibReader;
// note: @executable_path case is handled inside findFileUsingPaths()
// search for dylib using -F and -L paths
Options::FileInfo info = _options.findFileUsingPaths(installPath);
+ _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
+ info.ordinal = _indirectDylibOrdinal;
+ info.options.fIndirectDylib = true;
try {
- ld::File* reader = this->makeFile(info);
+ ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
//assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end());
//_installPathToDylibs[strdup(installPath)] = dylibReader;
+ addDylib(dylibReader, info);
this->logDylib(dylibReader, true);
return dylibReader;
}
throwf("indirect dylib at %s is not a dylib", info.path);
}
catch (const char* msg) {
- throwf("in %s, %s", info.path, msg);
+ throwf("in '%s', %s", info.path, msg);
}
}
}
-
-void InputFiles::createIndirectDylibs()
-{
- _allDirectDylibsLoaded = true;
-
- // mark all dylibs initially specified as required and check if they can be used
+// mark all dylibs initially specified as required, and check if they can be used
+void InputFiles::markExplicitlyLinkedDylibs()
+{
for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) {
it->second->setExplicitlyLinked();
this->checkDylibClientRestrictions(it->second);
}
-
+}
+
+bool InputFiles::libraryAlreadyLoaded(const char* path)
+{
+ for (std::vector<ld::File*>::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) {
+ if ( strcmp(path, (*it)->path()) == 0 )
+ return true;
+ }
+ return false;
+}
+
+
+void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler)
+{
+ if ( _options.outputKind() == Options::kObjectFile )
+ return;
+
+ // process frameworks specified in .o linker options
+ for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) {
+ const char* frameworkName = *it;
+ Options::FileInfo info = _options.findFramework(frameworkName);
+ if ( ! this->libraryAlreadyLoaded(info.path) ) {
+ info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
+ try {
+ ld::File* reader = this->makeFile(info, true);
+ ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
+ if ( dylibReader != NULL ) {
+ if ( ! dylibReader->installPathVersionSpecific() ) {
+ dylibReader->forEachAtom(handler);
+ dylibReader->setImplicitlyLinked();
+ this->addDylib(dylibReader, info);
+ }
+ }
+ else {
+ throwf("framework linker option at %s is not a dylib", info.path);
+ }
+ }
+ catch (const char* msg) {
+ warning("Auto-Linking supplied '%s', %s", info.path, msg);
+ }
+ }
+ }
+ // process libraries specified in .o linker options
+ for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) {
+ const char* libName = *it;
+ Options::FileInfo info = _options.findLibrary(libName);
+ if ( ! this->libraryAlreadyLoaded(info.path) ) {
+ info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
+ try {
+ //<rdar://problem/17787306> -force_load_swift_libs
+ info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0);
+ ld::File* reader = this->makeFile(info, true);
+ ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
+ ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
+ if ( dylibReader != NULL ) {
+ dylibReader->forEachAtom(handler);
+ dylibReader->setImplicitlyLinked();
+ this->addDylib(dylibReader, info);
+ }
+ else if ( archiveReader != NULL ) {
+ _searchLibraries.push_back(LibraryInfo(archiveReader));
+ if ( _options.dumpDependencyInfo() )
+ _options.dumpDependency(Options::depArchive, archiveReader->path());
+ //<rdar://problem/17787306> -force_load_swift_libs
+ if (info.options.fForceLoad) {
+ archiveReader->forEachAtom(handler);
+ }
+ }
+ else {
+ throwf("linker option dylib at %s is not a dylib", info.path);
+ }
+ }
+ catch (const char* msg) {
+ warning("Auto-Linking supplied '%s', %s", info.path, msg);
+ }
+ }
+ }
+}
+
+void InputFiles::createIndirectDylibs()
+{
// keep processing dylibs until no more dylibs are added
unsigned long lastMapSize = 0;
- std::set<ld::dylib::File*> dylibsProcessed;
+ std::set<ld::dylib::File*> dylibsProcessed;
while ( lastMapSize != _allDylibs.size() ) {
lastMapSize = _allDylibs.size();
// can't iterator _installPathToDylibs while modifying it, so use temp buffer
void InputFiles::createOpaqueFileSections()
{
- // extra command line section always at end
+ // extra command line sections always at end
for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) {
- _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data,
- it->dataLen, _nextInputOrdinal));
- // bump ordinal
- _nextInputOrdinal++;
+ _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen));
+ if ( _options.dumpDependencyInfo() )
+ _options.dumpDependency(Options::depSection, it->path);
}
}
_inferredArch = true;
// 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)];
+ uint8_t buffer[4096];
const std::vector<Options::FileInfo>& files = opts.getInputFiles();
for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
int fd = ::open(it->path, O_RDONLY, 0);
if ( fd != -1 ) {
- ssize_t amount = read(fd, buffer, sizeof(buffer));
- ::close(fd);
- if ( amount >= (ssize_t)sizeof(buffer) ) {
- cpu_type_t type;
- cpu_subtype_t subtype;
- if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) {
- opts.setArchitecture(type, subtype);
- *archName = opts.architectureName();
- return;
+ struct stat stat_buf;
+ if ( fstat(fd, &stat_buf) != -1) {
+ ssize_t readAmount = stat_buf.st_size;
+ if ( 4096 < readAmount )
+ readAmount = 4096;
+ ssize_t amount = read(fd, buffer, readAmount);
+ ::close(fd);
+ if ( amount >= readAmount ) {
+ cpu_type_t type;
+ cpu_subtype_t subtype;
+ Options::Platform platform;
+ if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype, &platform) ) {
+ opts.setArchitecture(type, subtype, platform);
+ *archName = opts.architectureName();
+ return;
+ }
}
}
}
// no thin .o files found, so default to same architecture this tool was built as
warning("-arch not specified");
-#if __ppc__
- opts.setArchitecture(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
-#elif __i386__
- opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL);
-#elif __ppc64__
- opts.setArchitecture(CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL);
+#if __i386__
+ opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, Options::kPlatformOSX);
#elif __x86_64__
- opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL);
+ opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX);
#elif __arm__
- opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6);
+ opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, Options::kPlatformOSX);
#else
#error unknown default architecture
#endif
InputFiles::InputFiles(Options& opts, const char** archName)
: _totalObjectSize(0), _totalArchiveSize(0),
_totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0),
- _options(opts), _bundleLoader(NULL), _nextInputOrdinal(1),
- _allDirectDylibsLoaded(false), _inferredArch(false)
+ _options(opts), _bundleLoader(NULL),
+ _inferredArch(false),
+ _exception(NULL),
+ _indirectDylibOrdinal(ld::File::Ordinal::indirectDylibBase()),
+ _linkerOptionOrdinal(ld::File::Ordinal::linkeOptionBase())
{
// fStartCreateReadersTime = mach_absolute_time();
if ( opts.architecture() == 0 ) {
// command line missing -arch, so guess arch
inferArchitecture(opts, archName);
}
-
- const std::vector<Options::FileInfo>& files = opts.getInputFiles();
+#if HAVE_PTHREADS
+ pthread_mutex_init(&_parseLock, NULL);
+ pthread_cond_init(&_parseWorkReady, NULL);
+ pthread_cond_init(&_newFileAvailable, NULL);
+#endif
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
if ( files.size() == 0 )
throw "no object files specified";
- // add all direct object, archives, and dylibs
+
_inputFiles.reserve(files.size());
+#if HAVE_PTHREADS
+ unsigned int inputFileSlot = 0;
+ _availableInputFiles = 0;
+ _parseCursor = 0;
+#endif
+ Options::FileInfo* entry;
for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
- const Options::FileInfo& entry = *it;
- try {
- _inputFiles.push_back(this->makeFile(entry));
- }
- catch (const char* msg) {
- if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
- if ( opts.ignoreOtherArchInputFiles() ) {
- // ignore, because this is about an architecture not in use
+ entry = (Options::FileInfo*)&(*it);
+#if HAVE_PTHREADS
+ // Assign input file slots to all the FileInfos.
+ // Also chain all FileInfos into one big list to set up for worker threads to do parsing.
+ entry->inputFileSlot = inputFileSlot;
+ entry->readyToParse = !entry->fromFileList || !_options.pipelineEnabled();
+ if (entry->readyToParse)
+ _availableInputFiles++;
+ _inputFiles.push_back(NULL);
+ inputFileSlot++;
+#else
+ // In the non-threaded case just parse the file now.
+ _inputFiles.push_back(makeFile(*entry, false));
+#endif
+ }
+
+#if HAVE_PTHREADS
+ _remainingInputFiles = files.size();
+
+ // initialize info for parsing input files on worker threads
+ unsigned int ncpus;
+ int mib[2];
+ size_t len = sizeof(ncpus);
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ if (sysctl(mib, 2, &ncpus, &len, NULL, 0) != 0) {
+ ncpus = 1;
+ }
+ _availableWorkers = MIN(ncpus, files.size()); // max # workers we permit
+ _idleWorkers = 0;
+
+ if (_options.pipelineEnabled()) {
+ // start up a thread to listen for available input files
+ startThread(InputFiles::waitForInputFiles);
+ }
+
+ // Start up one parser thread. More start on demand as parsed input files get consumed.
+ startThread(InputFiles::parseWorkerThread);
+ _availableWorkers--;
+#else
+ if (_options.pipelineEnabled()) {
+ throwf("pipelined linking not supported on this platform");
+ }
+#endif
+}
+
+
+#if HAVE_PTHREADS
+void InputFiles::startThread(void (*threadFunc)(InputFiles *)) const {
+ pthread_t thread;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ // set a nice big stack (same as main thread) because some code uses potentially large stack buffers
+ pthread_attr_setstacksize(&attr, 8 * 1024 * 1024);
+ pthread_create(&thread, &attr, (void *(*)(void*))threadFunc, (void *)this);
+ pthread_detach(thread);
+ pthread_attr_destroy(&attr);
+}
+
+// Work loop for input file parsing threads
+void InputFiles::parseWorkerThread() {
+ ld::File *file;
+ const char *exception = NULL;
+ pthread_mutex_lock(&_parseLock);
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ if (_s_logPThreads) printf("worker starting\n");
+ do {
+ if (_availableInputFiles == 0) {
+ _idleWorkers++;
+ pthread_cond_wait(&_parseWorkReady, &_parseLock);
+ _idleWorkers--;
+ } else {
+ int slot = _parseCursor;
+ while (slot < (int)files.size() && (_inputFiles[slot] != NULL || !files[slot].readyToParse))
+ slot++;
+ assert(slot < (int)files.size());
+ Options::FileInfo& entry = (Options::FileInfo&)files[slot];
+ _parseCursor = slot+1;
+ _availableInputFiles--;
+ entry.readyToParse = false; // to avoid multiple threads finding this file
+ pthread_mutex_unlock(&_parseLock);
+ if (_s_logPThreads) printf("parsing index %u\n", slot);
+ try {
+ file = makeFile(entry, false);
+ }
+ catch (const char *msg) {
+ if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
+ if ( _options.ignoreOtherArchInputFiles() ) {
+ // ignore, because this is about an architecture not in use
+ }
+ else {
+ warning("ignoring file %s, %s", entry.path, msg);
+ }
+ }
+ else if ( strstr(msg, "ignoring unexpected") != NULL ) {
+ warning("%s, %s", entry.path, msg);
}
else {
- warning("ignoring file %s, %s", entry.path, msg);
+ asprintf((char**)&exception, "%s file '%s'", msg, entry.path);
}
+ file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other);
}
+ pthread_mutex_lock(&_parseLock);
+ if (_remainingInputFiles > 0)
+ _remainingInputFiles--;
+ if (_s_logPThreads) printf("done with index %u, %d remaining\n", slot, _remainingInputFiles);
+ if (exception) {
+ // We are about to die, so set to zero to stop other threads from doing unneeded work.
+ _remainingInputFiles = 0;
+ _exception = exception;
+ }
else {
- throwf("in %s, %s", entry.path, msg);
+ _inputFiles[slot] = file;
+ if (_neededFileSlot == slot)
+ pthread_cond_signal(&_newFileAvailable);
}
}
- }
-
- this->createIndirectDylibs();
- this->createOpaqueFileSections();
+ } while (_remainingInputFiles);
+ if (_s_logPThreads) printf("worker exiting\n");
+ pthread_cond_broadcast(&_parseWorkReady);
+ pthread_cond_signal(&_newFileAvailable);
+ pthread_mutex_unlock(&_parseLock);
}
-
-ld::File* InputFiles::addArchive(ld::File* reader, const Options::FileInfo& info, uint64_t mappedLen)
-{
- // bump ordinal
- _nextInputOrdinal += reader->subFileCount();
-
- // update stats
- _totalArchiveSize += mappedLen;
- _totalArchivesLoaded++;
- return reader;
-}
-
-
-ld::File* InputFiles::addObject(ld::relocatable::File* file, const Options::FileInfo& info, uint64_t mappedLen)
-{
- // bump ordinal
- _nextInputOrdinal++;
-
- // update stats
- _totalObjectSize += mappedLen;
- _totalObjectLoaded++;
- return file;
+void InputFiles::parseWorkerThread(InputFiles *inputFiles) {
+ inputFiles->parseWorkerThread();
}
+#endif
-ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen)
+ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info)
{
_allDylibs.insert(reader);
}
// store options about how dylib will be used in dylib itself
if ( info.options.fWeakImport )
- reader->setWillBeWeakLinked();
+ reader->setForcedWeakLinked();
if ( info.options.fReExport )
reader->setWillBeReExported();
if ( info.options.fUpward ) {
}
}
}
- if ( !dylibOnCommandLineTwice && !isSymlink )
- warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path());
+ // remove warning for <rdar://problem/10860629> Same install name for CoreServices and CFNetwork?
+ //if ( !dylibOnCommandLineTwice && !isSymlink )
+ // warning("dylibs with same install name: %p %s and %p %s", pos->second, pos->second->path(), reader, reader->path());
}
}
else if ( info.options.fBundleLoader )
_bundleLoader = reader;
// log direct readers
- if ( !_allDirectDylibsLoaded )
+ if ( ! info.options.fIndirectDylib )
this->logDylib(reader, false);
- // bump ordinal
- _nextInputOrdinal++;
-
// update stats
_totalDylibsLoaded++;
+ // just add direct libraries to search-first list
+ if ( ! info.options.fIndirectDylib )
+ _searchLibraries.push_back(LibraryInfo(reader));
+
return reader;
}
-bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const
+#if HAVE_PTHREADS
+// Called during pipelined linking to listen for available input files.
+// Available files are enqueued for parsing.
+void InputFiles::waitForInputFiles()
{
- bool didSomething = false;
- for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
- didSomething |= (*it)->forEachAtom(handler);
- }
- if ( didSomething || true ) {
- switch ( _options.outputKind() ) {
- case Options::kDynamicExecutable:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomExecutable);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- if ( _options.pageZeroSize() != 0 )
- handler.doAtom(*new PageZeroAtom(_options.pageZeroSize()));
- if ( _options.hasCustomStack() )
- handler.doAtom(*new CustomStackAtom(_options.customStackSize()));
- break;
- case Options::kDynamicLibrary:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomDylib);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kDynamicBundle:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomBundle);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kDyld:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomDyld);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
+ if (_s_logPThreads) printf("starting pipeline listener\n");
+ try {
+ const char *fifo = _options.pipelineFifo();
+ assert(fifo);
+ std::map<const char *, const Options::FileInfo*, strcompclass> fileMap;
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
+ const Options::FileInfo& entry = *it;
+ if (entry.fromFileList) {
+ fileMap[entry.path] = &entry;
+ }
+ }
+ FILE *fileStream = fopen(fifo, "r");
+ if (!fileStream)
+ throwf("pipelined linking error - failed to open stream. fopen() returns %s for \"%s\"\n", strerror(errno), fifo);
+ while (fileMap.size() > 0) {
+ char path_buf[PATH_MAX+1];
+ if (fgets(path_buf, PATH_MAX, fileStream) == NULL)
+ throwf("pipelined linking error - %lu missing input files", fileMap.size());
+ int len = strlen(path_buf);
+ if (path_buf[len-1] == '\n')
+ path_buf[len-1] = 0;
+ std::map<const char *, const Options::FileInfo*, strcompclass>::iterator it = fileMap.find(path_buf);
+ if (it == fileMap.end())
+ throwf("pipelined linking error - not in file list: %s\n", path_buf);
+ Options::FileInfo* inputInfo = (Options::FileInfo*)it->second;
+ if (!inputInfo->checkFileExists(_options))
+ throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path);
+ pthread_mutex_lock(&_parseLock);
+ if (_idleWorkers)
+ pthread_cond_signal(&_parseWorkReady);
+ inputInfo->readyToParse = true;
+ if (_parseCursor > inputInfo->inputFileSlot)
+ _parseCursor = inputInfo->inputFileSlot;
+ _availableInputFiles++;
+ if (_s_logPThreads) printf("pipeline listener: %s slot=%d, _parseCursor=%d, _availableInputFiles = %d remaining = %ld\n", path_buf, inputInfo->inputFileSlot, _parseCursor, _availableInputFiles, fileMap.size()-1);
+ pthread_mutex_unlock(&_parseLock);
+ fileMap.erase(it);
+ }
+ } catch (const char *msg) {
+ pthread_mutex_lock(&_parseLock);
+ _exception = msg;
+ pthread_cond_signal(&_newFileAvailable);
+ pthread_mutex_unlock(&_parseLock);
+ }
+}
+
+
+void InputFiles::waitForInputFiles(InputFiles *inputFiles) {
+ inputFiles->waitForInputFiles();
+}
+#endif
+
+
+void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal& state)
+{
+ // add all direct object, archives, and dylibs
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ size_t fileIndex;
+ for (fileIndex=0; fileIndex<_inputFiles.size(); fileIndex++) {
+ ld::File *file;
+#if HAVE_PTHREADS
+ pthread_mutex_lock(&_parseLock);
+
+ // this loop waits for the needed file to be ready (parsed by worker thread)
+ while (_inputFiles[fileIndex] == NULL && _exception == NULL) {
+ // We are starved for input. If there are still files to parse and we have
+ // not maxed out the worker thread count start a new worker thread.
+ if (_availableInputFiles > 0 && _availableWorkers > 0) {
+ if (_s_logPThreads) printf("starting worker\n");
+ startThread(InputFiles::parseWorkerThread);
+ _availableWorkers--;
+ }
+ _neededFileSlot = fileIndex;
+ if (_s_logPThreads) printf("consumer blocking for %lu: %s\n", fileIndex, files[fileIndex].path);
+ pthread_cond_wait(&_newFileAvailable, &_parseLock);
+ }
+
+ if (_exception)
+ throw _exception;
+
+ // The input file is parsed. Assimilate it and call its atom iterator.
+ if (_s_logPThreads) printf("consuming slot %lu\n", fileIndex);
+ file = _inputFiles[fileIndex];
+ pthread_mutex_unlock(&_parseLock);
+#else
+ file = _inputFiles[fileIndex];
+#endif
+ const Options::FileInfo& info = files[fileIndex];
+ switch (file->type()) {
+ case ld::File::Reloc:
+ {
+ ld::relocatable::File* reloc = (ld::relocatable::File*)file;
+ _options.snapshot().recordObjectFile(reloc->path());
+ if ( _options.dumpDependencyInfo() )
+ _options.dumpDependency(Options::depObjectFile, reloc->path());
+ }
break;
- case Options::kStaticExecutable:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomExecutable);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
+ case ld::File::Dylib:
+ {
+ ld::dylib::File* dylib = (ld::dylib::File*)file;
+ addDylib(dylib, info);
+ }
break;
- case Options::kPreload:
- // add implicit __mh_preload_header label
- handler.doAtom(DSOHandleAtom::_s_atomPreload);
+ case ld::File::Archive:
+ {
+ ld::archive::File* archive = (ld::archive::File*)file;
+ // <rdar://problem/9740166> force loaded archives should be in LD_TRACE
+ if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() )
+ logArchive(archive);
+ _searchLibraries.push_back(LibraryInfo(archive));
+ if ( _options.dumpDependencyInfo() )
+ _options.dumpDependency(Options::depArchive, archive->path());
+ }
break;
- case Options::kObjectFile:
- handler.doAtom(DSOHandleAtom::_s_atomObjectFile);
+ case ld::File::Other:
break;
- case Options::kKextBundle:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomAll);
+ default:
+ {
+ throwf("Unknown file type for %s", file->path());
+ }
break;
}
+ file->forEachAtom(handler);
+ }
+
+ markExplicitlyLinkedDylibs();
+ addLinkerOptionLibraries(state, handler);
+ createIndirectDylibs();
+ createOpaqueFileSections();
+
+ while (fileIndex < _inputFiles.size()) {
+ ld::File *file = _inputFiles[fileIndex];
+ file->forEachAtom(handler);
+ fileIndex++;
+ }
+
+ switch ( _options.outputKind() ) {
+ case Options::kStaticExecutable:
+ case Options::kDynamicExecutable:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomExecutable);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ if ( _options.pageZeroSize() != 0 )
+ handler.doAtom(*new PageZeroAtom(_options.pageZeroSize()));
+ if ( _options.hasCustomStack() && !_options.needsEntryPointLoadCommand() )
+ handler.doAtom(*new CustomStackAtom(_options.customStackSize()));
+ break;
+ case Options::kDynamicLibrary:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomDylib);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kDynamicBundle:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomBundle);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kDyld:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomDyld);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kPreload:
+ // add implicit __mh_preload_header label
+ handler.doAtom(DSOHandleAtom::_s_atomPreload);
+ // add implicit __dso_handle label, but put it in __text section because
+ // with -preload the mach_header is no in the address space.
+ handler.doAtom(DSOHandleAtom::_s_atomPreloadDSO);
+ break;
+ case Options::kObjectFile:
+ handler.doAtom(DSOHandleAtom::_s_atomObjectFile);
+ break;
+ case Options::kKextBundle:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
}
- return didSomething;
}
-bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler& handler) const
+bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const
{
- // check each input file
- for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
- ld::File* file = *it;
- // 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.
- ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(file);
- if ( searchDylibs && (dylibFile != NULL) ) {
- //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() );
- if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
- // we found a definition in this dylib
- // done, unless it is a weak definition in which case we keep searching
- if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name))
- return true;
- // else continue search for a non-weak definition
- }
- }
- else if ( searchArchives && (dylibFile == NULL) ) {
- if ( file->justInTimeforEachAtom(name, handler) ) {
- if ( _options.traceArchives() )
- logArchive(file);
- // found definition in static library, done
- return true;
- }
- }
- }
+ // Check each input library.
+ for (std::vector<LibraryInfo>::const_iterator it=_searchLibraries.begin(); it != _searchLibraries.end(); ++it) {
+ LibraryInfo lib = *it;
+ if (lib.isDylib()) {
+ if (searchDylibs) {
+ ld::dylib::File *dylibFile = lib.dylib();
+ //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() );
+ if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
+ // we found a definition in this dylib
+ // done, unless it is a weak definition in which case we keep searching
+ _options.snapshot().recordDylibSymbol(dylibFile, name);
+ if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) {
+ return true;
+ }
+ // else continue search for a non-weak definition
+ }
+ }
+ } else {
+ if (searchArchives) {
+ ld::archive::File *archiveFile = lib.archive();
+ if ( dataSymbolOnly ) {
+ if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(archiveFile);
+ _options.snapshot().recordArchive(archiveFile->path());
+ // found data definition in static library, done
+ return true;
+ }
+ }
+ else {
+ if ( archiveFile->justInTimeforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(archiveFile);
+ _options.snapshot().recordArchive(archiveFile->path());
+ // found definition in static library, done
+ return true;
+ }
+ }
+ }
+ }
+ }
// search indirect dylibs
if ( searchDylibs ) {
if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
// we found a definition in this dylib
// done, unless it is a weak definition in which case we keep searching
- if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name))
+ _options.snapshot().recordDylibSymbol(dylibFile, name);
+ if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) {
return true;
+ }
// else continue search for a non-weak definition
}
}
bool InputFiles::searchWeakDefInDylib(const char* name) const
{
- // search all relevant dylibs to see if any of a weak-def with this name
+ // search all relevant dylibs to see if any have a weak-def with this name
for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
ld::dylib::File* dylibFile = it->second;
if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) {
}
return false;
}
+
+static bool vectorContains(const std::vector<ld::dylib::File*>& vec, ld::dylib::File* key)
+{
+ return std::find(vec.begin(), vec.end(), key) != vec.end();
+}
+
+struct DylibByInstallNameSorter
+{
+ bool operator()(const ld::dylib::File* left, const ld::dylib::File* right)
+ {
+ return (strcmp(left->installPath(), right->installPath()) < 0);
+ }
+};
void InputFiles::dylibs(ld::Internal& state)
{
- bool dylibsOK;
+ bool dylibsOK = false;
switch ( _options.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(*it);
// only add dylibs that are not "blank" dylib stubs
if ( (dylibFile != NULL) && ((dylibFile->installPath() != NULL) || (dylibFile == _bundleLoader)) ) {
- if ( dylibsOK )
- state.dylibs.push_back(dylibFile);
+ if ( dylibsOK ) {
+ if ( ! vectorContains(state.dylibs, dylibFile) ) {
+ state.dylibs.push_back(dylibFile);
+ }
+ }
else
warning("unexpected dylib (%s) on link line", dylibFile->path());
}
}
// add implicitly linked dylibs
if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) {
+ std::vector<ld::dylib::File*> implicitDylibs;
for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
ld::dylib::File* dylibFile = it->second;
- if ( dylibFile->implicitlyLinked() && dylibsOK )
- state.dylibs.push_back(dylibFile);
+ if ( dylibFile->implicitlyLinked() && dylibsOK ) {
+ if ( ! vectorContains(implicitDylibs, dylibFile) ) {
+ implicitDylibs.push_back(dylibFile);
+ }
+ }
}
+ // <rdar://problem/15002251> make implicit dylib order be deterministic by sorting by install_name
+ std::sort(implicitDylibs.begin(), implicitDylibs.end(), DylibByInstallNameSorter());
+ state.dylibs.insert(state.dylibs.end(), implicitDylibs.begin(), implicitDylibs.end());
}
+
+ //fprintf(stderr, "all dylibs:\n");
+ //for(std::vector<ld::dylib::File*>::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it) {
+ // const ld::dylib::File* dylib = *it;
+ // fprintf(stderr, " %p %s\n", dylib, dylib->path());
+ //}
+
// and -bundle_loader
state.bundleLoader = _bundleLoader;
+
+ // <rdar://problem/10807040> give an error when -nostdlib is used and libSystem is missing
+ if ( (state.dylibs.size() == 0) && _options.needsEntryPointLoadCommand() )
+ throw "dynamic main executables must link with libSystem.dylib";
}