X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/07feaf2cb00322d025073eb8ec22189ada5e4180..a645023da60d22e86be13f7b4d97adeff8bc6665:/src/ld/InputFiles.cpp diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp new file mode 100644 index 0000000..bc292bb --- /dev/null +++ b/src/ld/InputFiles.cpp @@ -0,0 +1,943 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" + + +namespace ld { +namespace tool { + + + + +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, + 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 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 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; +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); +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); + + + +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 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; } + 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 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; } + 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, "!\n", 8) == 0 ) + return "archive"; + + return "unsupported file format"; +} + + +ld::File* InputFiles::makeFile(const Options::FileInfo& info) +{ + // 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; + 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 ( _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) { + 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 < OSSwapBigToHostInt32(fh->nfat_arch); ++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.preferSubArchitecture(); + 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, _nextInputOrdinal, objOpts); + if ( objResult != NULL ) + return this->addObject(objResult, info, len); + + // 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); + + // 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.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); + + // 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()); + } + 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); + } + } + + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + throwf("missing required architecture %s in file", _options.architectureName()); + } + else { + if ( isFatFile ) + throwf("file is universal but does not contain a(n) %s slice", _options.architectureName()); + else + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + } +} + +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(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) ) { + // 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::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); + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + //_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); + try { + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); + //_installPathToDylibs[strdup(installPath)] = dylibReader; + 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; + + // 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 dylibsProcessed; + while ( lastMapSize != _allDylibs.size() ) { + lastMapSize = _allDylibs.size(); + // can't iterator _installPathToDylibs while modifying it, so use temp buffer + std::vector unprocessedDylibs; + for (std::set::iterator it=_allDylibs.begin(); it != _allDylibs.end(); it++) { + if ( dylibsProcessed.count(*it) == 0 ) + unprocessedDylibs.push_back(*it); + } + for (std::vector::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::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); it++) { + ld::dylib::File* dylibReader = dynamic_cast(*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, _nextInputOrdinal)); + // bump ordinal + _nextInputOrdinal++; + } + +} + + +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* 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_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& files = opts.getInputFiles(); + for (std::vector::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 __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); +#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), _nextInputOrdinal(1), + _allDirectDylibsLoaded(false), _inferredArch(false) +{ +// fStartCreateReadersTime = mach_absolute_time(); + if ( opts.architecture() == 0 ) { + // command line missing -arch, so guess arch + inferArchitecture(opts, archName); + } + + const std::vector& files = opts.getInputFiles(); + if ( files.size() == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + _inputFiles.reserve(files.size()); + for (std::vector::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 + } + else { + warning("ignoring file %s, %s", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + + this->createIndirectDylibs(); + this->createOpaqueFileSections(); +} + + + +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; +} + + +ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + _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->setWillBeWeakLinked(); + 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 ); + } + } + } + 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); + + // bump ordinal + _nextInputOrdinal++; + + // update stats + _totalDylibsLoaded++; + + return reader; +} + + +bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const +{ + bool didSomething = false; + for (std::vector::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); + break; + case Options::kStaticExecutable: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomExecutable); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kPreload: + // add implicit __mh_preload_header label + handler.doAtom(DSOHandleAtom::_s_atomPreload); + 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 +{ + // check each input file + for (std::vector::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(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; + } + } + } + + // 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 + 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; +} + +void InputFiles::dylibs(ld::Internal& state) +{ + bool dylibsOK; + 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::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + ld::dylib::File* dylibFile = dynamic_cast(*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); + 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 ) + state.dylibs.push_back(dylibFile); + } + } + // and -bundle_loader + state.bundleLoader = _bundleLoader; +} + + +} // namespace tool +} // namespace ld +