+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
- *
- * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#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 <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 <set>
-#include <string>
-#include <vector>
-#include <list>
-#include <algorithm>
-#include <ext/hash_map>
-#include <ext/hash_set>
-#include <dlfcn.h>
-#include <AvailabilityMacros.h>
-
-#include "Options.h"
-
-#include "InputFiles.h"
-#include "macho_relocatable_file.h"
-#include "macho_dylib_file.h"
-#include "archive_file.h"
-#include "lto_file.h"
-#include "opaque_section_file.h"
-#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, 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 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
- { }
- virtual void setScope(Scope) { }
-
- virtual ~DSOHandleAtom() {}
-
- 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_atomBundle;
- 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, _s_section_preload);
-DSOHandleAtom DSOHandleAtom::_s_atomPreloadDSO("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, _s_section_text);
-
-
-
-class PageZeroAtom : public ld::Atom {
-public:
- PageZeroAtom(uint64_t sz)
- : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
- ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill,
- symbolTableNotIn, true, false, false, ld::Atom::Alignment(12)),
- _size(sz) {}
-
- virtual ld::File* file() const { return NULL; }
- virtual const char* name() const { return "page zero"; }
- virtual uint64_t size() const { return _size; }
- virtual uint64_t objectAddress() const { return 0; }
- virtual void copyRawContent(uint8_t buffer[]) const
- { }
- virtual void setScope(Scope) { }
-
- virtual ~PageZeroAtom() {}
-
- static ld::Section _s_section;
- static DSOHandleAtom _s_atomAll;
-private:
- uint64_t _size;
-};
-ld::Section PageZeroAtom::_s_section("__PAGEZERO", "__pagezero", ld::Section::typePageZero, true);
-
-
-class CustomStackAtom : public ld::Atom {
-public:
- CustomStackAtom(uint64_t sz)
- : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
- ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill,
- symbolTableNotIn, false, false, false, ld::Atom::Alignment(12)),
- _size(sz) {}
-
- virtual ld::File* file() const { return NULL; }
- virtual const char* name() const { return "custom stack"; }
- virtual uint64_t size() const { return _size; }
- virtual uint64_t objectAddress() const { return 0; }
- virtual void copyRawContent(uint8_t buffer[]) const
- { }
- virtual void setScope(Scope) { }
-
- virtual ~CustomStackAtom() {}
-
-private:
- uint64_t _size;
- static ld::Section _s_section;
-};
-ld::Section CustomStackAtom::_s_section("__UNIXSTACK", "__stack", ld::Section::typeStack, true);
-
-
-
-const char* InputFiles::fileArch(const uint8_t* p, unsigned len)
-{
- const char* result = mach_o::relocatable::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";
-
- 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%2x", p[i]);
- strcat(unsupported, buf);
- }
- strcat(unsupported, " )");
- return unsupported;
-}
-
-
-ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib)
-{
- // map in whole file
- uint64_t len = info.fileLen;
- int fd = ::open(info.path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open file, errno=%d", errno);
- if ( info.fileLen < 20 )
- throw "file too small";
-
- uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- if ( p == (uint8_t*)(-1) )
- throwf("can't map file, errno=%d", errno);
-
- // 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));
- 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 < sliceCount; ++i) {
- if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture())
- && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) {
- sliceToUse = i;
- sliceFound = true;
- break;
- }
- }
- }
- if ( !sliceFound ) {
- // look for any slice that matches just cpu-type
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) {
- sliceToUse = i;
- sliceFound = true;
- break;
- }
- }
- }
- if ( sliceFound ) {
- 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",
- 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 ) {
- // unmap whole file
- munmap((caddr_t)p, info.fileLen);
- // re-map just part we need
- p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset);
- if ( p == (uint8_t*)(-1) )
- throwf("can't re-map file, errno=%d", errno);
- }
- else {
- p = &p[fileOffset];
- }
- }
- }
- ::close(fd);
-
- // see if it is an object file
- mach_o::relocatable::ParserOptions objOpts;
- objOpts.architecture = _options.architecture();
- objOpts.objSubtypeMustMatch = !_options.allowSubArchitectureMismatches();
- objOpts.logAllFiles = _options.logAllFiles();
- objOpts.convertUnwindInfo = _options.needsUnwindInfoSection();
- objOpts.subType = _options.subArchitecture();
- 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, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles());
- if ( objResult != NULL ) {
- OSAtomicAdd64(len, &_totalObjectSize);
- OSAtomicIncrement32(&_totalObjectLoaded);
- return objResult;
- }
-
- // see if it is a dynamic library
- ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib);
- if ( dylibResult != NULL ) {
- return dylibResult;
- }
-
- // see if it is a static library
- ::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::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("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 ldPath[PATH_MAX];
- char tmpPath[PATH_MAX];
- char libLTOPath[PATH_MAX];
- uint32_t bufSize = PATH_MAX;
- 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 )
- 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);
- }
- }
-
- // error handling
- if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount);
- }
- else {
- if ( isFatFile )
- 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): %s", fileArch(p, len), _options.architectureName(), info.path);
- }
-}
-
-void InputFiles::logDylib(ld::File* file, bool indirect)
-{
- if ( _options.traceDylibs() ) {
- const char* fullPath = file->path();
- char realName[MAXPATHLEN];
- if ( realpath(fullPath, realName) != NULL )
- fullPath = realName;
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(file);
- if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) {
- // don't log upward dylibs when XBS is computing dependencies
- logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath);
- }
- else {
- if ( indirect )
- logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
- else
- logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
- }
- }
-}
-
-void InputFiles::logArchive(ld::File* file) const
-{
- if ( _options.traceArchives() && (_archiveFilesLogged.count(file) == 0) ) {
- // <rdar://problem/4947347> LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive
- _archiveFilesLogged.insert(file);
- const char* fullPath = file->path();
- char realName[MAXPATHLEN];
- if ( realpath(fullPath, realName) != NULL )
- fullPath = realName;
- logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath);
- }
-}
-
-
-void InputFiles::logTraceInfo(const char* format, ...) const
-{
- // one time open() of custom LD_TRACE_FILE
- static int trace_file = -1;
- if ( trace_file == -1 ) {
- const char *trace_file_path = _options.traceOutputFile();
- 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);
- }
- else {
- trace_file = fileno(stderr);
- }
- }
-
- char trace_buffer[MAXPATHLEN * 2];
- va_list ap;
- va_start(ap, format);
- int length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap);
- va_end(ap);
- char* buffer_ptr = trace_buffer;
-
- while (length > 0) {
- ssize_t amount_written = write(trace_file, buffer_ptr, length);
- if(amount_written == -1)
- /* Failure to write shouldn't fail the build. */
- return;
- buffer_ptr += amount_written;
- length -= amount_written;
- }
-}
-
-ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath)
-{
- //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath);
- InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath);
- if ( pos != _installPathToDylibs.end() ) {
- return pos->second;
- }
- else {
- // allow -dylib_path option to override indirect library to use
- for (std::vector<Options::DylibOverride>::const_iterator dit = _options.dylibOverrides().begin(); dit != _options.dylibOverrides().end(); ++dit) {
- if ( strcmp(dit->installName,installPath) == 0 ) {
- try {
- Options::FileInfo info = _options.findFile(dit->useInstead);
- _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
- info.ordinal = _indirectDylibOrdinal;
- 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;
- }
- else
- throwf("indirect dylib at %s is not a dylib", dit->useInstead);
- }
- catch (const char* msg) {
- warning("ignoring -dylib_file option, %s", 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 = _options.findFileUsingPaths(installPath);
- _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
- info.ordinal = _indirectDylibOrdinal;
- try {
- 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;
- }
- else
- throwf("indirect dylib at %s is not a dylib", info.path);
- }
- catch (const char* msg) {
- throwf("in %s, %s", info.path, msg);
- }
- }
-}
-
-
-
-void InputFiles::createIndirectDylibs()
-{
- _allDirectDylibsLoaded = true;
- _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase();
-
- // mark all dylibs initially specified as required and check if they can be used
- for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) {
- it->second->setExplicitlyLinked();
- this->checkDylibClientRestrictions(it->second);
- }
-
- // keep processing dylibs until no more dylibs are added
- unsigned long lastMapSize = 0;
- std::set<ld::dylib::File*> dylibsProcessed;
- while ( lastMapSize != _allDylibs.size() ) {
- lastMapSize = _allDylibs.size();
- // can't iterator _installPathToDylibs while modifying it, so use temp buffer
- std::vector<ld::dylib::File*> unprocessedDylibs;
- for (std::set<ld::dylib::File*>::iterator it=_allDylibs.begin(); it != _allDylibs.end(); it++) {
- if ( dylibsProcessed.count(*it) == 0 )
- unprocessedDylibs.push_back(*it);
- }
- for (std::vector<ld::dylib::File*>::iterator it=unprocessedDylibs.begin(); it != unprocessedDylibs.end(); it++) {
- dylibsProcessed.insert(*it);
- (*it)->processIndirectLibraries(this, _options.implicitlyLinkIndirectPublicDylibs());
- }
- }
-
- // go back over original dylibs and mark sub frameworks as re-exported
- if ( _options.outputKind() == Options::kDynamicLibrary ) {
- const char* myLeaf = strrchr(_options.installPath(), '/');
- if ( myLeaf != NULL ) {
- for (std::vector<class ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); it++) {
- ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(*it);
- if ( dylibReader != NULL ) {
- const char* childParent = dylibReader->parentUmbrella();
- if ( childParent != NULL ) {
- if ( strcmp(childParent, &myLeaf[1]) == 0 ) {
- // mark that this dylib will be re-exported
- dylibReader->setWillBeReExported();
- }
- }
- }
- }
- }
- }
-
-}
-
-void InputFiles::createOpaqueFileSections()
-{
- // extra command line section 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));
- }
-
-}
-
-
-void InputFiles::checkDylibClientRestrictions(ld::dylib::File* dylib)
-{
- // Check for any restrictions on who can link with this dylib
- const char* dylibParentName = dylib->parentUmbrella() ;
- const std::vector<const char*>* clients = dylib->allowableClients();
- if ( (dylibParentName != NULL) || (clients != NULL) ) {
- // only dylibs that are in an umbrella or have a client list need verification
- const char* installName = _options.installPath();
- const char* installNameLastSlash = strrchr(installName, '/');
- bool isParent = false;
- bool isSibling = false;
- bool isAllowableClient = false;
- // There are three cases:
- if ( (dylibParentName != NULL) && (installNameLastSlash != NULL) ) {
- // starts after last slash
- const char* myName = &installNameLastSlash[1];
- unsigned int myNameLen = strlen(myName);
- if ( strncmp(myName, "lib", 3) == 0 )
- myName = &myName[3];
- // up to first dot
- const char* firstDot = strchr(myName, '.');
- if ( firstDot != NULL )
- myNameLen = firstDot - myName;
- // up to first underscore
- const char* firstUnderscore = strchr(myName, '_');
- if ( (firstUnderscore != NULL) && ((firstUnderscore - myName) < (int)myNameLen) )
- myNameLen = firstUnderscore - myName;
-
- // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella
- isParent = ( (strlen(dylibParentName) == myNameLen) && (strncmp(myName, dylibParentName, myNameLen) == 0) );
-
- // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent
- isSibling = ( (_options.umbrellaName() != NULL) && (strcmp(_options.umbrellaName(), dylibParentName) == 0) );
- }
-
- 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 = _options.clientName();
- int clientNameLen = 0;
- if ( clientName != NULL ) {
- // use client name as specified on command line
- clientNameLen = strlen(clientName);
- }
- else {
- // 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 ( 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 = 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*>::const_iterator it = clients->begin(); it != clients->end(); it++) {
- if ( strncmp(*it, clientName, clientNameLen) == 0 )
- isAllowableClient = true;
- }
- }
-
- if ( !isParent && !isSibling && !isAllowableClient ) {
- if ( dylibParentName != NULL ) {
- throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.",
- dylib->path(), dylibParentName);
- }
- else {
- throwf("cannot link directly with %s", dylib->path());
- }
- }
- }
-}
-
-
-void InputFiles::inferArchitecture(Options& opts, const char** archName)
-{
- _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)];
- 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;
- }
- }
- }
- }
-
- // no thin .o files found, so default to same architecture this tool was built as
- warning("-arch not specified");
-#if __i386__
- opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL);
-#elif __x86_64__
- opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL);
-#elif __arm__
- opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6);
-#else
- #error unknown default architecture
-#endif
- *archName = opts.architectureName();
-}
-
-
-InputFiles::InputFiles(Options& opts, const char** archName)
- : _totalObjectSize(0), _totalArchiveSize(0),
- _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0),
- _options(opts), _bundleLoader(NULL),
- _allDirectDylibsLoaded(false), _inferredArch(false), _fileMonitor(-1),
- _exception(NULL)
-{
-// fStartCreateReadersTime = mach_absolute_time();
- if ( opts.architecture() == 0 ) {
- // command line missing -arch, so guess arch
- inferArchitecture(opts, archName);
- }
-#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";
-
- _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) {
- 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 {
- exception = msg;
- }
- 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 {
- _inputFiles[slot] = file;
- if (_neededFileSlot == slot)
- pthread_cond_signal(&_newFileAvailable);
- }
- }
- } while (_remainingInputFiles);
- if (_s_logPThreads) printf("worker exiting\n");
- pthread_cond_broadcast(&_parseWorkReady);
- pthread_cond_signal(&_newFileAvailable);
- pthread_mutex_unlock(&_parseLock);
-}
-
-
-void InputFiles::parseWorkerThread(InputFiles *inputFiles) {
- inputFiles->parseWorkerThread();
-}
-#endif
-
-
-ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info)
-{
- _allDylibs.insert(reader);
-
- if ( (reader->installPath() == NULL) && !info.options.fBundleLoader ) {
- // this is a "blank" stub
- // silently ignore it
- return reader;
- }
- // store options about how dylib will be used in dylib itself
- if ( info.options.fWeakImport )
- reader->setForcedWeakLinked();
- if ( info.options.fReExport )
- reader->setWillBeReExported();
- if ( info.options.fUpward ) {
- if ( _options.outputKind() == Options::kDynamicLibrary )
- reader->setWillBeUpwardDylib();
- else
- warning("ignoring upward dylib option for %s\n", info.path);
- }
- if ( info.options.fLazyLoad )
- reader->setWillBeLazyLoadedDylb();
-
- // add to map of loaded dylibs
- const char* installPath = reader->installPath();
- if ( installPath != NULL ) {
- InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath);
- if ( pos == _installPathToDylibs.end() ) {
- _installPathToDylibs[strdup(installPath)] = reader;
- }
- else {
- bool dylibOnCommandLineTwice = ( strcmp(pos->second->path(), reader->path()) == 0 );
- bool isSymlink = false;
- // ignore if this is a symlink to a dylib we've already loaded
- if ( !dylibOnCommandLineTwice ) {
- char existingDylibPath[PATH_MAX];
- if ( realpath(pos->second->path(), existingDylibPath) != NULL ) {
- char newDylibPath[PATH_MAX];
- if ( realpath(reader->path(), newDylibPath) != NULL ) {
- isSymlink = ( strcmp(existingDylibPath, newDylibPath) == 0 );
- }
- }
- }
- // remove warning for <rdar://problem/10860629> Same install name for CoreServices and CFNetwork?
- //if ( !dylibOnCommandLineTwice && !isSymlink )
- // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path());
- }
- }
- else if ( info.options.fBundleLoader )
- _bundleLoader = reader;
-
- // log direct readers
- if ( !_allDirectDylibsLoaded )
- this->logDylib(reader, false);
-
- // update stats
- _totalDylibsLoaded++;
-
- _searchLibraries.push_back(LibraryInfo(reader));
- return reader;
-}
-
-
-#if HAVE_PTHREADS
-// Called during pipelined linking to listen for available input files.
-// Available files are enqueued for parsing.
-void InputFiles::waitForInputFiles()
-{
- 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())
- 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)
-{
- // 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());
- }
- break;
- case ld::File::Dylib:
- {
- ld::dylib::File* dylib = (ld::dylib::File*)file;
- addDylib(dylib, info);
- }
- break;
- 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));
- }
- break;
- case ld::File::Other:
- break;
- default:
- {
- throwf("Unknown file type for %s", file->path());
- }
- break;
- }
- file->forEachAtom(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;
- }
-}
-
-
-bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const
-{
- // Check each input library.
- std::vector<LibraryInfo>::const_iterator libIterator = _searchLibraries.begin();
-
-
- while (libIterator != _searchLibraries.end()) {
- LibraryInfo lib = *libIterator;
- 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;
- }
- }
- }
- }
- libIterator++;
- }
-
- // search indirect dylibs
- if ( searchDylibs ) {
- for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
- ld::dylib::File* dylibFile = it->second;
- bool searchThisDylib = false;
- if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) {
- // for two level namesapce, just check all implicitly linked dylibs
- searchThisDylib = dylibFile->implicitlyLinked() && !dylibFile->explicitlyLinked();
- }
- else {
- // for flat namespace, check all indirect dylibs
- searchThisDylib = ! dylibFile->explicitlyLinked();
- }
- if ( searchThisDylib ) {
- //fprintf(stderr, "searchLibraries(%s), looking in implicitly 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
- }
- }
- }
- }
-
- return false;
-}
-
-
-bool InputFiles::searchWeakDefInDylib(const char* name) const
-{
- // search all relevant dylibs to see if any of 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() ) {
- if ( dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name) ) {
- return true;
- }
- }
- }
- 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();
-}
-
-void InputFiles::dylibs(ld::Internal& state)
-{
- bool dylibsOK = false;
- switch ( _options.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- dylibsOK = true;
- break;
- case Options::kStaticExecutable:
- case Options::kDyld:
- case Options::kPreload:
- case Options::kObjectFile:
- case Options::kKextBundle:
- dylibsOK = false;
- break;
- }
-
- // add command line dylibs in order
- for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
- 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 ) {
- 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 ) {
- for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
- ld::dylib::File* dylibFile = it->second;
- if ( dylibFile->implicitlyLinked() && dylibsOK ) {
- if ( ! vectorContains(state.dylibs, dylibFile) ) {
- state.dylibs.push_back(dylibFile);
- }
- }
- }
- }
-
- //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";
-}
-
-
-} // namespace tool
-} // namespace ld
-
-