/* * Copyright (c) 2005 Apple Computer, 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@ */ namespace ObjectFileDylibMachO { class Reader : public ObjectFile::Reader { public: Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); virtual ~Reader(); virtual const char* getPath(); virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); virtual std::vector* getStabsDebugInfo(); virtual const char* getInstallPath(); virtual uint32_t getTimestamp(); virtual uint32_t getCurrentVersion(); virtual uint32_t getCompatibilityVersion(); virtual std::vector* getDependentLibraryPaths(); virtual bool reExports(ObjectFile::Reader*); virtual bool isDefinitionWeak(const ObjectFile::Atom&); private: struct CStringComparor { bool operator()(const char* left, const char* right) { return (strcmp(left, right) > 0); } }; typedef std::map Mapper; void init(const macho_header* header,const char* path); const macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], const struct dylib_table_of_contents toc[], uint32_t symbolCount); const macho_nlist* binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount); bool hasExport(const char* name); const macho_nlist* findExport(const char* name); const char* fPath; const macho_header* fHeader; const char* fStrings; const macho_dysymtab_command* fDynamicInfo; const macho_dylib_command* fDylibID; const macho_nlist* fSymbols; uint32_t fSymbolCount; Mapper fAtoms; std::vector fReExportedDylibs; static std::vector fEmptyAtomList; }; std::vector Reader::fEmptyAtomList; class Segment : public ObjectFile::Segment { public: Segment(const char* name) { fName = name; } virtual const char* getName() const { return fName; } virtual bool isContentReadable() const { return true; } virtual bool isContentWritable() const { return false; } virtual bool isContentExecutable() const { return false; } private: const char* fName; }; class ExportAtom : public ObjectFile::Atom { public: virtual ObjectFile::Reader* getFile() const { return &fOwner; } virtual const char* getName() const { return fName; } virtual const char* getDisplayName() const; virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } virtual bool isTentativeDefinition() const { return false; } virtual bool isWeakDefinition() const { return false; } virtual bool isCoalesableByName() const { return false; } virtual bool isCoalesableByValue() const { return false; } virtual bool isZeroFill() const { return false; } virtual bool dontDeadStrip() const { return false; } virtual bool dontStripName() const { return false; } virtual bool isImportProxy() const { return true; } 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 std::vector* getStabsDebugInfo() const { return NULL; } virtual uint8_t getAlignment() const { return 0; } virtual WeakImportSetting getImportWeakness() const { return fWeakImportSetting; } virtual void copyRawContent(uint8_t buffer[]) const {} virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} virtual void setScope(Scope) { } virtual void setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; } protected: friend class Reader; ExportAtom(Reader& owner, const char* name) : fOwner(owner), fName(name), fWeakImportSetting(kWeakUnset) {} virtual ~ExportAtom() {} Reader& fOwner; const char* fName; WeakImportSetting fWeakImportSetting; static std::vector fgEmptyReferenceList; static Segment fgImportSegment; }; Segment ExportAtom::fgImportSegment("__LINKEDIT"); std::vector ExportAtom::fgEmptyReferenceList; const char* ExportAtom::getDisplayName() const { static char temp[300]; strcpy(temp, fName); strcat(temp, "$import"); return temp; } Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) : fHeader(header), fStrings(NULL), fDylibID(NULL), fSymbols(NULL), fSymbolCount(0) { typedef std::pair DylibAndReExportFlag; std::vector dependentDylibs; fPath = strdup(path); const uint32_t cmd_count = header->ncmds(); const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); // get all dylib load commands 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: { DylibAndReExportFlag info; info.first = (struct macho_dylib_command*)cmd; info.second = options.fFlatNamespace; dependentDylibs.push_back(info); } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } // cache interesting pointers cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SYMTAB: { const macho_symtab_command* symtab = (macho_symtab_command*)cmd; fSymbolCount = symtab->nsyms(); fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); fStrings = (char*)header + symtab->stroff(); } break; case LC_DYSYMTAB: fDynamicInfo = (macho_dysymtab_command*)cmd; break; case LC_ID_DYLIB: fDylibID = (macho_dylib_command*)cmd; break; case LC_SUB_UMBRELLA: if ( !options.fFlatNamespace ) { const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { const char* dylibName = it->first->name(); const char* lastSlash = strrchr(dylibName, '/'); if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) it->second = true; } } break; case LC_SUB_LIBRARY: if ( !options.fFlatNamespace ) { const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { const char* dylibName = it->first->name(); 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->second = true; } } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } // load dylibs we need to re-export for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { if ( it->second ) { // printf("%s need to re-export %s\n", path, it->first->name()); //fReExportedDylibs.push_back( } } } Reader::~Reader() { } const char* Reader::getPath() { return fPath; } std::vector& Reader::getAtoms() { return fEmptyAtomList; } const macho_nlist* Reader::binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], const struct dylib_table_of_contents toc[], uint32_t symbolCount) { int32_t high = symbolCount-1; int32_t mid = symbolCount/2; for (int32_t low = 0; low <= high; mid = (low+high)/2) { const uint32_t index = ENDIAN_READ32(toc[mid].symbol_index); const macho_nlist* pivot = &symbols[index]; const char* pivotStr = &stringPool[pivot->n_strx()]; int cmp = strcmp(key, pivotStr); if ( cmp == 0 ) return pivot; if ( cmp > 0 ) { // key > pivot low = mid + 1; } else { // key < pivot high = mid - 1; } } return NULL; } const macho_nlist* Reader::binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount) { const macho_nlist* base = symbols; for (uint32_t n = symbolCount; n > 0; n /= 2) { const macho_nlist* pivot = &base[n/2]; const char* pivotStr = &stringPool[pivot->n_strx()]; int cmp = strcmp(key, pivotStr); if ( cmp == 0 ) return pivot; if ( cmp > 0 ) { // key > pivot // move base to symbol after pivot base = &pivot[1]; --n; } else { // key < pivot // keep same base } } return NULL; } const macho_nlist* Reader::findExport(const char* name) { if ( fDynamicInfo->tocoff() == 0 ) return binarySearch(name, fStrings, &fSymbols[fDynamicInfo->iextdefsym()], fDynamicInfo->nextdefsym()); else { return binarySearchWithToc(name, fStrings, fSymbols, (dylib_table_of_contents*)((char*)fHeader + fDynamicInfo->tocoff()), fDynamicInfo->nextdefsym()); } } bool Reader::hasExport(const char* name) { return ( findExport(name) != NULL ); } std::vector* Reader::getJustInTimeAtomsFor(const char* name) { std::vector* atoms = NULL; // search exports if ( this->hasExport(name) ) { // see if this atom already synthesized ObjectFile::Atom* atom = NULL; Mapper::iterator pos = fAtoms.find(name); if ( pos != fAtoms.end() ) { atom = pos->second; } else { atom = new ExportAtom(*this, name); fAtoms[name] = atom; } // return a vector of one atom atoms = new std::vector; atoms->push_back(atom); return atoms; } // check re-exports for (std::vector::iterator it = fReExportedDylibs.begin(); it != fReExportedDylibs.end(); it++) { Reader* reExportedReader = *it; atoms = reExportedReader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) return atoms; } return NULL; } std::vector* Reader::getStabsDebugInfo() { return NULL; } const char* Reader::getInstallPath() { return fDylibID->name(); } uint32_t Reader::getTimestamp() { return fDylibID->timestamp(); } uint32_t Reader::getCurrentVersion() { return fDylibID->current_version(); } uint32_t Reader::getCompatibilityVersion() { return fDylibID->compatibility_version(); } std::vector* Reader::getDependentLibraryPaths() { std::vector* result = new std::vector; const uint32_t cmd_count = fHeader->ncmds(); const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); 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: { result->push_back(((struct macho_dylib_command*)cmd)->name()); } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } return result; } bool Reader::reExports(ObjectFile::Reader* child) { // A dependent dylib is re-exported under two conditions: // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name { const uint32_t cmd_count = fHeader->ncmds(); const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); const macho_load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SUB_UMBRELLA: { const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); const char* dylibName = child->getPath(); const char* lastSlash = strrchr(dylibName, '/'); if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) return true; } break; case LC_SUB_LIBRARY: { const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); const char* dylibName = child->getPath(); 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 ) return true; } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } } // 2) child contains LC_SUB_FRAMEWORK with parent name { const uint32_t cmd_count = ((Reader*)child)->fHeader->ncmds(); const macho_load_command* const cmds = (macho_load_command*)((char*)(((Reader*)child)->fHeader) + macho_header::size); const macho_load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SUB_FRAMEWORK: { const char* frameworkLeafName = ((macho_sub_framework_command*)cmd)->name(); const char* parentName = this->getPath(); const char* lastSlash = strrchr(parentName, '/'); if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) return true; } break; } cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); } } return false; } bool Reader::isDefinitionWeak(const ObjectFile::Atom& atom) { const macho_nlist* sym = findExport(atom.getName()); if ( sym != NULL ) { if ( (sym->n_desc() & N_WEAK_DEF) != 0 ) return true; } return false; } Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) { return new Reader(mh, path, options); } };