You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
508 lines
19 KiB
508 lines
19 KiB
//===- 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.
|
|
whiteList, // -exported_symbol[s_list], only listed symbols exported.
|
|
blackList // -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);
|
|
void checkExportWhiteList(const DefinedAtom *atom) const;
|
|
void checkExportBlackList(const DefinedAtom *atom) const;
|
|
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
|