darling-dyld/dyld3/ClosureBuilder.h
2023-04-29 11:24:58 -07:00

398 lines
21 KiB
C++

/*
* Copyright (c) 2017 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@
*/
#ifndef ClosureBuilder_h
#define ClosureBuilder_h
#include "Closure.h"
#include "ClosureFileSystem.h"
#include "ClosureWriter.h"
#include "PathOverrides.h"
#include "DyldSharedCache.h"
#include "MachOAnalyzer.h"
#include "MachOAnalyzerSet.h"
#include "Map.h"
#include "Loading.h"
#include <optional>
namespace objc_opt {
struct objc_clsopt_t;
struct objc_selopt_t;
struct objc_protocolopt2_t;
}
typedef dyld3::MachOAnalyzerSet::WrappedMachO WrappedMachO;
namespace dyld3 {
class RootsChecker;
namespace closure {
class ClosureAnalyzerSet;
class VIS_HIDDEN ClosureBuilder : public dyld3::MachOAnalyzerSet
{
public:
struct ResolvedTargetInfo
{
const MachOLoaded* foundInDylib;
const char* requestedSymbolName;
const char* foundSymbolName;
uint64_t addend;
bool weakBindCoalese;
bool weakBindSameImage;
bool isWeakDef;
bool skippableWeakDef;
int libOrdinal;
};
typedef void (^DylibFixupHandler)(const MachOLoaded* fixupIn, uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target);
enum class AtPath { none, all, onlyInRPaths };
ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const RootsChecker& rootsChecker,
const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
const GradedArchs& archs, const PathOverrides& pathOverrides,
AtPath atPathHandling=AtPath::all, bool allowRelativePaths=true, LaunchErrorInfo* errorInfo=nullptr,
Platform platform=MachOFile::currentPlatform(), DylibFixupHandler dylibFixupHandler=nullptr);
~ClosureBuilder();
Diagnostics& diagnostics() { return _diag; }
const LaunchClosure* makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures);
const LaunchClosure* makeLaunchClosure(const char* mainPath,bool allowInsertFailures);
void makeMinimalClosures() { _makeMinimalClosure = true; }
void setCanSkipEncodingRebases() { _leaveRebasesAsOpcodes = true; }
static const DlopenClosure* sRetryDlopenClosure;
const DlopenClosure* makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array<LoadedImage>& loadedList,
closure::ImageNum callerImageNum, bool noLoad, bool forceBindLazies, bool canUseSharedCacheClosure,
closure::ImageNum* topImageNum);
ImageNum nextFreeImageNum() const { return _startImageNum + _nextIndex; }
Platform platform() const { return _platform; }
void setDyldCacheInvalidFormatVersion();
void disableInterposing() { _interposingDisabled = true; }
struct PatchableExport
{
uint32_t cacheOffsetOfImpl;
uint32_t cacheOffsetOfName;
uint32_t patchLocationsCount;
const dyld_cache_patchable_location* patchLocations;
};
struct CachedDylibInfo
{
LoadedFileInfo fileInfo;
};
struct CachedDylibAlias
{
const char* realPath;
const char* aliasPath;
};
const ImageArray* makeDyldCacheImageArray(const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases);
const ImageArray* makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount);
static void buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd);
private:
friend ClosureAnalyzerSet;
struct InitInfo
{
uint32_t initOrder : 30,
danglingUpward : 1,
visited : 1;
};
struct BuilderLoadedImage
{
Array<Image::LinkedImage> dependents;
ImageNum imageNum;
uint32_t unmapWhenDone : 1,
contentRebased : 1,
hasInits : 1,
markNeverUnload : 1,
rtldLocal : 1,
isBadImage : 1,
mustBuildClosure : 1,
hasMissingWeakImports : 1,
hasInterposingTuples : 1,
padding : 11,
overrideImageNum : 12;
uint32_t exportsTrieOffset;
uint32_t exportsTrieSize;
LoadedFileInfo loadedFileInfo;
// Convenience method to get the information from the loadedFileInfo
const MachOAnalyzer* loadAddress() const { return (const MachOAnalyzer*)loadedFileInfo.fileContent; }
const char* path() const { return loadedFileInfo.path; }
};
struct LoadedImageChain
{
LoadedImageChain* previous;
BuilderLoadedImage& image;
};
// Represents how the current image is linked
enum class LinkageType {
kStatic, // Linked via LC_LOAD_* by main executable or other dylib
kDynamic, // Only used for the image we dlopen'ed, not anything it links
kInserted // This is an inserted library
};
typedef LaunchClosure::SkippedFile SkippedFile;
void recursiveLoadDependents(LoadedImageChain& forImageChain, bool canUseSharedCacheClosure = true);
void loadDanglingUpwardLinks(bool canUseSharedCacheClosure = true);
void forEachResolvedPathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath, LinkageType linkageType,
void (^handler)(const char* possiblePath, bool& stop));
bool findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, LinkageType linkageType,
uint32_t compatVersion, bool canUseSharedCacheClosure);
void buildImage(ImageWriter& writer, BuilderLoadedImage& forImage);
void addSegments(ImageWriter& writer, const MachOAnalyzer* mh);
void addFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
void addChainedFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
void addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh);
void computeInitOrder(ImageWriter& writer, uint32_t loadIndex);
void addClosureInfo(LaunchClosureWriter& closureWriter);
void depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError);
const MachOAnalyzer* machOForImageNum(ImageNum imageNum);
ImageNum imageNumForMachO(const MachOAnalyzer* mh);
const MachOAnalyzer* findDependent(const MachOLoaded* mh, uint32_t depIndex);
BuilderLoadedImage& findLoadedImage(ImageNum imageNum);
const BuilderLoadedImage& findLoadedImage(ImageNum imageNum) const;
BuilderLoadedImage& findLoadedImage(const MachOAnalyzer* mh);
uint32_t index(const BuilderLoadedImage&);
bool expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[]);
bool expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, bool fromLCRPATHinMain, char fixedPath[]);
void addMustBeMissingPath(const char* path);
void addSkippedFile(const char* path, uint64_t inode, uint64_t mtime);
const char* strdup_temp(const char* path) const;
bool overridableDylib(const BuilderLoadedImage& forImage);
void addOperatorCachePatches(BuilderLoadedImage& forImage);
void addWeakDefCachePatch(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget);
struct HashCString {
static size_t hash(const char* v);
};
struct EqualCString {
static bool equal(const char* s1, const char* s2);
};
struct HashPointer {
template<typename T>
static size_t hash(const T* v) {
return std::hash<const T*>{}(v);
}
};
struct EqualPointer {
template<typename T>
static bool equal(const T* s1, const T* s2) {
return s1 == s2;
}
};
struct ObjCOptimizerImage {
ObjCOptimizerImage() {
}
~ObjCOptimizerImage() {
}
typedef std::pair<closure::Image::ObjCClassNameImageOffset, closure::Image::ObjCClassImageOffset> SeenClass;
struct SelectorFixup {
uint32_t fixupVMOffset;
bool isSharedCache;
union {
struct {
uint32_t selectorTableIndex;
} sharedCache;
struct {
const char* selectorString;
} image;
};
};
BuilderLoadedImage* loadedImage = nullptr;
ImageWriter* writer = nullptr;
uint64_t fairplayFileOffsetStart = 0;
uint64_t fairplayFileOffsetEnd = 0;
Diagnostics diag;
// Image info optimisation
uint64_t objcImageInfoVMOffset;
// Class and protocol optimisation data structures
OverflowSafeArray<std::pair<uint64_t, uint64_t>> classesNameAndDataVMOffsets;
OverflowSafeArray<SeenClass> seenClasses;
OverflowSafeArray<uint64_t> classStableSwiftFixups;
Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString> classSharedCacheDuplicates;
OverflowSafeArray<SeenClass> seenProtocols;
OverflowSafeArray<uint64_t> protocolISAFixups;
// Selector optimsation data structures
OverflowSafeArray<SelectorFixup> selectorFixups;
Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString> selectorMap;
std::optional<uint64_t> methodNameVMOffset;
OverflowSafeArray<uint64_t> methodListFixups;
};
bool optimizeObjC(Array<ImageWriter>& writers);
void optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSelOpt,
const Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString>& closureSelectorMap,
ObjCOptimizerImage& image);
void optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt,
const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
const Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString>& duplicateSharedCacheClasses,
const Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore,
ObjCOptimizerImage& image);
void optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* objcProtocolOpt,
const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
ObjCOptimizerImage& image);
void writeClassOrProtocolHashTable(bool classes, Array<ObjCOptimizerImage>& objcImages);
void addDuplicateObjCClassWarning(const char* className,
const char* duplicateDefinitionPath,
const char* canonicalDefinitionPath);
void parseObjCClassDuplicates(Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore);
static bool inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum);
static void buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* toAdd);
void invalidateInitializerRoots();
Image::ResolvedSymbolTarget makeResolvedTarget(const FixupTarget& target) const;
// MachOAnalyzerSet implementations
void mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override;
bool mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const override;
void mas_mainExecutable(WrappedMachO& anImage) const override;
void* mas_dyldCache() const override;
bool wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override;
const char* wmo_path(const WrappedMachO* image) const override;
ExportsTrie wmo_getExportsTrie(const WrappedMachO* image) const override;
bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override;
const FileSystem& _fileSystem;
const RootsChecker& _rootsChecker;
const DyldSharedCache* const _dyldCache;
const PathOverrides& _pathOverrides;
const GradedArchs& _archs;
Platform const _platform;
uint32_t const _startImageNum;
const ImageArray* _dyldImageArray = nullptr;
DylibFixupHandler _dylibFixupHandler = nullptr;
const Array<CachedDylibAlias>* _aliases = nullptr;
const AtPath _atPathHandling = AtPath::none;
uint32_t _mainProgLoadIndex = 0;
const char* _mainProgLoadPath = nullptr;
Diagnostics _diag;
LaunchErrorInfo* _launchErrorInfo = nullptr;
mutable PathPool* _tempPaths = nullptr;
PathPool* _mustBeMissingPaths = nullptr;
OverflowSafeArray<SkippedFile> _skippedFiles;
uint32_t _nextIndex = 0;
OverflowSafeArray<BuilderLoadedImage,2048> _loadedImages;
OverflowSafeArray<Image::LinkedImage,65536> _dependencies; // all dylibs in cache need ~20,000 edges
OverflowSafeArray<InterposingTuple> _interposingTuples;
OverflowSafeArray<Closure::PatchEntry> _weakDefCacheOverrides;
OverflowSafeArray<uint8_t> _objcSelectorsHashTable;
OverflowSafeArray<Image::ObjCSelectorImage> _objcSelectorsHashTableImages;
OverflowSafeArray<uint8_t> _objcClassesHashTable;
OverflowSafeArray<uint8_t> _objcProtocolsHashTable;
OverflowSafeArray<Image::ObjCClassImage> _objcClassesHashTableImages;
OverflowSafeArray<uint8_t> _objcClassesDuplicatesHashTable;
PathPool* _objcDuplicateClassWarnings = nullptr;
uint32_t _alreadyInitedIndex = 0;
bool _isLaunchClosure = false;
bool _makingDyldCacheImages = false;
bool _dyldCacheIsLive = false; // means kernel is rebasing dyld cache content being viewed
bool _makingClosuresInCache = false;
bool _allowRelativePaths = false;
bool _atPathUsed = false;
bool _interposingTuplesUsed = false;
bool _fallbackPathUsed = false;
bool _allowMissingLazies = false;
bool _dyldCacheInvalidFormatVersion = false;
bool _foundNonCachedImage = false; // true means we have one or more images from disk we need to build closure(s) for
bool _foundDyldCacheRoots = false; // true if one or more images are roots of the shared cache
bool _foundMissingLazyBinds = false; // true if one or more images having missing lazy binds
bool _makeMinimalClosure = false;
bool _interposingDisabled = false;
bool _leaveRebasesAsOpcodes = false;
ImageNum _libDyldImageNum = 0;
ImageNum _libSystemImageNum = 0;
};
class VIS_HIDDEN RebasePatternBuilder
{
public:
RebasePatternBuilder(OverflowSafeArray<closure::Image::RebasePattern>& entriesStorage, uint64_t ptrSize);
void add(uint64_t runtimeOffset);
private:
OverflowSafeArray<closure::Image::RebasePattern>& _rebaseEntries;
uint64_t _lastLocation;
const uint64_t _ptrSize;
static const Image::RebasePattern _s_maxLeapPattern;
static const uint64_t _s_maxLeapCount;
};
class VIS_HIDDEN BindPatternBuilder
{
public:
BindPatternBuilder(OverflowSafeArray<closure::Image::BindPattern>& entriesStorage, uint64_t ptrSize);
void add(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, bool weakBindCoalese);
private:
OverflowSafeArray<closure::Image::BindPattern>& _bindEntries;
const uint64_t _ptrSize;
uint64_t _lastOffset;
Image::ResolvedSymbolTarget _lastTarget;
};
} // namespace closure
} // namespace dyld3
#endif /* ClosureBuilder_h */