/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2004 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@ */ #ifndef __IMAGELOADER__ #define __IMAGELOADER__ #include #include // struct mach_timebase_info #include #include #include "mach-o/dyld_gdb.h" // utility __attribute__((noreturn)) void throwf(const char* format, ...); // // ImageLoader is an abstract base class. To support loading a particular executable // file format, you make a concrete subclass of ImageLoader. // // For each executable file (dynamic shared object) in use, an ImageLoader is instantiated. // // The ImageLoader base class does the work of linking together images, but it knows nothing // about any particular file format. // // class ImageLoader { public: typedef uint32_t DefinitionFlags; static const DefinitionFlags kNoDefinitionOptions = 0; static const DefinitionFlags kWeakDefinition = 1; typedef uint32_t ReferenceFlags; static const ReferenceFlags kNoReferenceOptions = 0; static const ReferenceFlags kWeakReference = 1; static const ReferenceFlags kTentativeDefinition = 2; enum BindingLaziness { kNonLazyOnly, kLazyAndNonLazy, kLazyOnly, kLazyOnlyNoDependents }; enum InitializerRunning { kDontRunInitializers, kRunInitializers, kDontRunInitializersButTellObjc }; enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding }; enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers }; enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion }; struct Symbol; // abstact symbol struct MappedRegion { uintptr_t address; size_t size; }; typedef std::vector RegionsVector; struct LinkContext { ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const char* rpath[]); uint32_t (*imageNotification)(ImageLoader* image, uint32_t startIndex); void (*terminationRecorder)(ImageLoader* image); bool (*flatExportFinder)(const char* name, const Symbol** sym, ImageLoader** image); bool (*coalescedExportFinder)(const char* name, const Symbol** sym, ImageLoader** image); void (*undefinedHandler)(const char* name); void (*addImageNeedingNotification)(ImageLoader* image); void (*notifyAdding)(std::vector& images); void (*getAllMappedRegions)(RegionsVector&); void * (*bindingHandler)(const char *, const char *, void *); BindingOptions bindingOptions; int argc; const char** argv; const char** envp; const char** apple; ImageLoader* mainExecutable; const char* imageSuffix; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; bool bindFlat; bool slideAndPackDylibs; bool verboseOpts; bool verboseEnv; bool verboseMapping; bool verboseRebase; bool verboseBind; bool verboseInit; bool verbosePrebinding; bool verboseWarnings; }; // constructor is protected, but anyone can delete an image virtual ~ImageLoader(); // link() takes a newly instantiated ImageLoader and does all // fixups needed to make it usable by the process void link(const LinkContext& context, BindingLaziness mode, InitializerRunning inits, uint32_t notifyCount); // runInitializers() is normally called in link() but the main executable must // run crt code before initializers void runInitializers(const LinkContext& context); // runNotification() is normally called in link() but the main executable must // run crt code before initializers void runNotification(const LinkContext& context, uint32_t notifyCount); // used by dyld to see if a requested library is already loaded (might be symlink) bool statMatch(const struct stat& stat_buf) const; // get short name of this image const char* getShortName() const; // get path used to load this image, not necessarily the "real" path const char* getPath() const { return fPath; } uint32_t getPathHash() const { return fPathHash; } // get path used to load this image represents (ZeroLink only) which image this .o is part of const char* getLogicalPath() const; // get path this image is intended to be placed on disk or NULL if no preferred install location virtual const char* getInstallPath() const = 0; // image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path bool matchInstallPath() const; void setMatchInstallPath(bool); // if path was a fat file, offset of image loaded in that fat file uint64_t getOffsetInFatFile() const; // mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL) void setHideExports(bool hide = true); // check if this image's exported symbols should be ignored when linking other images bool hasHiddenExports() const; // checks if this image is already linked into the process bool isLinked() const; // even if image is deleted, leave segments mapped in void setLeaveMapped(); // checks if the specifed address is within one of this image's segments virtual bool containsAddress(const void* addr) const; // adds to list of ranges of memory mapped in void addMappedRegions(RegionsVector& regions) const; // st_mtime from stat() on file time_t lastModified(); // image should create prebound version of itself void reprebind(const LinkContext& context, time_t timestamp); // if 'commit', the prebound version should be swapped in, otherwise deleted void reprebindCommit(const LinkContext& context, bool commit); // only valid for main executables, returns a pointer its entry point virtual void* getMain() const = 0; // dyld API's require each image to have an associated mach_header virtual const struct mach_header* machHeader() const = 0; // dyld API's require each image to have a slide (actual load address minus preferred load address) virtual uintptr_t getSlide() const = 0; // dyld API's require each image to have a slide (actual load address minus preferred load address) virtual const void* getBaseAddress() const = 0; // image has exports that participate in runtime coalescing virtual bool hasCoalescedExports() const = 0; // search symbol table of definitions in this image for requested name virtual const Symbol* findExportedSymbol(const char* name, const void* hint, bool searchReExports, ImageLoader** foundIn) const = 0; // gets address of implementation (code) of the specified exported symbol virtual uintptr_t getExportedSymbolAddress(const Symbol* sym) const = 0; // gets attributes of the specified exported symbol virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0; // gets name of the specified exported symbol virtual const char* getExportedSymbolName(const Symbol* sym) const = 0; // gets how many symbols are exported by this image virtual uint32_t getExportedSymbolCount() const = 0; // gets the i'th exported symbol virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const = 0; // find exported symbol as if imported by this image // used by RTLD_NEXT and RTLD_SELF virtual const Symbol* resolveSymbol(const char* name, bool searchSelf, ImageLoader** foundIn) const; // gets how many symbols are imported by this image virtual uint32_t getImportedSymbolCount() const = 0; // gets the i'th imported symbol virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const = 0; // gets attributes of the specified imported symbol virtual ReferenceFlags geImportedSymbolInfo(const Symbol* sym) const = 0; // gets name of the specified imported symbol virtual const char* getImportedSymbolName(const Symbol* sym) const = 0; // checks if this image is a bundle and can be loaded but not linked virtual bool isBundle() const = 0; // checks if this image is a dylib virtual bool isDylib() const = 0; // only for main executable virtual bool forceFlat() const = 0; // called at runtime when a lazily bound function is first called virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; // calls termination routines (e.g. C++ static destructors for image) virtual void doTermination(const LinkContext& context) = 0; // tell this image about other images virtual void doNotification(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]) = 0; // return if this image has initialization routines virtual bool needsInitialization() = 0; // return if this image has a routine to be called when any image is loaded or unloaded virtual bool hasImageNotification() = 0; // return if this image has specified section and set start and length virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0; // given a pointer into an image, find which segment and section it is in virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0; // the image supports being prebound virtual bool isPrebindable() const = 0; // the image is prebindable and its prebinding is valid virtual bool usablePrebinding(const LinkContext& context) const = 0; // used to implement refernce counting of images void incrementReferenceCount(); bool decrementReferenceCount(); // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast static void printStatistics(unsigned int imageCount); // used with DYLD_IMAGE_SUFFIX static void addSuffix(const char* path, const char* suffix, char* result); static uint32_t hash(const char*); void setPath(const char* path); // only called for images created from memory void setLogicalPath(const char* path); protected: // abstract base class so all constructors protected ImageLoader(const char* path, uint64_t offsetInFat, const struct stat& info); ImageLoader(const char* moduleName); ImageLoader(const ImageLoader&); void operator=(const ImageLoader&); struct LibraryInfo { uint64_t checksum; uint32_t minVersion; uint32_t maxVersion; }; struct DependentLibrary { const char* name; ImageLoader* image; LibraryInfo info; bool required; bool checksumMatches; bool isReExported; bool isSubFramework; }; typedef void (*Initializer)(int argc, const char* argv[], const char* envp[],const char* apple[]); typedef void (*Terminator)(void); // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. // These methods do the above, exactly once, and it the right order void recursiveLoadLibraries(const LinkContext& context); void recursiveRebase(const LinkContext& context); void recursiveBind(const LinkContext& context, BindingLaziness bindness); void recursiveImageAnnouncement(const LinkContext& context, std::vector& newImages); void recursiveImageNotification(const LinkContext& context, uint32_t addImageCount); void recursiveInitialization(const LinkContext& context); // map any segments this image has into memory and build fSegments // this is called before doGetDependentLibraryCount so if metadata is in segments it is mapped in virtual void instantiateSegments(const uint8_t* fileData) = 0; // return how many libraries this image depends on virtual uint32_t doGetDependentLibraryCount() = 0; // fill in information about dependent libraries (array length is doGetDependentLibraryCount()) virtual void doGetDependentLibraries(DependentLibrary libs[]) = 0; // called on images that are libraries, returns info about itself virtual LibraryInfo doGetLibraryInfo() = 0; // do any fix ups in this image that depend only on the load address of the image virtual void doRebase(const LinkContext& context) = 0; // do any symbolic fix ups in this image virtual void doBind(const LinkContext& context, BindingLaziness bindness) = 0; // run any initialization routines in this image virtual void doInitialization(const LinkContext& context) = 0; // write prebinding updates to mapped file fileToPrebind virtual void doPrebinding(const LinkContext& context, time_t timestamp, uint8_t* fileToPrebind) = 0; // return if this image has termination routines virtual bool needsTermination() = 0; // support for runtimes in which segments don't have to maintain their relative positions virtual bool segmentsMustSlideTogether() const = 0; // built with PIC code and can load at any address virtual bool segmentsCanSlide() const = 0; // set how much all segments slide virtual void setSlide(intptr_t slide) = 0; // utility routine to map in all segements in fSegments from a file virtual void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); // utility routine to map in all segements in fSegments from a memory image virtual void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); // returns if all dependent libraries checksum's were as expected and none slide bool allDependentLibrariesAsWhenPreBound() const; // in mach-o a child tells it parent to re-export, instead of the other way around... virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0; // in mach-o a parent library knows name of sub libraries it re-exports.. virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; static uint32_t fgImagesWithUsedPrebinding; static uint32_t fgTotalRebaseFixups; static uint32_t fgTotalBindFixups; static uint32_t fgTotalLazyBindFixups; static uint32_t fgTotalPossibleLazyBindFixups; static uint64_t fgTotalLoadLibrariesTime; static uint64_t fgTotalRebaseTime; static uint64_t fgTotalBindTime; static uint64_t fgTotalNotifyTime; static uint64_t fgTotalInitTime; static uintptr_t fgNextSplitSegAddress; const char* fPath; const char* fLogicalPath; // for ZeroLink - the image which this bundle is part of dev_t fDevice; ino_t fInode; time_t fLastModified; uint64_t fOffsetInFatFile; std::vector fSegments; DependentLibrary* fLibraries; uint32_t fLibrariesCount; uint32_t fPathHash; uint32_t fReferenceCount; bool fAllLibraryChecksumsAndLoadAddressesMatch; bool fLeaveMapped; // when unloaded, leave image mapped in cause some other code may have pointers into it private: void init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate); intptr_t assignSegmentAddresses(const LinkContext& context); void copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize); bool fHideSymbols; // ignore this image's exported symbols when linking other images bool fMatchByInstallName;// look at image's install-path not its load path bool fLibrariesLoaded; bool fBased; bool fBoundAllNonLazy; bool fBoundAllLazy; bool fAnnounced; bool fInitialized; uint16_t fNextAddImageIndex; }; // // Segment is an abstract base class. A segment is a chunk of an executable // file that is mapped into memory. Each subclass of ImageLoader typically // implements its own concrete subclass of Segment. // // class Segment { public: virtual ~Segment() {} virtual const ImageLoader* getImage() = 0; virtual const char* getName() = 0; virtual uintptr_t getSize() = 0; virtual uintptr_t getFileSize() = 0; virtual bool hasTrailingZeroFill(); virtual uintptr_t getFileOffset() = 0; virtual bool readable() = 0; virtual bool writeable() = 0; virtual bool executable() = 0; virtual bool unaccessible() = 0; virtual bool hasFixUps() = 0; virtual uintptr_t getActualLoadAddress() = 0; virtual uintptr_t getPreferredLoadAddress() = 0; virtual void setUnMapWhenDestructed(bool unmap) = 0; protected: // abstract base class so all constructors protected Segment() {} Segment(const Segment&); void operator=(const Segment&); virtual bool hasPreferredLoadAddress() = 0; //virtual void setActualLoadAddress(uint64_t addr) = 0; static bool reserveAddressRange(uintptr_t start, size_t length); static uintptr_t reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context); static uintptr_t fgNextNonSplitSegAddress; private: void map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader::LinkContext& context); void map(const void* memoryImage, intptr_t slide, const ImageLoader::LinkContext& context); void setPermissions(); void tempWritable(); friend class ImageLoader; friend class ImageLoaderMachO; }; #endif