X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/07feaf2cb00322d025073eb8ec22189ada5e4180..a645023da60d22e86be13f7b4d97adeff8bc6665:/src/ld/parsers/macho_dylib_file.cpp diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp new file mode 100644 index 0000000..40dad13 --- /dev/null +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -0,0 +1,996 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 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 "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "macho_dylib_file.h" + + +namespace mach_o { +namespace dylib { + + +// forward reference +template class File; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ld::Atom +{ +public: + ExportAtom(const File& f, const char* nm, bool weakDef, + bool tlv, typename A::P::uint_t address) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(nm), _address(address) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + virtual ~ExportAtom() {} + + const File& _file; + const char* _name; + pint_t _address; +}; + + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template +class ImportAtom : public ld::Atom +{ +public: + ImportAtom(File& f, std::vector& imports); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + 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 ld::Fixup::iterator fixupsBegin() const { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + +protected: + typedef typename A::P P; + + virtual ~ImportAtom() {} + + + File& _file; + mutable std::vector _undefs; +}; + +template +ImportAtom::ImportAtom(File& f, std::vector& imports) +: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) +{ + for(std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { + _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it))); + } +} + + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class File : public ld::dylib::File +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, uint32_t ordinal, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, + bool logAllFiles); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); + virtual bool providedExportAtom() const { return _providedAtom; } + virtual const char* parentUmbrella() const { return _parentUmbrella; } + virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : NULL; } + virtual bool hasWeakExternals() const { return _hasWeakExports; } + virtual bool deadStrippable() const { return _deadStrippable; } + virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } + virtual bool hasWeakDefinition(const char* name) const; + + +protected: + + struct ReExportChain { ReExportChain* prev; File* file; }; + + void assertNoReExportCycles(ReExportChain*); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + friend class ExportAtom; + friend class ImportAtom; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + struct AtomAndWeak { ld::Atom* atom; bool weak; bool tlv; pint_t address; }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + + struct Dependent { const char* path; File* dylib; bool reExport; }; + + bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; + bool isPublicLocation(const char* pth); + void addSymbol(const char* name, bool weak, bool tlv, pint_t address); + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent); + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent); + static const char* objCInfoSegmentName(); + static const char* objCInfoSectionName(); + + const ld::MacVersionMin _macVersionMin; + const ld::IPhoneVersionMin _iPhoneVersionMin; + bool _linkingFlat; + bool _implicitlyLinkPublicDylibs; + ld::File::ObjcConstraint _objcContraint; + ld::Section _importProxySection; + ld::Section _flatDummySection; + std::vector _dependentDylibs; + std::vector _allowableClients; + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + const char* _parentUmbrella; + ImportAtom* _importAtom; + bool _noRexports; + bool _hasWeakExports; + bool _deadStrippable; + bool _hasPublicInstallName; + mutable bool _providedAtom; + bool _explictReExportFound; + + static bool _s_logHashtable; +}; + +template +bool File::_s_logHashtable = false; + +template <> const char* File::objCInfoSegmentName() { return "__DATA"; } +template <> const char* File::objCInfoSegmentName() { return "__DATA"; } +template const char* File::objCInfoSegmentName() { return "__OBJC"; } + +template <> const char* File::objCInfoSectionName() { return "__objc_imageinfo"; } +template <> const char* File::objCInfoSectionName() { return "__objc_imageinfo"; } +template const char* File::objCInfoSectionName() { return "__image_info"; } + +template +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, + bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles) + : ld::dylib::File(strdup(pth), mTime, ord), + _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin), + _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _objcContraint(ld::File::objcConstraintNone), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), + _deadStrippable(false), _hasPublicInstallName(false), + _providedAtom(false), _explictReExportFound(false) +{ + const macho_header

* header = (const macho_header

*)fileContent; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((char*)header + sizeof(macho_header

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

*)((char*)header + sizeof(macho_header

) + header->sizeofcmds()); + + // write out path for -t option + if ( logAllFiles ) + printf("%s\n", pth); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + + // optimize the case where we know there is no reason to look at indirect dylibs + _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) + || (header->filetype() == MH_BUNDLE) + || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader + _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); + _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + + // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format + const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_dyld_info_command

* dyldInfo = NULL; + const macho_nlist

* symbolTable = NULL; + const char* strings = NULL; + bool compressedLinkEdit = false; + uint32_t dependentLibCount = 0; + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + macho_dylib_command

* dylibID; + const macho_symtab_command

* symtab; + switch (cmd->cmd()) { + case LC_SYMTAB: + symtab = (macho_symtab_command

*)cmd; + symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

*)cmd; + compressedLinkEdit = true; + break; + case LC_ID_DYLIB: + dylibID = (macho_dylib_command

*)cmd; + _dylibInstallPath = strdup(dylibID->name()); + _dylibTimeStamp = dylibID->timestamp(); + _dylibCurrentVersion = dylibID->current_version(); + _dylibCompatibilityVersion = dylibID->compatibility_version(); + _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + ++dependentLibCount; + break; + case LC_REEXPORT_DYLIB: + _explictReExportFound = true; + ++dependentLibCount; + break; + case LC_SUB_FRAMEWORK: + _parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + break; + case LC_SUB_CLIENT: + _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + break; + case macho_segment_command

::CMD: + // check for Objective-C info + if ( strcmp(((macho_segment_command

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)segment + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segment->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strncmp(sect->sectname(), objCInfoSectionName(), strlen(objCInfoSectionName())) == 0 ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + _objcContraint = ld::File::objcConstraintGC; + else if ( (flags & 2) == 2 ) + _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else + _objcContraint = ld::File::objcConstraintRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); + } + } + } + } + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); + } + + // figure out if we need to examine dependent dylibs + // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted + bool processDependentLibraries = true; + if ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) + processDependentLibraries = false; + + if ( processDependentLibraries ) { + // pass 2 builds list of all dependent libraries + _dependentDylibs.reserve(dependentLibCount); + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + // with new linkedit format only care about LC_REEXPORT_DYLIB + if ( compressedLinkEdit && !linkingFlatNamespace ) + break; + case LC_REEXPORT_DYLIB: + Dependent entry; + entry.path = strdup(((macho_dylib_command

*)cmd)->name()); + entry.dylib = NULL; + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + _dependentDylibs.push_back(entry); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + // verify MH_NO_REEXPORTED_DYLIBS bit was correct + if ( compressedLinkEdit && !linkingFlatNamespace ) { + assert(_dependentDylibs.size() != 0); + } + // pass 3 add re-export info + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + const char* frameworkLeafName; + const char* dylibBaseName; + switch (cmd->cmd()) { + case LC_SUB_UMBRELLA: + frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + break; + case LC_SUB_LIBRARY: + dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + } + + // validate minimal load commands + if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", pth); + if ( dyldInfo == NULL ) { + if ( symbolTable == NULL ) + throw "binary missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "binary missing LC_DYSYMTAB load command"; + } + + // if linking flat and this is a flat dylib, create one atom that references all imported symbols + if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { + std::vector importNames; + importNames.reserve(dynamicInfo->nundefsym()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; + for (const macho_nlist

* sym=start; sym < end; ++sym) { + importNames.push_back(&strings[sym->n_strx()]); + } + _importAtom = new ImportAtom(*this, importNames); + } + + // build hash table + if ( dyldInfo != NULL ) + buildExportHashTableFromExportInfo(dyldInfo, fileContent); + else + buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + +template +void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent) +{ + if ( dynamicInfo->tocoff() == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; + _atoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + for (const macho_nlist

* sym=start; sym < end; ++sym) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + else { + int32_t count = dynamicInfo->ntoc(); + _atoms.resize(count); // set initial bucket count + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist

* sym = &symbolTable[index]; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + + // special case old libSystem + if ( (_dylibInstallPath != NULL) && (strcmp(_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) + addDyldFastStub(); +} + + +template +void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent) +{ + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); + if ( dyldInfo->export_size() > 0 ) { + const uint8_t* start = fileContent + dyldInfo->export_off(); + const uint8_t* end = &start[dyldInfo->export_size()]; + std::vector list; + parseTrie(start, end, list); + for (std::vector::iterator it=list.begin(); it != list.end(); ++it) + this->addSymbol(it->name, + it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, + (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, + it->address); + } +} + + +template <> +void File::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template <> +void File::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template +void File::addDyldFastStub() +{ + // do nothing +} + +template +void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) +{ + if ( weakDef ) { + assert(_hasWeakExports); + } + //fprintf(stderr, "addSymbol() %s\n", name); + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != NULL ) { + char curOSVers[16]; + if ( _macVersionMin != ld::macVersionUnset ) { + sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF)); + } + else if ( _iPhoneVersionMin != ld::iPhoneVersionUnset ) { + sprintf(curOSVers, "$os%d.%d$", (_iPhoneVersionMin >> 16), ((_iPhoneVersionMin >> 8) & 0xFF)); + } + else { + assert(0 && "targeting neither macosx nor iphoneos"); + } + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef, false, 0); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weakDef; + bucket.tlv = tlv; + bucket.address = address; + if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + // if doing flatnamespace and need all this dylib's imports resolve + // add atom which references alls undefines in this dylib + if ( _importAtom != NULL ) { + handler.doAtom(*_importAtom); + return true; + } + return false; +} + +template +bool File::hasWeakDefinition(const char* name) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + typename NameToAtomMap::const_iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + return pos->second.weak; + } + else { + // look in children that I re-export + for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport ) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath()); + typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name); + if ( cpos != it->dylib->_atoms.end() ) + return cpos->second.weak; + } + } + } + return false; +} + +template +bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const +{ + // check myself + typename NameToAtomMap::iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + *weakDef = pos->second.weak; + *tlv = pos->second.tlv; + *defAddress = pos->second.address; + return true; + } + + // check dylibs I re-export + for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport && !it->dylib->implicitlyLinked() ) { + if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) + return true; + } + } + + return false; +} + + +template +bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + if ( this->containsOrReExports(name, &bucket.weak, &bucket.tlv, &bucket.address) ) { + bucket.atom = new ExportAtom(*this, name, bucket.weak, bucket.tlv, bucket.address); + _atoms[name] = bucket; + _providedAtom = true; + if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + + + +template +bool File::isPublicLocation(const char* pth) +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&pth[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &pth[27]; + if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + const static bool log = false; + if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + it->dylib = (File*)handler->findDylib(it->path, this->path()); + } + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + it->dylib = (File*)handler->findDylib(it->path, this->path()); + if ( it->dylib->hasPublicInstallName() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); + it->dylib->setImplicitlyLinked(); + } + else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else { + // add all child's symbols to me + if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else if ( !_explictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + it->dylib = (File*)handler->findDylib(it->path, this->path()); + const char* parentUmbrellaName = it->dylib->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->path(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + it->reExport = true; + if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.file = this; + this->assertNoReExportCycles(&chain); +} + +template +void File::assertNoReExportCycles(ReExportChain* prev) +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.file = this; + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + ld::File* child = it->dylib; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->file == child ) { + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + } + if ( it->dylib != NULL ) + it->dylib->assertNoReExportCycles(&chain); + } + } +} + + +struct ParserOptions { + bool linkingFlat; + bool linkingMain; + bool addImplictDylibs; + ld::MacVersionMin macOSMin; + ld::IPhoneVersionMin iphoneOSMin; +}; + + +template +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t mTime, + uint32_t ordinal, const Options& opts) { + return new File(fileContent, fileLength, path, mTime, + ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.macosxVersionMin(), + opts.iphoneOSVersionMin(), + opts.logAllFiles()); + } + +}; + + + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_ARM ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + + + +// +// main function used by linker to instantiate ld::Files +// +ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, const Options& opts, uint32_t ordinal, bool bundleLoader) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + + +}; // namespace dylib +}; // namespace mach_o + +