X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/74cfe461234fcf76aadb30ed686f281f06b555cd..2f2f92e40575142405a1caa9bcf847f7ad011c92:/src/MachOReaderDylib.hpp diff --git a/src/MachOReaderDylib.hpp b/src/MachOReaderDylib.hpp old mode 100644 new mode 100755 index 055dd4f..eb8e844 --- a/src/MachOReaderDylib.hpp +++ b/src/MachOReaderDylib.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -72,7 +72,7 @@ private: // // An ExportAtom has no content. It exists so that the linker can track which imported -// symbols can from which dynamic libraries. +// symbols came from which dynamic libraries. // template class ExportAtom : public ObjectFile::Atom @@ -87,13 +87,14 @@ public: virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } virtual bool dontDeadStrip() const { return false; } virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } virtual uint64_t getSize() const { return 0; } virtual std::vector& getReferences() const { return fgEmptyReferenceList; } virtual bool mustRemainInSection() const { return false; } virtual const char* getSectionName() const { return "._imports"; } virtual Segment& getSegment() const { return fgImportSegment; } - virtual bool requiresFollowOnAtom() const{ return false; } virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } virtual std::vector* getLineInfo() const { return NULL; } virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } virtual void copyRawContent(uint8_t buffer[]) const {} @@ -104,12 +105,13 @@ protected: friend class Reader; typedef typename A::P P; - ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak) - : fOwner(owner), fName(name), fWeakDefinition(weak) {} + ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) + : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} virtual ~ExportAtom() {} ObjectFile::Reader& fOwner; const char* fName; + uint32_t fOrdinal; bool fWeakDefinition; static std::vector fgEmptyReferenceList; @@ -123,6 +125,93 @@ template std::vector ExportAtom::fgEmptyReferenceList; + +class ImportReference : public ObjectFile::Reference +{ +public: + ImportReference(const char* name) + : fTarget(NULL), fTargetName(strdup(name)) {} + virtual ~ImportReference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return 0; } + virtual uint64_t getFixUpOffset() const { return 0; } + virtual const char* getTargetName() const { return fTargetName; } + virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } + virtual uint64_t getTargetOffset() const { return 0; } + virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } + virtual const char* getFromTargetName() const { return NULL; } + virtual uint64_t getFromTargetOffset() const { return 0; } + virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; } + virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } + virtual const char* getDescription() const { return "dylib import reference"; } + +private: + const ObjectFile::Atom* fTarget; + const char* fTargetName; +}; + + +// +// 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 ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return "flat-imports"; } + virtual const char* getDisplayName() const { return "flat_namespace undefines"; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector& imports) + : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } + virtual ~ImportAtom() {} + void makeReferences(std::vector& imports) { + for (std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { + fReferences.push_back(new ImportReference(*it)); + } + } + + + ObjectFile::Reader& fOwner; + uint32_t fOrdinal; + std::vector fReferences; + + static Segment fgImportSegment; +}; + +template +Segment ImportAtom::fgImportSegment("__LINKEDIT"); + + + + // // 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 @@ -133,9 +222,9 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, - bool executableOrDylib, const ObjectFile::ReaderOptions& options) - { return new Reader(fileContent, fileLength, path, executableOrDylib, options); } + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, + uint32_t ordinalBase); virtual ~Reader() {} virtual const char* getPath() { return fPath; } @@ -144,16 +233,28 @@ public: virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); virtual std::vector* getStabs() { return NULL; } + virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } virtual const char* getInstallPath() { return fDylibInstallPath; } virtual uint32_t getTimestamp() { return fDylibTimeStamp; } virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } - virtual std::vector* getDependentLibraryPaths(); - virtual bool reExports(ObjectFile::Reader*); + virtual void processIndirectLibraries(DylibHander* handler); + virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } + virtual bool explicitlyLinked() { return fExplicitlyLinked; } + virtual bool implicitlyLinked() { return fImplicitlyLinked; } + virtual bool providedExportAtom() { return fProvidedAtom; } + virtual const char* parentUmbrella() { return fParentUmbrella; } virtual std::vector* getAllowableClients(); + virtual bool hasWeakExternals() { return fHasWeakExports; } + virtual bool isLazyLoadedDylib() { return fLazyLoaded; } + + virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } protected: - const char* parentUmbrella() { return fParentUmbrella; } + + struct ReExportChain { ReExportChain* prev; Reader* reader; }; + + void assertNoReExportCycles(ReExportChain*); private: typedef typename A::P P; @@ -164,14 +265,15 @@ private: public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; }; + struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; typedef typename NameToAtomMap::iterator NameToAtomMapIterator; struct PathAndFlag { const char* path; bool reExport; }; - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - bool executableOrDylib, const ObjectFile::ReaderOptions& options); + bool isPublicLocation(const char* path); + void addSymbol(const char* name, bool weak, uint32_t ordinal); const char* fPath; const char* fParentUmbrella; @@ -180,8 +282,24 @@ private: uint32_t fDylibTimeStamp; uint32_t fDylibtCurrentVersion; uint32_t fDylibCompatibilityVersion; + uint32_t fReExportedOrdinal; std::vector fDependentLibraryPaths; NameToAtomMap fAtoms; + NameSet fIgnoreExports; + bool fNoRexports; + bool fHasWeakExports; + const bool fLinkingFlat; + const bool fLinkingMainExecutable; + bool fExplictReExportFound; + bool fExplicitlyLinked; + bool fImplicitlyLinked; + bool fProvidedAtom; + bool fImplicitlyLinkPublicDylibs; + bool fLazyLoaded; + ObjectFile::Reader::ObjcConstraint fObjcContraint; + std::vector fReExportedChildren; + const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; + std::vector fFlatImports; static bool fgLogHashtable; static std::vector fgEmptyAtomList; @@ -194,11 +312,19 @@ bool Reader::fgLogHashtable = false; template -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, bool executableOrDylib, const ObjectFile::ReaderOptions& options) - : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), fDylibCompatibilityVersion(0) +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), + fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace), + fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), + fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), + fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), + fObjcContraint(ObjectFile::Reader::kObjcNone), + fDeploymentVersionMin(options.fVersionMin) { // sanity check - if ( ! validFile(fileContent, executableOrDylib) ) + if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) throw "not a valid mach-o object file"; fPath = strdup(path); @@ -206,6 +332,17 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p 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 -whatsloaded option + if ( options.fLogAllFiles ) + printf("%s\n", path); + + if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) + warning("using -root_safe but linking against %s which is not root safe", path); + + if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) + warning("using -setuid_safe but linking against %s which is not setuid safe", path); // a "blank" stub has zero load commands if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { @@ -214,21 +351,34 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p return; } + + // optimize the case where we know there is no reason to look at indirect dylibs + fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); + fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); + bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; + // pass 1 builds list of all dependent libraries const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - PathAndFlag entry; - entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); - entry.reExport = false; - fDependentLibraryPaths.push_back(entry); - break; + if ( trackDependentLibraries ) { + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_REEXPORT_DYLIB: + fExplictReExportFound = true; + // fall into next case + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + PathAndFlag entry; + entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + fDependentLibraryPaths.push_back(entry); + break; + } + 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, path); } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } - + // pass 2 determines re-export info const macho_dysymtab_command

* dynamicInfo = NULL; const macho_nlist

* symbolTable = NULL; @@ -247,14 +397,16 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p dynamicInfo = (macho_dysymtab_command

*)cmd; break; case LC_ID_DYLIB: + { macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; fDylibInstallPath = strdup(dylibID->name()); fDylibTimeStamp = dylibID->timestamp(); fDylibtCurrentVersion = dylibID->current_version(); fDylibCompatibilityVersion = dylibID->compatibility_version(); + } break; case LC_SUB_UMBRELLA: - if ( !options.fFlatNamespace ) { + if ( trackDependentLibraries ) { const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { const char* dylibName = it->path; @@ -265,7 +417,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p } break; case LC_SUB_LIBRARY: - if ( !options.fFlatNamespace ) { + if ( trackDependentLibraries) { const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { const char* dylibName = it->path; @@ -285,74 +437,188 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p case LC_SUB_FRAMEWORK: fParentUmbrella = strdup(((macho_sub_framework_command

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

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

*)cmd)->segname(), "__OBJC") == 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 ( strcmp(sect->sectname(), "__image_info") == 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 ) + fObjcContraint = ObjectFile::Reader::kObjcGC; + else if ( (flags & 2) == 2 ) + fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + else + fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse __OBJC/__image_info section in %s", fPath); + } + } + } + } } 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, path); } + // Process the rest of the commands here. cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SUB_CLIENT: const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); - fAllowableClients.push_back(temp); break; } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } // validate minimal load commands - if ( (fDylibInstallPath == NULL) && (header->filetype() != MH_EXECUTE) ) - throw "dylib missing LC_ID_DYLIB load command"; + if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", path); if ( symbolTable == NULL ) - throw "dylib missing LC_SYMTAB load command"; + throw "binary missing LC_SYMTAB load command"; if ( dynamicInfo == NULL ) - throw "dylib missing LC_DYSYMTAB load command"; + 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 ( fLinkingFlat && fLinkingMainExecutable && ((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()]); + } + fFlatImports.push_back(new ImportAtom(*this, ordinalBase++, importNames)); + } + // build hash table if ( dynamicInfo->tocoff() == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, "ld64: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); const macho_nlist

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

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

* sym=start; sym < end; ++sym) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); - const char* name = strdup(&strings[sym->n_strx()]); - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[name] = bucket; + uint32_t index = ordinalBase; + for (const macho_nlist

* sym=start; sym < end; ++sym, ++index) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index); } + fReExportedOrdinal = index; } else { int32_t count = dynamicInfo->ntoc(); fAtoms.resize(count); // set initial bucket count - if ( fgLogHashtable ) fprintf(stderr, "ld64: building hashtable of %u entries for %s\n", count, path); + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path); const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + 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]; - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); - const char* name = strdup(&strings[sym->n_strx()]); - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[name] = bucket; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i); } + fReExportedOrdinal = ordinalBase + count; } + // unmap file munmap((caddr_t)fileContent, fileLength); } + + +template +void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) +{ + // 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 ) { + ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset; + if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { + switch ( symCond[6] - '0' ) { + case 0: + case 1: + symVersionCondition = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + symVersionCondition = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + symVersionCondition = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + symVersionCondition = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + symVersionCondition = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + symVersionCondition = ObjectFile::ReaderOptions::k10_6; + break; + } + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weak, ordinal); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } + } + } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol version: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->getPath()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( fIgnoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weak; + bucket.ordinal = ordinal; + if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); + fAtoms[strdup(name)] = bucket; + } +} + + template std::vector& Reader::getAtoms() { - // TO DO: for flat-namespace libraries, when linking flat_namespace - // we need to create an atom which references all undefines - return fgEmptyAtomList; + return fFlatImports; } @@ -360,12 +626,13 @@ template std::vector* Reader::getJustInTimeAtomsFor(const char* name) { std::vector* atoms = NULL; - + NameToAtomMapIterator pos = fAtoms.find(name); if ( pos != fAtoms.end() ) { if ( pos->second.atom == NULL ) { // instantiate atom and update hash table - pos->second.atom = new ExportAtom(*this, name, pos->second.weak); + pos->second.atom = new ExportAtom(*this, name, pos->second.weak, pos->second.ordinal); + fProvidedAtom = true; if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); } // return a vector of one atom @@ -374,6 +641,25 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha } else { if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); + // if not supposed to ignore this export, see if I have it + if ( fIgnoreExports.count(name) == 0 ) { + // look in children that I re-export + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); + std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); + if ( childAtoms != NULL ) { + // make a new atom that says this reader is the owner + bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); + // return a vector of one atom + ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); + fProvidedAtom = true; + atoms = new std::vector; + atoms->push_back(newAtom); + delete childAtoms; + return atoms; + } + } + } } return atoms; } @@ -381,52 +667,125 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha template -std::vector* Reader::getDependentLibraryPaths() +bool Reader::isPublicLocation(const char* path) { - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - result->push_back(it->path); + // -no_implicit_dylibs disables this optimization + if ( ! fImplicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[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 - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } } - return result; + + return false; } template -std::vector* Reader::getAllowableClients() +void Reader::processIndirectLibraries(DylibHander* handler) { - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fAllowableClients.begin(); - it != fAllowableClients.end(); - it++) { - result->push_back(*it); + if ( fLinkingFlat ) { + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + handler->findDylib(it->path, this->getPath()); + } } - return (fAllowableClients.size() != 0 ? result : NULL); + else if ( fNoRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + if ( it->reExport ) { + //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + if ( isPublicLocation(child->getInstallPath()) ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( this->explicitlyLinked() || this->implicitlyLinked() ) { + //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); + ((Reader*)child)->setImplicitlyLinked(); + } + else + fReExportedChildren.push_back(child); + } + else { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + else if ( !fExplictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->getPath(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.reader = this; + this->assertNoReExportCycles(&chain); } template -bool Reader::reExports(ObjectFile::Reader* child) +void Reader::assertNoReExportCycles(ReExportChain* prev) { - // A dependent dylib is re-exported under two conditions: - // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name - const char* childInstallPath = child->getInstallPath(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport && ((strcmp(it->path, child->getPath()) == 0) || ((childInstallPath!=NULL) && (strcmp(it->path, childInstallPath)==0))) ) - return true; + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.reader = this; + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + ObjectFile::Reader* child = *it; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->reader == child ) { + throwf("cycle in dylib re-exports with %s", child->getPath()); + } + } + ((Reader*)(*it))->assertNoReExportCycles(&chain); } +} - // 2) child contains LC_SUB_FRAMEWORK with parent name - const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->getPath(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) - return true; - } - return false; +template +std::vector* Reader::getAllowableClients() +{ + std::vector* result = new std::vector; + for (typename std::vector::iterator it = fAllowableClients.begin(); + it != fAllowableClients.end(); + it++) { + result->push_back(*it); + } + return (fAllowableClients.size() != 0 ? result : NULL); } template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC ) @@ -437,15 +796,23 @@ bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) 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: - return executableOrDylib; + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; default: return false; } } template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC_64 ) @@ -456,15 +823,23 @@ bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib 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: - return executableOrDylib; + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; default: return false; } } template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC ) @@ -475,15 +850,23 @@ bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) 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: - return executableOrDylib; + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; default: return false; } } template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC_64 ) @@ -494,15 +877,47 @@ bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyli 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: - return executableOrDylib; + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; default: return false; } } - - +template <> +bool Reader::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; + } +} }; // namespace dylib }; // namespace mach_o