blob: 974f323bc612ca953a629e517aa745b7c2b1685a [file] [log] [blame]
//===- 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