| //===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H |
| #define LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H |
| |
| #include "lld/Core/LinkingContext.h" |
| #include "lld/Core/Reader.h" |
| #include "lld/Core/Writer.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/BinaryFormat/MachO.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include <set> |
| |
| using llvm::MachO::HeaderFileType; |
| |
| namespace lld { |
| |
| namespace mach_o { |
| class ArchHandler; |
| class MachODylibFile; |
| class MachOFile; |
| class SectCreateFile; |
| } |
| |
| class MachOLinkingContext : public LinkingContext { |
| public: |
| MachOLinkingContext(); |
| ~MachOLinkingContext() override; |
| |
| enum Arch { |
| arch_unknown, |
| arch_ppc, |
| arch_x86, |
| arch_x86_64, |
| arch_armv6, |
| arch_armv7, |
| arch_armv7s, |
| arch_arm64, |
| }; |
| |
| enum class OS { |
| unknown, |
| macOSX, |
| iOS, |
| iOS_simulator |
| }; |
| |
| enum class ExportMode { |
| globals, // Default, all global symbols exported. |
| exported, // -exported_symbol[s_list], only listed symbols exported. |
| unexported // -unexported_symbol[s_list], no listed symbol exported. |
| }; |
| |
| enum class DebugInfoMode { |
| addDebugMap, // Default |
| noDebugMap // -S option |
| }; |
| |
| enum class UndefinedMode { |
| error, |
| warning, |
| suppress, |
| dynamicLookup |
| }; |
| |
| enum ObjCConstraint { |
| objc_unknown = 0, |
| objc_supports_gc = 2, |
| objc_gc_only = 4, |
| // Image optimized by dyld = 8 |
| // GC compaction = 16 |
| objc_retainReleaseForSimulator = 32, |
| objc_retainRelease |
| }; |
| |
| /// Initializes the context to sane default values given the specified output |
| /// file type, arch, os, and minimum os version. This should be called before |
| /// other setXXX() methods. |
| void configure(HeaderFileType type, Arch arch, OS os, uint32_t minOSVersion, |
| bool exportDynamicSymbols); |
| |
| void addPasses(PassManager &pm) override; |
| bool validateImpl() override; |
| std::string demangle(StringRef symbolName) const override; |
| |
| void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; |
| |
| /// Creates a new file which is owned by the context. Returns a pointer to |
| /// the new file. |
| template <class T, class... Args> |
| typename std::enable_if<!std::is_array<T>::value, T *>::type |
| make_file(Args &&... args) const { |
| auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...)); |
| auto *filePtr = file.get(); |
| auto *ctx = const_cast<MachOLinkingContext *>(this); |
| ctx->getNodes().push_back(std::make_unique<FileNode>(std::move(file))); |
| return filePtr; |
| } |
| |
| uint32_t getCPUType() const; |
| uint32_t getCPUSubType() const; |
| |
| bool addEntryPointLoadCommand() const; |
| bool addUnixThreadLoadCommand() const; |
| bool outputTypeHasEntry() const; |
| bool is64Bit() const; |
| |
| virtual uint64_t pageZeroSize() const { return _pageZeroSize; } |
| virtual uint64_t pageSize() const { return _pageSize; } |
| |
| mach_o::ArchHandler &archHandler() const; |
| |
| HeaderFileType outputMachOType() const { return _outputMachOType; } |
| |
| Arch arch() const { return _arch; } |
| StringRef archName() const { return nameFromArch(_arch); } |
| OS os() const { return _os; } |
| |
| ExportMode exportMode() const { return _exportMode; } |
| void setExportMode(ExportMode mode) { _exportMode = mode; } |
| void addExportSymbol(StringRef sym); |
| bool exportRestrictMode() const { return _exportMode != ExportMode::globals; } |
| bool exportSymbolNamed(StringRef sym) const; |
| |
| DebugInfoMode debugInfoMode() const { return _debugInfoMode; } |
| void setDebugInfoMode(DebugInfoMode mode) { |
| _debugInfoMode = mode; |
| } |
| |
| void appendOrderedSymbol(StringRef symbol, StringRef filename); |
| |
| bool keepPrivateExterns() const { return _keepPrivateExterns; } |
| void setKeepPrivateExterns(bool v) { _keepPrivateExterns = v; } |
| bool demangleSymbols() const { return _demangle; } |
| void setDemangleSymbols(bool d) { _demangle = d; } |
| bool mergeObjCCategories() const { return _mergeObjCCategories; } |
| void setMergeObjCCategories(bool v) { _mergeObjCCategories = v; } |
| /// Create file at specified path which will contain a binary encoding |
| /// of all input and output file paths. |
| std::error_code createDependencyFile(StringRef path); |
| void addInputFileDependency(StringRef path) const; |
| void addInputFileNotFound(StringRef path) const; |
| void addOutputFileDependency(StringRef path) const; |
| |
| bool minOS(StringRef mac, StringRef iOS) const; |
| void setDoNothing(bool value) { _doNothing = value; } |
| bool doNothing() const { return _doNothing; } |
| bool printAtoms() const { return _printAtoms; } |
| bool testingFileUsage() const { return _testingFileUsage; } |
| const StringRefVector &searchDirs() const { return _searchDirs; } |
| const StringRefVector &frameworkDirs() const { return _frameworkDirs; } |
| void setSysLibRoots(const StringRefVector &paths); |
| const StringRefVector &sysLibRoots() const { return _syslibRoots; } |
| bool PIE() const { return _pie; } |
| void setPIE(bool pie) { _pie = pie; } |
| bool generateVersionLoadCommand() const { |
| return _generateVersionLoadCommand; |
| } |
| void setGenerateVersionLoadCommand(bool v) { |
| _generateVersionLoadCommand = v; |
| } |
| |
| bool generateFunctionStartsLoadCommand() const { |
| return _generateFunctionStartsLoadCommand; |
| } |
| void setGenerateFunctionStartsLoadCommand(bool v) { |
| _generateFunctionStartsLoadCommand = v; |
| } |
| |
| bool generateDataInCodeLoadCommand() const { |
| return _generateDataInCodeLoadCommand; |
| } |
| void setGenerateDataInCodeLoadCommand(bool v) { |
| _generateDataInCodeLoadCommand = v; |
| } |
| |
| uint64_t stackSize() const { return _stackSize; } |
| void setStackSize(uint64_t stackSize) { _stackSize = stackSize; } |
| |
| uint64_t baseAddress() const { return _baseAddress; } |
| void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; } |
| |
| ObjCConstraint objcConstraint() const { return _objcConstraint; } |
| |
| uint32_t osMinVersion() const { return _osMinVersion; } |
| |
| uint32_t sdkVersion() const { return _sdkVersion; } |
| void setSdkVersion(uint64_t v) { _sdkVersion = v; } |
| |
| uint64_t sourceVersion() const { return _sourceVersion; } |
| void setSourceVersion(uint64_t v) { _sourceVersion = v; } |
| |
| uint32_t swiftVersion() const { return _swiftVersion; } |
| |
| /// Checks whether a given path on the filesystem exists. |
| /// |
| /// When running in -test_file_usage mode, this method consults an |
| /// internally maintained list of files that exist (provided by -path_exists) |
| /// instead of the actual filesystem. |
| bool pathExists(StringRef path) const; |
| |
| /// Like pathExists() but only used on files - not directories. |
| bool fileExists(StringRef path) const; |
| |
| /// Adds any library search paths derived from the given base, possibly |
| /// modified by -syslibroots. |
| /// |
| /// The set of paths added consists of approximately all syslibroot-prepended |
| /// versions of libPath that exist, or the original libPath if there are none |
| /// for whatever reason. With various edge-cases for compatibility. |
| void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false); |
| |
| /// Determine whether -lFoo can be resolve within the given path, and |
| /// return the filename if so. |
| /// |
| /// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in |
| /// that order, unless Foo ends in ".o", in which case only the exact file |
| /// matches (e.g. -lfoo.o would only find foo.o). |
| llvm::Optional<StringRef> searchDirForLibrary(StringRef path, |
| StringRef libName) const; |
| |
| /// Iterates through all search path entries looking for libName (as |
| /// specified by -lFoo). |
| llvm::Optional<StringRef> searchLibrary(StringRef libName) const; |
| |
| /// Add a framework search path. Internally, this method may be prepended |
| /// the path with syslibroot. |
| void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false); |
| |
| /// Iterates through all framework directories looking for |
| /// Foo.framework/Foo (when fwName = "Foo"). |
| llvm::Optional<StringRef> findPathForFramework(StringRef fwName) const; |
| |
| /// The dylib's binary compatibility version, in the raw uint32 format. |
| /// |
| /// When building a dynamic library, this is the compatibility version that |
| /// gets embedded into the result. Other Mach-O binaries that link against |
| /// this library will store the compatibility version in its load command. At |
| /// runtime, the loader will verify that the binary is compatible with the |
| /// installed dynamic library. |
| uint32_t compatibilityVersion() const { return _compatibilityVersion; } |
| |
| /// The dylib's current version, in the raw uint32 format. |
| /// |
| /// When building a dynamic library, this is the current version that gets |
| /// embedded into the result. Other Mach-O binaries that link against |
| /// this library will store the compatibility version in its load command. |
| uint32_t currentVersion() const { return _currentVersion; } |
| |
| /// The dylib's install name. |
| /// |
| /// Binaries that link against the dylib will embed this path into the dylib |
| /// load command. When loading the binaries at runtime, this is the location |
| /// on disk that the loader will look for the dylib. |
| StringRef installName() const { return _installName; } |
| |
| /// Whether or not the dylib has side effects during initialization. |
| /// |
| /// Dylibs marked as being dead strippable provide the guarantee that loading |
| /// the dylib has no side effects, allowing the linker to strip out the dylib |
| /// when linking a binary that does not use any of its symbols. |
| bool deadStrippableDylib() const { return _deadStrippableDylib; } |
| |
| /// Whether or not to use flat namespace. |
| /// |
| /// MachO usually uses a two-level namespace, where each external symbol |
| /// referenced by the target is associated with the dylib that will provide |
| /// the symbol's definition at runtime. Using flat namespace overrides this |
| /// behavior: the linker searches all dylibs on the command line and all |
| /// dylibs those original dylibs depend on, but does not record which dylib |
| /// an external symbol came from. At runtime dyld again searches all images |
| /// and uses the first definition it finds. In addition, any undefines in |
| /// loaded flat_namespace dylibs must be resolvable at build time. |
| bool useFlatNamespace() const { return _flatNamespace; } |
| |
| /// How to handle undefined symbols. |
| /// |
| /// Options are: |
| /// * error: Report an error and terminate linking. |
| /// * warning: Report a warning, but continue linking. |
| /// * suppress: Ignore and continue linking. |
| /// * dynamic_lookup: For use with -twolevel namespace: Records source dylibs |
| /// for symbols that are defined in a linked dylib at static link time. |
| /// Undefined symbols are handled by searching all loaded images at |
| /// runtime. |
| UndefinedMode undefinedMode() const { return _undefinedMode; } |
| |
| /// The path to the executable that will load the bundle at runtime. |
| /// |
| /// When building a Mach-O bundle, this executable will be examined if there |
| /// are undefined symbols after the main link phase. It is expected that this |
| /// binary will be loading the bundle at runtime and will provide the symbols |
| /// at that point. |
| StringRef bundleLoader() const { return _bundleLoader; } |
| |
| void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; } |
| void setCurrentVersion(uint32_t vers) { _currentVersion = vers; } |
| void setInstallName(StringRef name) { _installName = name; } |
| void setDeadStrippableDylib(bool deadStrippable) { |
| _deadStrippableDylib = deadStrippable; |
| } |
| void setUseFlatNamespace(bool flatNamespace) { |
| _flatNamespace = flatNamespace; |
| } |
| |
| void setUndefinedMode(UndefinedMode undefinedMode) { |
| _undefinedMode = undefinedMode; |
| } |
| |
| void setBundleLoader(StringRef loader) { _bundleLoader = loader; } |
| void setPrintAtoms(bool value=true) { _printAtoms = value; } |
| void setTestingFileUsage(bool value = true) { |
| _testingFileUsage = value; |
| } |
| void addExistingPathForDebug(StringRef path) { |
| _existingPaths.insert(path); |
| } |
| |
| void addRpath(StringRef rpath); |
| const StringRefVector &rpaths() const { return _rpaths; } |
| |
| /// Add section alignment constraint on final layout. |
| void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align); |
| |
| /// Add a section based on a command-line sectcreate option. |
| void addSectCreateSection(StringRef seg, StringRef sect, |
| std::unique_ptr<MemoryBuffer> content); |
| |
| /// Returns true if specified section had alignment constraints. |
| bool sectionAligned(StringRef seg, StringRef sect, uint16_t &align) const; |
| |
| StringRef dyldPath() const { return "/usr/lib/dyld"; } |
| |
| /// Stub creation Pass should be run. |
| bool needsStubsPass() const; |
| |
| // GOT creation Pass should be run. |
| bool needsGOTPass() const; |
| |
| /// Pass to add TLV sections. |
| bool needsTLVPass() const; |
| |
| /// Pass to transform __compact_unwind into __unwind_info should be run. |
| bool needsCompactUnwindPass() const; |
| |
| /// Pass to add shims switching between thumb and arm mode. |
| bool needsShimPass() const; |
| |
| /// Pass to add objc image info and optimized objc data. |
| bool needsObjCPass() const; |
| |
| /// Magic symbol name stubs will need to help lazy bind. |
| StringRef binderSymbolName() const; |
| |
| /// Used to keep track of direct and indirect dylibs. |
| void registerDylib(mach_o::MachODylibFile *dylib, bool upward) const; |
| |
| // Reads a file from disk to memory. Returns only a needed chunk |
| // if a fat binary. |
| ErrorOr<std::unique_ptr<MemoryBuffer>> getMemoryBuffer(StringRef path); |
| |
| /// Used to find indirect dylibs. Instantiates a MachODylibFile if one |
| /// has not already been made for the requested dylib. Uses -L and -F |
| /// search paths to allow indirect dylibs to be overridden. |
| mach_o::MachODylibFile* findIndirectDylib(StringRef path); |
| |
| uint32_t dylibCurrentVersion(StringRef installName) const; |
| |
| uint32_t dylibCompatVersion(StringRef installName) const; |
| |
| ArrayRef<mach_o::MachODylibFile*> allDylibs() const { |
| return _allDylibs; |
| } |
| |
| /// Creates a copy (owned by this MachOLinkingContext) of a string. |
| StringRef copy(StringRef str) { return str.copy(_allocator); } |
| |
| /// If the memoryBuffer is a fat file with a slice for the current arch, |
| /// this method will return the offset and size of that slice. |
| bool sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size); |
| |
| /// Returns if a command line option specified dylib is an upward link. |
| bool isUpwardDylib(StringRef installName) const; |
| |
| static bool isThinObjectFile(StringRef path, Arch &arch); |
| static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype); |
| static Arch archFromName(StringRef archName); |
| static StringRef nameFromArch(Arch arch); |
| static uint32_t cpuTypeFromArch(Arch arch); |
| static uint32_t cpuSubtypeFromArch(Arch arch); |
| static bool is64Bit(Arch arch); |
| static bool isHostEndian(Arch arch); |
| static bool isBigEndian(Arch arch); |
| |
| /// Construct 32-bit value from string "X.Y.Z" where |
| /// bits are xxxx.yy.zz. Largest number is 65535.255.255 |
| static bool parsePackedVersion(StringRef str, uint32_t &result); |
| |
| /// Construct 64-bit value from string "A.B.C.D.E" where |
| /// bits are aaaa.bb.cc.dd.ee. Largest number is 16777215.1023.1023.1023.1023 |
| static bool parsePackedVersion(StringRef str, uint64_t &result); |
| |
| void finalizeInputFiles() override; |
| |
| llvm::Error handleLoadedFile(File &file) override; |
| |
| bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right, |
| bool &leftBeforeRight) const; |
| |
| /// Return the 'flat namespace' file. This is the file that supplies |
| /// atoms for otherwise undefined symbols when the -flat_namespace or |
| /// -undefined dynamic_lookup options are used. |
| File* flatNamespaceFile() const { return _flatNamespaceFile; } |
| |
| private: |
| Writer &writer() const override; |
| mach_o::MachODylibFile* loadIndirectDylib(StringRef path); |
| struct ArchInfo { |
| StringRef archName; |
| MachOLinkingContext::Arch arch; |
| bool littleEndian; |
| uint32_t cputype; |
| uint32_t cpusubtype; |
| }; |
| |
| struct SectionAlign { |
| StringRef segmentName; |
| StringRef sectionName; |
| uint16_t align; |
| }; |
| |
| struct OrderFileNode { |
| StringRef fileFilter; |
| unsigned order; |
| }; |
| |
| static bool findOrderOrdinal(const std::vector<OrderFileNode> &nodes, |
| const DefinedAtom *atom, unsigned &ordinal); |
| |
| static ArchInfo _s_archInfos[]; |
| |
| std::set<StringRef> _existingPaths; // For testing only. |
| StringRefVector _searchDirs; |
| StringRefVector _syslibRoots; |
| StringRefVector _frameworkDirs; |
| HeaderFileType _outputMachOType = llvm::MachO::MH_EXECUTE; |
| bool _outputMachOTypeStatic = false; // Disambiguate static vs dynamic prog |
| bool _doNothing = false; // for -help and -v which just print info |
| bool _pie = false; |
| Arch _arch = arch_unknown; |
| OS _os = OS::macOSX; |
| uint32_t _osMinVersion = 0; |
| uint32_t _sdkVersion = 0; |
| uint64_t _sourceVersion = 0; |
| uint64_t _pageZeroSize = 0; |
| uint64_t _pageSize = 4096; |
| uint64_t _baseAddress = 0; |
| uint64_t _stackSize = 0; |
| uint32_t _compatibilityVersion = 0; |
| uint32_t _currentVersion = 0; |
| ObjCConstraint _objcConstraint = objc_unknown; |
| uint32_t _swiftVersion = 0; |
| StringRef _installName; |
| StringRefVector _rpaths; |
| bool _flatNamespace = false; |
| UndefinedMode _undefinedMode = UndefinedMode::error; |
| bool _deadStrippableDylib = false; |
| bool _printAtoms = false; |
| bool _testingFileUsage = false; |
| bool _keepPrivateExterns = false; |
| bool _demangle = false; |
| bool _mergeObjCCategories = true; |
| bool _generateVersionLoadCommand = false; |
| bool _generateFunctionStartsLoadCommand = false; |
| bool _generateDataInCodeLoadCommand = false; |
| StringRef _bundleLoader; |
| mutable std::unique_ptr<mach_o::ArchHandler> _archHandler; |
| mutable std::unique_ptr<Writer> _writer; |
| std::vector<SectionAlign> _sectAligns; |
| mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap; |
| mutable std::vector<mach_o::MachODylibFile*> _allDylibs; |
| mutable std::set<mach_o::MachODylibFile*> _upwardDylibs; |
| mutable std::vector<std::unique_ptr<File>> _indirectDylibs; |
| mutable std::mutex _dylibsMutex; |
| ExportMode _exportMode = ExportMode::globals; |
| llvm::StringSet<> _exportedSymbols; |
| DebugInfoMode _debugInfoMode = DebugInfoMode::addDebugMap; |
| std::unique_ptr<llvm::raw_fd_ostream> _dependencyInfo; |
| llvm::StringMap<std::vector<OrderFileNode>> _orderFiles; |
| unsigned _orderFileEntries = 0; |
| File *_flatNamespaceFile = nullptr; |
| mach_o::SectCreateFile *_sectCreateFile = nullptr; |
| }; |
| |
| } // end namespace lld |
| |
| #endif // LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H |