forked from OSchip/llvm-project
[lld-macho] Remove old macho darwin lld
During the llvm round table it was generally agreed that the newer macho lld implementation is feature complete enough to replace the old implementation entirely. This will reduce confusion for new users who aren't aware of the history. Differential Revision: https://reviews.llvm.org/D114842
This commit is contained in:
parent
98dd2f9ed3
commit
9e3552523e
|
@ -195,12 +195,10 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
|||
endif()
|
||||
|
||||
add_subdirectory(Common)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tools/lld)
|
||||
|
||||
if (LLVM_INCLUDE_TESTS)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(unittests)
|
||||
endif()
|
||||
|
||||
add_subdirectory(docs)
|
||||
|
|
|
@ -42,11 +42,6 @@ bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
|
|||
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
|
||||
}
|
||||
|
||||
namespace mach_o {
|
||||
bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
|
||||
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
|
||||
}
|
||||
|
||||
namespace macho {
|
||||
bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
|
||||
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
|
||||
|
|
|
@ -42,9 +42,8 @@ class Reference {
|
|||
public:
|
||||
/// Which universe defines the kindValue().
|
||||
enum class KindNamespace {
|
||||
all = 0,
|
||||
all = 0,
|
||||
testing = 1,
|
||||
mach_o = 2,
|
||||
};
|
||||
|
||||
KindNamespace kindNamespace() const { return (KindNamespace)_kindNamespace; }
|
||||
|
|
|
@ -1,505 +0,0 @@
|
|||
//===- 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
|
|
@ -1,42 +0,0 @@
|
|||
//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===//
|
||||
//
|
||||
// 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_YAML_CONTEXT_H
|
||||
#define LLD_READER_WRITER_YAML_CONTEXT_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
class File;
|
||||
class LinkingContext;
|
||||
class Registry;
|
||||
namespace mach_o {
|
||||
namespace normalized {
|
||||
struct NormalizedFile;
|
||||
}
|
||||
}
|
||||
|
||||
using lld::mach_o::normalized::NormalizedFile;
|
||||
|
||||
/// When YAML I/O is used in lld, the yaml context always holds a YamlContext
|
||||
/// object. We need to support hetergenous yaml documents which each require
|
||||
/// different context info. This struct supports all clients.
|
||||
struct YamlContext {
|
||||
const LinkingContext *_ctx = nullptr;
|
||||
const Registry *_registry = nullptr;
|
||||
File *_file = nullptr;
|
||||
NormalizedFile *_normalizeMachOFile = nullptr;
|
||||
StringRef _path;
|
||||
};
|
||||
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_YAML_CONTEXT_H
|
|
@ -1,3 +0,0 @@
|
|||
add_subdirectory(Core)
|
||||
add_subdirectory(Driver)
|
||||
add_subdirectory(ReaderWriter)
|
|
@ -1,24 +0,0 @@
|
|||
add_lld_library(lldCore
|
||||
DefinedAtom.cpp
|
||||
Error.cpp
|
||||
File.cpp
|
||||
LinkingContext.cpp
|
||||
Reader.cpp
|
||||
Resolver.cpp
|
||||
SymbolTable.cpp
|
||||
Writer.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/Core
|
||||
|
||||
LINK_COMPONENTS
|
||||
BinaryFormat
|
||||
MC
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
|
@ -1,81 +0,0 @@
|
|||
//===- DefinedAtom.cpp ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
|
||||
namespace lld {
|
||||
|
||||
DefinedAtom::ContentPermissions DefinedAtom::permissions() const {
|
||||
// By default base permissions on content type.
|
||||
return permissions(this->contentType());
|
||||
}
|
||||
|
||||
// Utility function for deriving permissions from content type
|
||||
DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
|
||||
switch (type) {
|
||||
case typeCode:
|
||||
case typeResolver:
|
||||
case typeBranchIsland:
|
||||
case typeBranchShim:
|
||||
case typeStub:
|
||||
case typeStubHelper:
|
||||
case typeMachHeader:
|
||||
return permR_X;
|
||||
|
||||
case typeConstant:
|
||||
case typeCString:
|
||||
case typeUTF16String:
|
||||
case typeCFI:
|
||||
case typeLSDA:
|
||||
case typeLiteral4:
|
||||
case typeLiteral8:
|
||||
case typeLiteral16:
|
||||
case typeDTraceDOF:
|
||||
case typeCompactUnwindInfo:
|
||||
case typeProcessedUnwindInfo:
|
||||
case typeObjCImageInfo:
|
||||
case typeObjCMethodList:
|
||||
return permR__;
|
||||
|
||||
case typeData:
|
||||
case typeDataFast:
|
||||
case typeZeroFill:
|
||||
case typeZeroFillFast:
|
||||
case typeObjC1Class:
|
||||
case typeLazyPointer:
|
||||
case typeLazyDylibPointer:
|
||||
case typeNonLazyPointer:
|
||||
case typeThunkTLV:
|
||||
return permRW_;
|
||||
|
||||
case typeGOT:
|
||||
case typeConstData:
|
||||
case typeCFString:
|
||||
case typeInitializerPtr:
|
||||
case typeTerminatorPtr:
|
||||
case typeCStringPtr:
|
||||
case typeObjCClassPtr:
|
||||
case typeObjC2CategoryList:
|
||||
case typeInterposingTuples:
|
||||
case typeTLVInitialData:
|
||||
case typeTLVInitialZeroFill:
|
||||
case typeTLVInitializerPtr:
|
||||
return permRW_L;
|
||||
|
||||
case typeUnknown:
|
||||
case typeTempLTO:
|
||||
case typeSectCreate:
|
||||
case typeDSOHandle:
|
||||
return permUnknown;
|
||||
}
|
||||
llvm_unreachable("unknown content type");
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -1,93 +0,0 @@
|
|||
//===- Error.cpp - system_error extensions for lld --------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/Error.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace lld;
|
||||
|
||||
namespace {
|
||||
class _YamlReaderErrorCategory : public std::error_category {
|
||||
public:
|
||||
const char* name() const noexcept override {
|
||||
return "lld.yaml.reader";
|
||||
}
|
||||
|
||||
std::string message(int ev) const override {
|
||||
switch (static_cast<YamlReaderError>(ev)) {
|
||||
case YamlReaderError::unknown_keyword:
|
||||
return "Unknown keyword found in yaml file";
|
||||
case YamlReaderError::illegal_value:
|
||||
return "Bad value found in yaml file";
|
||||
}
|
||||
llvm_unreachable("An enumerator of YamlReaderError does not have a "
|
||||
"message defined.");
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
const std::error_category &lld::YamlReaderCategory() {
|
||||
static _YamlReaderErrorCategory o;
|
||||
return o;
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
|
||||
/// Temporary class to enable make_dynamic_error_code() until
|
||||
/// llvm::ErrorOr<> is updated to work with error encapsulations
|
||||
/// other than error_code.
|
||||
class dynamic_error_category : public std::error_category {
|
||||
public:
|
||||
~dynamic_error_category() override = default;
|
||||
|
||||
const char *name() const noexcept override {
|
||||
return "lld.dynamic_error";
|
||||
}
|
||||
|
||||
std::string message(int ev) const override {
|
||||
assert(ev >= 0);
|
||||
assert(ev < (int)_messages.size());
|
||||
// The value is an index into the string vector.
|
||||
return _messages[ev];
|
||||
}
|
||||
|
||||
int add(std::string msg) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
// Value zero is always the success value.
|
||||
if (_messages.empty())
|
||||
_messages.push_back("Success");
|
||||
_messages.push_back(msg);
|
||||
// Return the index of the string just appended.
|
||||
return _messages.size() - 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> _messages;
|
||||
std::recursive_mutex _mutex;
|
||||
};
|
||||
|
||||
static dynamic_error_category categorySingleton;
|
||||
|
||||
std::error_code make_dynamic_error_code(StringRef msg) {
|
||||
return std::error_code(categorySingleton.add(std::string(msg)),
|
||||
categorySingleton);
|
||||
}
|
||||
|
||||
char GenericError::ID = 0;
|
||||
|
||||
GenericError::GenericError(Twine Msg) : Msg(Msg.str()) { }
|
||||
|
||||
void GenericError::log(raw_ostream &OS) const {
|
||||
OS << Msg;
|
||||
}
|
||||
|
||||
} // namespace lld
|
|
@ -1,28 +0,0 @@
|
|||
//===- Core/File.cpp - A Container of Atoms -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/File.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace lld {
|
||||
|
||||
File::~File() = default;
|
||||
|
||||
File::AtomVector<DefinedAtom> File::_noDefinedAtoms;
|
||||
File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms;
|
||||
File::AtomVector<SharedLibraryAtom> File::_noSharedLibraryAtoms;
|
||||
File::AtomVector<AbsoluteAtom> File::_noAbsoluteAtoms;
|
||||
|
||||
std::error_code File::parse() {
|
||||
std::lock_guard<std::mutex> lock(_parseMutex);
|
||||
if (!_lastError.hasValue())
|
||||
_lastError = doParse();
|
||||
return _lastError.getValue();
|
||||
}
|
||||
|
||||
} // end namespace lld
|
|
@ -1,69 +0,0 @@
|
|||
//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/LinkingContext.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Node.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/Core/Writer.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace lld {
|
||||
|
||||
LinkingContext::LinkingContext() = default;
|
||||
|
||||
LinkingContext::~LinkingContext() = default;
|
||||
|
||||
bool LinkingContext::validate() {
|
||||
return validateImpl();
|
||||
}
|
||||
|
||||
llvm::Error LinkingContext::writeFile(const File &linkedFile) const {
|
||||
return this->writer().writeFile(linkedFile, _outputPath);
|
||||
}
|
||||
|
||||
std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {
|
||||
return createEntrySymbolFile("<command line option -e>");
|
||||
}
|
||||
|
||||
std::unique_ptr<File>
|
||||
LinkingContext::createEntrySymbolFile(StringRef filename) const {
|
||||
if (entrySymbolName().empty())
|
||||
return nullptr;
|
||||
std::unique_ptr<SimpleFile> entryFile(new SimpleFile(filename,
|
||||
File::kindEntryObject));
|
||||
entryFile->addAtom(
|
||||
*(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
|
||||
return std::move(entryFile);
|
||||
}
|
||||
|
||||
std::unique_ptr<File> LinkingContext::createUndefinedSymbolFile() const {
|
||||
return createUndefinedSymbolFile("<command line option -u or --defsym>");
|
||||
}
|
||||
|
||||
std::unique_ptr<File>
|
||||
LinkingContext::createUndefinedSymbolFile(StringRef filename) const {
|
||||
if (_initialUndefinedSymbols.empty())
|
||||
return nullptr;
|
||||
std::unique_ptr<SimpleFile> undefinedSymFile(
|
||||
new SimpleFile(filename, File::kindUndefinedSymsObject));
|
||||
for (StringRef undefSym : _initialUndefinedSymbols)
|
||||
undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
|
||||
*undefinedSymFile, undefSym)));
|
||||
return std::move(undefinedSymFile);
|
||||
}
|
||||
|
||||
void LinkingContext::createInternalFiles(
|
||||
std::vector<std::unique_ptr<File>> &result) const {
|
||||
if (std::unique_ptr<File> file = createEntrySymbolFile())
|
||||
result.push_back(std::move(file));
|
||||
if (std::unique_ptr<File> file = createUndefinedSymbolFile())
|
||||
result.push_back(std::move(file));
|
||||
}
|
||||
|
||||
} // end namespace lld
|
|
@ -1,113 +0,0 @@
|
|||
//===- lib/Core/Reader.cpp ------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/Reader.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
using llvm::file_magic;
|
||||
using llvm::identify_magic;
|
||||
|
||||
namespace lld {
|
||||
|
||||
YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() = default;
|
||||
|
||||
void Registry::add(std::unique_ptr<Reader> reader) {
|
||||
_readers.push_back(std::move(reader));
|
||||
}
|
||||
|
||||
void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) {
|
||||
_yamlHandlers.push_back(std::move(handler));
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<File>>
|
||||
Registry::loadFile(std::unique_ptr<MemoryBuffer> mb) const {
|
||||
// Get file magic.
|
||||
StringRef content(mb->getBufferStart(), mb->getBufferSize());
|
||||
file_magic fileType = identify_magic(content);
|
||||
|
||||
// Ask each registered reader if it can handle this file type or extension.
|
||||
for (const std::unique_ptr<Reader> &reader : _readers) {
|
||||
if (!reader->canParse(fileType, mb->getMemBufferRef()))
|
||||
continue;
|
||||
return reader->loadFile(std::move(mb), *this);
|
||||
}
|
||||
|
||||
// No Reader could parse this file.
|
||||
return make_error_code(llvm::errc::executable_format_error);
|
||||
}
|
||||
|
||||
static const Registry::KindStrings kindStrings[] = {
|
||||
{Reference::kindLayoutAfter, "layout-after"},
|
||||
{Reference::kindAssociate, "associate"},
|
||||
LLD_KIND_STRING_END};
|
||||
|
||||
Registry::Registry() {
|
||||
addKindTable(Reference::KindNamespace::all, Reference::KindArch::all,
|
||||
kindStrings);
|
||||
}
|
||||
|
||||
bool Registry::handleTaggedDoc(llvm::yaml::IO &io,
|
||||
const lld::File *&file) const {
|
||||
for (const std::unique_ptr<YamlIOTaggedDocumentHandler> &h : _yamlHandlers)
|
||||
if (h->handledDocTag(io, file))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Registry::addKindTable(Reference::KindNamespace ns,
|
||||
Reference::KindArch arch,
|
||||
const KindStrings array[]) {
|
||||
KindEntry entry = { ns, arch, array };
|
||||
_kindEntries.push_back(entry);
|
||||
}
|
||||
|
||||
bool Registry::referenceKindFromString(StringRef inputStr,
|
||||
Reference::KindNamespace &ns,
|
||||
Reference::KindArch &arch,
|
||||
Reference::KindValue &value) const {
|
||||
for (const KindEntry &entry : _kindEntries) {
|
||||
for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
|
||||
if (!inputStr.equals(pair->name))
|
||||
continue;
|
||||
ns = entry.ns;
|
||||
arch = entry.arch;
|
||||
value = pair->value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Registry::referenceKindToString(Reference::KindNamespace ns,
|
||||
Reference::KindArch arch,
|
||||
Reference::KindValue value,
|
||||
StringRef &str) const {
|
||||
for (const KindEntry &entry : _kindEntries) {
|
||||
if (entry.ns != ns)
|
||||
continue;
|
||||
if (entry.arch != arch)
|
||||
continue;
|
||||
for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
|
||||
if (pair->value != value)
|
||||
continue;
|
||||
str = pair->name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace lld
|
|
@ -1,496 +0,0 @@
|
|||
//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/Resolver.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/ArchiveLibraryFile.h"
|
||||
#include "lld/Core/Atom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Instrumentation.h"
|
||||
#include "lld/Core/LinkingContext.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "lld/Core/SymbolTable.h"
|
||||
#include "lld/Core/UndefinedAtom.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
|
||||
llvm::Expected<bool> Resolver::handleFile(File &file) {
|
||||
if (auto ec = _ctx.handleLoadedFile(file))
|
||||
return std::move(ec);
|
||||
bool undefAdded = false;
|
||||
for (auto &atom : file.defined().owning_ptrs())
|
||||
doDefinedAtom(std::move(atom));
|
||||
for (auto &atom : file.undefined().owning_ptrs()) {
|
||||
if (doUndefinedAtom(std::move(atom)))
|
||||
undefAdded = true;
|
||||
}
|
||||
for (auto &atom : file.sharedLibrary().owning_ptrs())
|
||||
doSharedLibraryAtom(std::move(atom));
|
||||
for (auto &atom : file.absolute().owning_ptrs())
|
||||
doAbsoluteAtom(std::move(atom));
|
||||
return undefAdded;
|
||||
}
|
||||
|
||||
llvm::Expected<bool> Resolver::forEachUndefines(File &file,
|
||||
UndefCallback callback) {
|
||||
size_t i = _undefineIndex[&file];
|
||||
bool undefAdded = false;
|
||||
do {
|
||||
for (; i < _undefines.size(); ++i) {
|
||||
StringRef undefName = _undefines[i];
|
||||
if (undefName.empty())
|
||||
continue;
|
||||
const Atom *atom = _symbolTable.findByName(undefName);
|
||||
if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) {
|
||||
// The symbol was resolved by some other file. Cache the result.
|
||||
_undefines[i] = "";
|
||||
continue;
|
||||
}
|
||||
auto undefAddedOrError = callback(undefName);
|
||||
if (auto ec = undefAddedOrError.takeError())
|
||||
return std::move(ec);
|
||||
undefAdded |= undefAddedOrError.get();
|
||||
}
|
||||
} while (i < _undefines.size());
|
||||
_undefineIndex[&file] = i;
|
||||
return undefAdded;
|
||||
}
|
||||
|
||||
llvm::Expected<bool> Resolver::handleArchiveFile(File &file) {
|
||||
ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file);
|
||||
return forEachUndefines(file,
|
||||
[&](StringRef undefName) -> llvm::Expected<bool> {
|
||||
if (File *member = archiveFile->find(undefName)) {
|
||||
member->setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
return handleFile(*member);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
llvm::Error Resolver::handleSharedLibrary(File &file) {
|
||||
// Add all the atoms from the shared library
|
||||
SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file);
|
||||
auto undefAddedOrError = handleFile(*sharedLibrary);
|
||||
if (auto ec = undefAddedOrError.takeError())
|
||||
return ec;
|
||||
undefAddedOrError =
|
||||
forEachUndefines(file, [&](StringRef undefName) -> llvm::Expected<bool> {
|
||||
auto atom = sharedLibrary->exports(undefName);
|
||||
if (atom.get())
|
||||
doSharedLibraryAtom(std::move(atom));
|
||||
return false;
|
||||
});
|
||||
|
||||
if (auto ec = undefAddedOrError.takeError())
|
||||
return ec;
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
bool Resolver::doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< " UndefinedAtom: "
|
||||
<< llvm::format("0x%09lX", atom.get())
|
||||
<< ", name=" << atom.get()->name() << "\n");
|
||||
|
||||
// tell symbol table
|
||||
bool newUndefAdded = _symbolTable.add(*atom.get());
|
||||
if (newUndefAdded)
|
||||
_undefines.push_back(atom.get()->name());
|
||||
|
||||
// add to list of known atoms
|
||||
_atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
|
||||
|
||||
return newUndefAdded;
|
||||
}
|
||||
|
||||
// Called on each atom when a file is added. Returns true if a given
|
||||
// atom is added to the symbol table.
|
||||
void Resolver::doDefinedAtom(OwningAtomPtr<DefinedAtom> atom) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< " DefinedAtom: "
|
||||
<< llvm::format("0x%09lX", atom.get())
|
||||
<< ", file=#"
|
||||
<< atom.get()->file().ordinal()
|
||||
<< ", atom=#"
|
||||
<< atom.get()->ordinal()
|
||||
<< ", name="
|
||||
<< atom.get()->name()
|
||||
<< ", type="
|
||||
<< atom.get()->contentType()
|
||||
<< "\n");
|
||||
|
||||
// An atom that should never be dead-stripped is a dead-strip root.
|
||||
if (_ctx.deadStrip() &&
|
||||
atom.get()->deadStrip() == DefinedAtom::deadStripNever) {
|
||||
_deadStripRoots.insert(atom.get());
|
||||
}
|
||||
|
||||
// add to list of known atoms
|
||||
_symbolTable.add(*atom.get());
|
||||
_atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
|
||||
}
|
||||
|
||||
void Resolver::doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< " SharedLibraryAtom: "
|
||||
<< llvm::format("0x%09lX", atom.get())
|
||||
<< ", name="
|
||||
<< atom.get()->name()
|
||||
<< "\n");
|
||||
|
||||
// tell symbol table
|
||||
_symbolTable.add(*atom.get());
|
||||
|
||||
// add to list of known atoms
|
||||
_atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
|
||||
}
|
||||
|
||||
void Resolver::doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< " AbsoluteAtom: "
|
||||
<< llvm::format("0x%09lX", atom.get())
|
||||
<< ", name="
|
||||
<< atom.get()->name()
|
||||
<< "\n");
|
||||
|
||||
// tell symbol table
|
||||
if (atom.get()->scope() != Atom::scopeTranslationUnit)
|
||||
_symbolTable.add(*atom.get());
|
||||
|
||||
// add to list of known atoms
|
||||
_atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
|
||||
}
|
||||
|
||||
// Returns true if at least one of N previous files has created an
|
||||
// undefined symbol.
|
||||
bool Resolver::undefinesAdded(int begin, int end) {
|
||||
std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
|
||||
for (int i = begin; i < end; ++i)
|
||||
if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
|
||||
if (_newUndefinesAdded[node->getFile()])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
File *Resolver::getFile(int &index) {
|
||||
std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
|
||||
if ((size_t)index >= inputs.size())
|
||||
return nullptr;
|
||||
if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
|
||||
// We are at the end of the current group. If one or more new
|
||||
// undefined atom has been added in the last groupSize files, we
|
||||
// reiterate over the files.
|
||||
int size = group->getSize();
|
||||
if (undefinesAdded(index - size, index)) {
|
||||
index -= size;
|
||||
return getFile(index);
|
||||
}
|
||||
++index;
|
||||
return getFile(index);
|
||||
}
|
||||
return cast<FileNode>(inputs[index++].get())->getFile();
|
||||
}
|
||||
|
||||
// Keep adding atoms until _ctx.getNextFile() returns an error. This
|
||||
// function is where undefined atoms are resolved.
|
||||
bool Resolver::resolveUndefines() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Resolving undefines:\n");
|
||||
ScopedTask task(getDefaultDomain(), "resolveUndefines");
|
||||
int index = 0;
|
||||
std::set<File *> seen;
|
||||
for (;;) {
|
||||
bool undefAdded = false;
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "Loading file #" << index << "\n");
|
||||
File *file = getFile(index);
|
||||
if (!file)
|
||||
return true;
|
||||
if (std::error_code ec = file->parse()) {
|
||||
llvm::errs() << "Cannot open " + file->path() << ": " << ec.message()
|
||||
<< "\n";
|
||||
return false;
|
||||
}
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "Loaded file: " << file->path() << "\n");
|
||||
switch (file->kind()) {
|
||||
case File::kindErrorObject:
|
||||
case File::kindNormalizedObject:
|
||||
case File::kindMachObject:
|
||||
case File::kindCEntryObject:
|
||||
case File::kindHeaderObject:
|
||||
case File::kindEntryObject:
|
||||
case File::kindUndefinedSymsObject:
|
||||
case File::kindStubHelperObject:
|
||||
case File::kindResolverMergedObject:
|
||||
case File::kindSectCreateObject: {
|
||||
// The same file may be visited more than once if the file is
|
||||
// in --start-group and --end-group. Only library files should
|
||||
// be processed more than once.
|
||||
if (seen.count(file))
|
||||
break;
|
||||
seen.insert(file);
|
||||
assert(!file->hasOrdinal());
|
||||
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
auto undefAddedOrError = handleFile(*file);
|
||||
if (auto EC = undefAddedOrError.takeError()) {
|
||||
// FIXME: This should be passed to logAllUnhandledErrors but it needs
|
||||
// to be passed a Twine instead of a string.
|
||||
llvm::errs() << "Error in " + file->path() << ": ";
|
||||
logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
|
||||
return false;
|
||||
}
|
||||
undefAdded = undefAddedOrError.get();
|
||||
break;
|
||||
}
|
||||
case File::kindArchiveLibrary: {
|
||||
if (!file->hasOrdinal())
|
||||
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
auto undefAddedOrError = handleArchiveFile(*file);
|
||||
if (auto EC = undefAddedOrError.takeError()) {
|
||||
// FIXME: This should be passed to logAllUnhandledErrors but it needs
|
||||
// to be passed a Twine instead of a string.
|
||||
llvm::errs() << "Error in " + file->path() << ": ";
|
||||
logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
|
||||
return false;
|
||||
}
|
||||
undefAdded = undefAddedOrError.get();
|
||||
break;
|
||||
}
|
||||
case File::kindSharedLibrary:
|
||||
if (!file->hasOrdinal())
|
||||
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
if (auto EC = handleSharedLibrary(*file)) {
|
||||
// FIXME: This should be passed to logAllUnhandledErrors but it needs
|
||||
// to be passed a Twine instead of a string.
|
||||
llvm::errs() << "Error in " + file->path() << ": ";
|
||||
logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
_newUndefinesAdded[file] = undefAdded;
|
||||
}
|
||||
}
|
||||
|
||||
// switch all references to undefined or coalesced away atoms
|
||||
// to the new defined atom
|
||||
void Resolver::updateReferences() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Updating references:\n");
|
||||
ScopedTask task(getDefaultDomain(), "updateReferences");
|
||||
for (const OwningAtomPtr<Atom> &atom : _atoms) {
|
||||
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
|
||||
for (const Reference *ref : *defAtom) {
|
||||
// A reference of type kindAssociate shouldn't be updated.
|
||||
// Instead, an atom having such reference will be removed
|
||||
// if the target atom is coalesced away, so that they will
|
||||
// go away as a group.
|
||||
if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
|
||||
ref->kindValue() == lld::Reference::kindAssociate) {
|
||||
if (_symbolTable.isCoalescedAway(atom.get()))
|
||||
_deadAtoms.insert(ref->target());
|
||||
continue;
|
||||
}
|
||||
const Atom *newTarget = _symbolTable.replacement(ref->target());
|
||||
const_cast<Reference *>(ref)->setTarget(newTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For dead code stripping, recursively mark atoms "live"
|
||||
void Resolver::markLive(const Atom *atom) {
|
||||
// Mark the atom is live. If it's already marked live, then stop recursion.
|
||||
auto exists = _liveAtoms.insert(atom);
|
||||
if (!exists.second)
|
||||
return;
|
||||
|
||||
// Mark all atoms it references as live
|
||||
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
|
||||
for (const Reference *ref : *defAtom)
|
||||
markLive(ref->target());
|
||||
for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) {
|
||||
const Atom *target = p.second;
|
||||
markLive(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isBackref(const Reference *ref) {
|
||||
if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
|
||||
return false;
|
||||
return (ref->kindValue() == lld::Reference::kindLayoutAfter);
|
||||
}
|
||||
|
||||
// remove all atoms not actually used
|
||||
void Resolver::deadStripOptimize() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Dead stripping unused atoms:\n");
|
||||
ScopedTask task(getDefaultDomain(), "deadStripOptimize");
|
||||
// only do this optimization with -dead_strip
|
||||
if (!_ctx.deadStrip())
|
||||
return;
|
||||
|
||||
// Some type of references prevent referring atoms to be dead-striped.
|
||||
// Make a reverse map of such references before traversing the graph.
|
||||
// While traversing the list of atoms, mark AbsoluteAtoms as live
|
||||
// in order to avoid reclaim.
|
||||
for (const OwningAtomPtr<Atom> &atom : _atoms) {
|
||||
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
|
||||
for (const Reference *ref : *defAtom)
|
||||
if (isBackref(ref))
|
||||
_reverseRef.insert(std::make_pair(ref->target(), atom.get()));
|
||||
if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom.get()))
|
||||
markLive(absAtom);
|
||||
}
|
||||
|
||||
// By default, shared libraries are built with all globals as dead strip roots
|
||||
if (_ctx.globalsAreDeadStripRoots())
|
||||
for (const OwningAtomPtr<Atom> &atom : _atoms)
|
||||
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
|
||||
if (defAtom->scope() == DefinedAtom::scopeGlobal)
|
||||
_deadStripRoots.insert(defAtom);
|
||||
|
||||
// Or, use list of names that are dead strip roots.
|
||||
for (const StringRef &name : _ctx.deadStripRoots()) {
|
||||
const Atom *symAtom = _symbolTable.findByName(name);
|
||||
assert(symAtom);
|
||||
_deadStripRoots.insert(symAtom);
|
||||
}
|
||||
|
||||
// mark all roots as live, and recursively all atoms they reference
|
||||
for (const Atom *dsrAtom : _deadStripRoots)
|
||||
markLive(dsrAtom);
|
||||
|
||||
// now remove all non-live atoms from _atoms
|
||||
llvm::erase_if(_atoms, [&](OwningAtomPtr<Atom> &a) {
|
||||
return _liveAtoms.count(a.get()) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
// error out if some undefines remain
|
||||
bool Resolver::checkUndefines() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Checking for undefines:\n");
|
||||
|
||||
// build vector of remaining undefined symbols
|
||||
std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
|
||||
if (_ctx.deadStrip()) {
|
||||
// When dead code stripping, we don't care if dead atoms are undefined.
|
||||
llvm::erase_if(undefinedAtoms,
|
||||
[&](const Atom *a) { return _liveAtoms.count(a) == 0; });
|
||||
}
|
||||
|
||||
if (undefinedAtoms.empty())
|
||||
return false;
|
||||
|
||||
// Warn about unresolved symbols.
|
||||
bool foundUndefines = false;
|
||||
for (const UndefinedAtom *undef : undefinedAtoms) {
|
||||
// Skip over a weak symbol.
|
||||
if (undef->canBeNull() != UndefinedAtom::canBeNullNever)
|
||||
continue;
|
||||
|
||||
// If this is a library and undefined symbols are allowed on the
|
||||
// target platform, skip over it.
|
||||
if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines())
|
||||
continue;
|
||||
|
||||
// If the undefine is coalesced away, skip over it.
|
||||
if (_symbolTable.isCoalescedAway(undef))
|
||||
continue;
|
||||
|
||||
// Seems like this symbol is undefined. Warn that.
|
||||
foundUndefines = true;
|
||||
if (_ctx.printRemainingUndefines()) {
|
||||
llvm::errs() << "Undefined symbol: " << undef->file().path() << ": "
|
||||
<< _ctx.demangle(undef->name()) << "\n";
|
||||
}
|
||||
}
|
||||
if (!foundUndefines)
|
||||
return false;
|
||||
if (_ctx.printRemainingUndefines())
|
||||
llvm::errs() << "symbol(s) not found\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove from _atoms all coalesced away atoms.
|
||||
void Resolver::removeCoalescedAwayAtoms() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Removing coalesced away atoms:\n");
|
||||
ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
|
||||
llvm::erase_if(_atoms, [&](OwningAtomPtr<Atom> &a) {
|
||||
return _symbolTable.isCoalescedAway(a.get()) || _deadAtoms.count(a.get());
|
||||
});
|
||||
}
|
||||
|
||||
bool Resolver::resolve() {
|
||||
DEBUG_WITH_TYPE("resolver",
|
||||
llvm::dbgs() << "******** Resolving atom references:\n");
|
||||
if (!resolveUndefines())
|
||||
return false;
|
||||
updateReferences();
|
||||
deadStripOptimize();
|
||||
if (checkUndefines()) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... ");
|
||||
if (!_ctx.allowRemainingUndefines()) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n");
|
||||
return false;
|
||||
}
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n");
|
||||
}
|
||||
removeCoalescedAwayAtoms();
|
||||
_result->addAtoms(_atoms);
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resolver::MergedFile::addAtoms(
|
||||
llvm::MutableArrayRef<OwningAtomPtr<Atom>> all) {
|
||||
ScopedTask task(getDefaultDomain(), "addAtoms");
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
|
||||
|
||||
for (OwningAtomPtr<Atom> &atom : all) {
|
||||
#ifndef NDEBUG
|
||||
if (auto *definedAtom = dyn_cast<DefinedAtom>(atom.get())) {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< llvm::format(" 0x%09lX", definedAtom)
|
||||
<< ", file=#"
|
||||
<< definedAtom->file().ordinal()
|
||||
<< ", atom=#"
|
||||
<< definedAtom->ordinal()
|
||||
<< ", name="
|
||||
<< definedAtom->name()
|
||||
<< ", type="
|
||||
<< definedAtom->contentType()
|
||||
<< "\n");
|
||||
} else {
|
||||
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
|
||||
<< llvm::format(" 0x%09lX", atom.get())
|
||||
<< ", name="
|
||||
<< atom.get()->name()
|
||||
<< "\n");
|
||||
}
|
||||
#endif
|
||||
addAtom(*atom.release());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lld
|
|
@ -1,284 +0,0 @@
|
|||
//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/SymbolTable.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/AbsoluteAtom.h"
|
||||
#include "lld/Core/Atom.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/LinkingContext.h"
|
||||
#include "lld/Core/Resolver.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/UndefinedAtom.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); }
|
||||
|
||||
bool SymbolTable::add(const SharedLibraryAtom &atom) { return addByName(atom); }
|
||||
|
||||
bool SymbolTable::add(const AbsoluteAtom &atom) { return addByName(atom); }
|
||||
|
||||
bool SymbolTable::add(const DefinedAtom &atom) {
|
||||
if (!atom.name().empty() &&
|
||||
atom.scope() != DefinedAtom::scopeTranslationUnit) {
|
||||
// Named atoms cannot be merged by content.
|
||||
assert(atom.merge() != DefinedAtom::mergeByContent);
|
||||
// Track named atoms that are not scoped to file (static).
|
||||
return addByName(atom);
|
||||
}
|
||||
if (atom.merge() == DefinedAtom::mergeByContent) {
|
||||
// Named atoms cannot be merged by content.
|
||||
assert(atom.name().empty());
|
||||
// Currently only read-only constants can be merged.
|
||||
if (atom.permissions() == DefinedAtom::permR__)
|
||||
return addByContent(atom);
|
||||
// TODO: support mergeByContent of data atoms by comparing content & fixups.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum NameCollisionResolution {
|
||||
NCR_First,
|
||||
NCR_Second,
|
||||
NCR_DupDef,
|
||||
NCR_DupUndef,
|
||||
NCR_DupShLib,
|
||||
NCR_Error
|
||||
};
|
||||
|
||||
static NameCollisionResolution cases[4][4] = {
|
||||
//regular absolute undef sharedLib
|
||||
{
|
||||
// first is regular
|
||||
NCR_DupDef, NCR_Error, NCR_First, NCR_First
|
||||
},
|
||||
{
|
||||
// first is absolute
|
||||
NCR_Error, NCR_Error, NCR_First, NCR_First
|
||||
},
|
||||
{
|
||||
// first is undef
|
||||
NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
|
||||
},
|
||||
{
|
||||
// first is sharedLib
|
||||
NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
|
||||
}
|
||||
};
|
||||
|
||||
static NameCollisionResolution collide(Atom::Definition first,
|
||||
Atom::Definition second) {
|
||||
return cases[first][second];
|
||||
}
|
||||
|
||||
enum MergeResolution {
|
||||
MCR_First,
|
||||
MCR_Second,
|
||||
MCR_Largest,
|
||||
MCR_SameSize,
|
||||
MCR_Error
|
||||
};
|
||||
|
||||
static MergeResolution mergeCases[][6] = {
|
||||
// no tentative weak weakAddress sameNameAndSize largest
|
||||
{MCR_Error, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // no
|
||||
{MCR_Second, MCR_Largest, MCR_Second, MCR_Second, MCR_SameSize, MCR_Largest}, // tentative
|
||||
{MCR_Second, MCR_First, MCR_First, MCR_Second, MCR_SameSize, MCR_Largest}, // weak
|
||||
{MCR_Second, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // weakAddress
|
||||
{MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize
|
||||
{MCR_Largest, MCR_Largest, MCR_Largest, MCR_Largest, MCR_SameSize, MCR_Largest}, // largest
|
||||
};
|
||||
|
||||
static MergeResolution mergeSelect(DefinedAtom::Merge first,
|
||||
DefinedAtom::Merge second) {
|
||||
assert(first != DefinedAtom::mergeByContent);
|
||||
assert(second != DefinedAtom::mergeByContent);
|
||||
return mergeCases[first][second];
|
||||
}
|
||||
|
||||
bool SymbolTable::addByName(const Atom &newAtom) {
|
||||
StringRef name = newAtom.name();
|
||||
assert(!name.empty());
|
||||
const Atom *existing = findByName(name);
|
||||
if (existing == nullptr) {
|
||||
// Name is not in symbol table yet, add it associate with this atom.
|
||||
_nameTable[name] = &newAtom;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do nothing if the same object is added more than once.
|
||||
if (existing == &newAtom)
|
||||
return false;
|
||||
|
||||
// Name is already in symbol table and associated with another atom.
|
||||
bool useNew = true;
|
||||
switch (collide(existing->definition(), newAtom.definition())) {
|
||||
case NCR_First:
|
||||
useNew = false;
|
||||
break;
|
||||
case NCR_Second:
|
||||
useNew = true;
|
||||
break;
|
||||
case NCR_DupDef: {
|
||||
const auto *existingDef = cast<DefinedAtom>(existing);
|
||||
const auto *newDef = cast<DefinedAtom>(&newAtom);
|
||||
switch (mergeSelect(existingDef->merge(), newDef->merge())) {
|
||||
case MCR_First:
|
||||
useNew = false;
|
||||
break;
|
||||
case MCR_Second:
|
||||
useNew = true;
|
||||
break;
|
||||
case MCR_Largest: {
|
||||
uint64_t existingSize = existingDef->sectionSize();
|
||||
uint64_t newSize = newDef->sectionSize();
|
||||
useNew = (newSize >= existingSize);
|
||||
break;
|
||||
}
|
||||
case MCR_SameSize: {
|
||||
uint64_t existingSize = existingDef->sectionSize();
|
||||
uint64_t newSize = newDef->sectionSize();
|
||||
if (existingSize == newSize) {
|
||||
useNew = true;
|
||||
break;
|
||||
}
|
||||
llvm::errs() << "Size mismatch: " << existing->name() << " ("
|
||||
<< existingSize << ") " << newAtom.name() << " (" << newSize
|
||||
<< ")\n";
|
||||
LLVM_FALLTHROUGH;
|
||||
}
|
||||
case MCR_Error:
|
||||
llvm::errs() << "Duplicate symbols: " << existing->name() << ":"
|
||||
<< existing->file().path() << " and " << newAtom.name()
|
||||
<< ":" << newAtom.file().path() << "\n";
|
||||
llvm::report_fatal_error("duplicate symbol error");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NCR_DupUndef: {
|
||||
const UndefinedAtom* existingUndef = cast<UndefinedAtom>(existing);
|
||||
const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom);
|
||||
|
||||
bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
|
||||
if (sameCanBeNull)
|
||||
useNew = false;
|
||||
else
|
||||
useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
|
||||
break;
|
||||
}
|
||||
case NCR_DupShLib: {
|
||||
useNew = false;
|
||||
break;
|
||||
}
|
||||
case NCR_Error:
|
||||
llvm::errs() << "SymbolTable: error while merging " << name << "\n";
|
||||
llvm::report_fatal_error("duplicate symbol error");
|
||||
break;
|
||||
}
|
||||
|
||||
if (useNew) {
|
||||
// Update name table to use new atom.
|
||||
_nameTable[name] = &newAtom;
|
||||
// Add existing atom to replacement table.
|
||||
_replacedAtoms[existing] = &newAtom;
|
||||
} else {
|
||||
// New atom is not being used. Add it to replacement table.
|
||||
_replacedAtoms[&newAtom] = existing;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
|
||||
auto content = atom->rawContent();
|
||||
return llvm::hash_combine(atom->size(),
|
||||
atom->contentType(),
|
||||
llvm::hash_combine_range(content.begin(),
|
||||
content.end()));
|
||||
}
|
||||
|
||||
bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
|
||||
const DefinedAtom * const r) {
|
||||
if (l == r)
|
||||
return true;
|
||||
if (l == getEmptyKey() || r == getEmptyKey())
|
||||
return false;
|
||||
if (l == getTombstoneKey() || r == getTombstoneKey())
|
||||
return false;
|
||||
if (l->contentType() != r->contentType())
|
||||
return false;
|
||||
if (l->size() != r->size())
|
||||
return false;
|
||||
if (l->sectionChoice() != r->sectionChoice())
|
||||
return false;
|
||||
if (l->sectionChoice() == DefinedAtom::sectionCustomRequired) {
|
||||
if (!l->customSectionName().equals(r->customSectionName()))
|
||||
return false;
|
||||
}
|
||||
ArrayRef<uint8_t> lc = l->rawContent();
|
||||
ArrayRef<uint8_t> rc = r->rawContent();
|
||||
return memcmp(lc.data(), rc.data(), lc.size()) == 0;
|
||||
}
|
||||
|
||||
bool SymbolTable::addByContent(const DefinedAtom &newAtom) {
|
||||
AtomContentSet::iterator pos = _contentTable.find(&newAtom);
|
||||
if (pos == _contentTable.end()) {
|
||||
_contentTable.insert(&newAtom);
|
||||
return true;
|
||||
}
|
||||
const Atom* existing = *pos;
|
||||
// New atom is not being used. Add it to replacement table.
|
||||
_replacedAtoms[&newAtom] = existing;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Atom *SymbolTable::findByName(StringRef sym) {
|
||||
NameToAtom::iterator pos = _nameTable.find(sym);
|
||||
if (pos == _nameTable.end())
|
||||
return nullptr;
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
const Atom *SymbolTable::replacement(const Atom *atom) {
|
||||
// Find the replacement for a given atom. Atoms in _replacedAtoms
|
||||
// may be chained, so find the last one.
|
||||
for (;;) {
|
||||
AtomToAtom::iterator pos = _replacedAtoms.find(atom);
|
||||
if (pos == _replacedAtoms.end())
|
||||
return atom;
|
||||
atom = pos->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolTable::isCoalescedAway(const Atom *atom) {
|
||||
return _replacedAtoms.count(atom) > 0;
|
||||
}
|
||||
|
||||
std::vector<const UndefinedAtom *> SymbolTable::undefines() {
|
||||
std::vector<const UndefinedAtom *> ret;
|
||||
for (auto it : _nameTable) {
|
||||
const Atom *atom = it.second;
|
||||
assert(atom != nullptr);
|
||||
if (const auto *undef = dyn_cast<const UndefinedAtom>(atom))
|
||||
if (_replacedAtoms.count(undef) == 0)
|
||||
ret.push_back(undef);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace lld
|
|
@ -1,17 +0,0 @@
|
|||
//===- lib/Core/Writer.cpp ------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/Writer.h"
|
||||
|
||||
namespace lld {
|
||||
|
||||
Writer::Writer() = default;
|
||||
|
||||
Writer::~Writer() = default;
|
||||
|
||||
} // end namespace lld
|
|
@ -1,23 +0,0 @@
|
|||
set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
|
||||
tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
|
||||
add_public_tablegen_target(DriverOptionsTableGen)
|
||||
|
||||
add_lld_library(lldDriver
|
||||
DarwinLdDriver.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/Driver
|
||||
|
||||
LINK_COMPONENTS
|
||||
Option
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
lldCommon
|
||||
lldCore
|
||||
lldMachOOld
|
||||
lldReaderWriter
|
||||
lldYAML
|
||||
)
|
||||
|
||||
add_dependencies(lldDriver DriverOptionsTableGen)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,250 +0,0 @@
|
|||
include "llvm/Option/OptParser.td"
|
||||
|
||||
|
||||
// output kinds
|
||||
def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">;
|
||||
def relocatable : Flag<["-"], "r">,
|
||||
HelpText<"Create relocatable object file">, Group<grp_kind>;
|
||||
def static : Flag<["-"], "static">,
|
||||
HelpText<"Create static executable">, Group<grp_kind>;
|
||||
def dynamic : Flag<["-"], "dynamic">,
|
||||
HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
|
||||
def dylib : Flag<["-"], "dylib">,
|
||||
HelpText<"Create dynamic library">, Group<grp_kind>;
|
||||
def bundle : Flag<["-"], "bundle">,
|
||||
HelpText<"Create dynamic bundle">, Group<grp_kind>;
|
||||
def execute : Flag<["-"], "execute">,
|
||||
HelpText<"Create main executable (default)">, Group<grp_kind>;
|
||||
def preload : Flag<["-"], "preload">,
|
||||
HelpText<"Create binary for use with embedded systems">, Group<grp_kind>;
|
||||
|
||||
// optimizations
|
||||
def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
|
||||
def dead_strip : Flag<["-"], "dead_strip">,
|
||||
HelpText<"Remove unreference code and data">, Group<grp_opts>;
|
||||
def macosx_version_min : Separate<["-"], "macosx_version_min">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"Minimum Mac OS X version">, Group<grp_opts>;
|
||||
def ios_version_min : Separate<["-"], "ios_version_min">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"Minimum iOS version">, Group<grp_opts>;
|
||||
def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
|
||||
Alias<ios_version_min>;
|
||||
def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"Minimum iOS simulator version">, Group<grp_opts>;
|
||||
def sdk_version : Separate<["-"], "sdk_version">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"SDK version">, Group<grp_opts>;
|
||||
def source_version : Separate<["-"], "source_version">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"Source version">, Group<grp_opts>;
|
||||
def version_load_command : Flag<["-"], "version_load_command">,
|
||||
HelpText<"Force generation of a version load command">, Group<grp_opts>;
|
||||
def no_version_load_command : Flag<["-"], "no_version_load_command">,
|
||||
HelpText<"Disable generation of a version load command">, Group<grp_opts>;
|
||||
def function_starts : Flag<["-"], "function_starts">,
|
||||
HelpText<"Force generation of a function starts load command">,
|
||||
Group<grp_opts>;
|
||||
def no_function_starts : Flag<["-"], "no_function_starts">,
|
||||
HelpText<"Disable generation of a function starts load command">,
|
||||
Group<grp_opts>;
|
||||
def data_in_code_info : Flag<["-"], "data_in_code_info">,
|
||||
HelpText<"Force generation of a data in code load command">,
|
||||
Group<grp_opts>;
|
||||
def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">,
|
||||
HelpText<"Disable generation of a data in code load command">,
|
||||
Group<grp_opts>;
|
||||
def mllvm : Separate<["-"], "mllvm">,
|
||||
MetaVarName<"<option>">,
|
||||
HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>;
|
||||
def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
|
||||
MetaVarName<"<file-path>">,
|
||||
HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
|
||||
def exported_symbol : Separate<["-"], "exported_symbol">,
|
||||
MetaVarName<"<symbol>">,
|
||||
HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
|
||||
def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
|
||||
MetaVarName<"<file-path>">,
|
||||
HelpText<"Lists symbols that should not be exported">, Group<grp_opts>;
|
||||
def unexported_symbol : Separate<["-"], "unexported_symbol">,
|
||||
MetaVarName<"<symbol>">,
|
||||
HelpText<"A symbol which should not be exported">, Group<grp_opts>;
|
||||
def keep_private_externs : Flag<["-"], "keep_private_externs">,
|
||||
HelpText<"Private extern (hidden) symbols should not be transformed "
|
||||
"into local symbols">, Group<grp_opts>;
|
||||
def order_file : Separate<["-"], "order_file">,
|
||||
MetaVarName<"<file-path>">,
|
||||
HelpText<"re-order and move specified symbols to start of their section">,
|
||||
Group<grp_opts>;
|
||||
def flat_namespace : Flag<["-"], "flat_namespace">,
|
||||
HelpText<"Resolves symbols in any (transitively) linked dynamic libraries. "
|
||||
"Source libraries are not recorded: dyld will re-search all "
|
||||
"images at runtime and use the first definition found.">,
|
||||
Group<grp_opts>;
|
||||
def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
|
||||
HelpText<"Resolves symbols in listed libraries only. Source libraries are "
|
||||
"recorded in the symbol table.">,
|
||||
Group<grp_opts>;
|
||||
def undefined : Separate<["-"], "undefined">,
|
||||
MetaVarName<"<undefined>">,
|
||||
HelpText<"Determines how undefined symbols are handled.">,
|
||||
Group<grp_opts>;
|
||||
def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
|
||||
HelpText<"Disables the optimisation which merges Objective-C categories "
|
||||
"on a class in to the class itself.">,
|
||||
Group<grp_opts>;
|
||||
|
||||
// main executable options
|
||||
def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">;
|
||||
def entry : Separate<["-"], "e">,
|
||||
MetaVarName<"<entry-name>">,
|
||||
HelpText<"entry symbol name">,Group<grp_main>;
|
||||
def pie : Flag<["-"], "pie">,
|
||||
HelpText<"Create Position Independent Executable (for ASLR)">,
|
||||
Group<grp_main>;
|
||||
def no_pie : Flag<["-"], "no_pie">,
|
||||
HelpText<"Do not create Position Independent Executable">,
|
||||
Group<grp_main>;
|
||||
def stack_size : Separate<["-"], "stack_size">,
|
||||
HelpText<"Specifies the maximum stack size for the main thread in a program. "
|
||||
"Must be a page-size multiple. (default=8Mb)">,
|
||||
Group<grp_main>;
|
||||
def export_dynamic : Flag<["-"], "export_dynamic">,
|
||||
HelpText<"Preserves all global symbols in main executables during LTO">,
|
||||
Group<grp_main>;
|
||||
|
||||
// dylib executable options
|
||||
def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
|
||||
def install_name : Separate<["-"], "install_name">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"The dylib's install name">, Group<grp_dylib>;
|
||||
def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
|
||||
HelpText<"Marks the dylib as having no side effects during initialization">,
|
||||
Group<grp_dylib>;
|
||||
def compatibility_version : Separate<["-"], "compatibility_version">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
|
||||
def current_version : Separate<["-"], "current_version">,
|
||||
MetaVarName<"<version>">,
|
||||
HelpText<"The dylib's current version">, Group<grp_dylib>;
|
||||
|
||||
// dylib executable options - compatibility aliases
|
||||
def dylib_install_name : Separate<["-"], "dylib_install_name">,
|
||||
Alias<install_name>;
|
||||
def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
|
||||
MetaVarName<"<version>">, Alias<compatibility_version>;
|
||||
def dylib_current_version : Separate<["-"], "dylib_current_version">,
|
||||
MetaVarName<"<version>">, Alias<current_version>;
|
||||
|
||||
// bundle executable options
|
||||
def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
|
||||
def bundle_loader : Separate<["-"], "bundle_loader">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"The executable that will be loading this Mach-O bundle">,
|
||||
Group<grp_bundle>;
|
||||
|
||||
// library options
|
||||
def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
|
||||
def L : JoinedOrSeparate<["-"], "L">,
|
||||
MetaVarName<"<dir>">,
|
||||
HelpText<"Add directory to library search path">, Group<grp_libs>;
|
||||
def F : JoinedOrSeparate<["-"], "F">,
|
||||
MetaVarName<"<dir>">,
|
||||
HelpText<"Add directory to framework search path">, Group<grp_libs>;
|
||||
def Z : Flag<["-"], "Z">,
|
||||
HelpText<"Do not search standard directories for libraries or frameworks">;
|
||||
def all_load : Flag<["-"], "all_load">,
|
||||
HelpText<"Forces all members of all static libraries to be loaded">,
|
||||
Group<grp_libs>;
|
||||
def force_load : Separate<["-"], "force_load">,
|
||||
MetaVarName<"<library-path>">,
|
||||
HelpText<"Forces all members of specified static libraries to be loaded">,
|
||||
Group<grp_libs>;
|
||||
def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">,
|
||||
HelpText<"Add path to SDK to all absolute library search paths">,
|
||||
Group<grp_libs>;
|
||||
|
||||
// Input options
|
||||
def l : Joined<["-"], "l">,
|
||||
MetaVarName<"<libname>">,
|
||||
HelpText<"Base name of library searched for in -L directories">;
|
||||
def upward_l : Joined<["-"], "upward-l">,
|
||||
MetaVarName<"<libname>">,
|
||||
HelpText<"Base name of upward library searched for in -L directories">;
|
||||
def framework : Separate<["-"], "framework">,
|
||||
MetaVarName<"<name>">,
|
||||
HelpText<"Base name of framework searched for in -F directories">;
|
||||
def upward_framework : Separate<["-"], "upward_framework">,
|
||||
MetaVarName<"<name>">,
|
||||
HelpText<"Base name of upward framework searched for in -F directories">;
|
||||
def upward_library : Separate<["-"], "upward_library">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"path to upward dylib to link with">;
|
||||
def filelist : Separate<["-"], "filelist">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"file containing paths to input files">;
|
||||
|
||||
|
||||
// test case options
|
||||
def print_atoms : Flag<["-"], "print_atoms">,
|
||||
HelpText<"Emit output as yaml atoms">;
|
||||
def test_file_usage : Flag<["-"], "test_file_usage">,
|
||||
HelpText<"Only files specified by -file_exists are considered to exist. "
|
||||
"Print which files would be used">;
|
||||
def path_exists : Separate<["-"], "path_exists">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Used with -test_file_usage to declare a path">;
|
||||
|
||||
|
||||
// general options
|
||||
def output : Separate<["-"], "o">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Output file path">;
|
||||
def arch : Separate<["-"], "arch">,
|
||||
MetaVarName<"<arch-name>">,
|
||||
HelpText<"Architecture to link">;
|
||||
def sectalign : MultiArg<["-"], "sectalign", 3>,
|
||||
MetaVarName<"<segname> <sectname> <alignment>">,
|
||||
HelpText<"Alignment for segment/section">;
|
||||
def sectcreate : MultiArg<["-"], "sectcreate", 3>,
|
||||
MetaVarName<"<segname> <sectname> <file>">,
|
||||
HelpText<"Create section <segname>/<sectname> from contents of <file>">;
|
||||
def image_base : Separate<["-"], "image_base">;
|
||||
def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>;
|
||||
def demangle : Flag<["-"], "demangle">,
|
||||
HelpText<"Demangles symbol names in errors and warnings">;
|
||||
def dependency_info : Separate<["-"], "dependency_info">,
|
||||
MetaVarName<"<file>">,
|
||||
HelpText<"Write binary list of files used during link">;
|
||||
def S : Flag<["-"], "S">,
|
||||
HelpText<"Remove debug information (STABS or DWARF) from the output file">;
|
||||
def rpath : Separate<["-"], "rpath">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Add path to the runpath search path list for image being created">;
|
||||
|
||||
def t : Flag<["-"], "t">,
|
||||
HelpText<"Print the names of the input files as ld processes them">;
|
||||
def v : Flag<["-"], "v">,
|
||||
HelpText<"Print linker information">;
|
||||
def error_limit : Separate<["-", "--"], "error-limit">,
|
||||
MetaVarName<"<number>">,
|
||||
HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
|
||||
// Ignored options
|
||||
def lto_library : Separate<["-"], "lto_library">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Ignored for compatibility with other linkers">;
|
||||
|
||||
// Obsolete options
|
||||
def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">;
|
||||
def single_module : Flag<["-"], "single_module">,
|
||||
HelpText<"Default for dylibs">, Group<grp_obsolete>;
|
||||
def multi_module : Flag<["-"], "multi_module">,
|
||||
HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>;
|
||||
def objc_gc_compaction : Flag<["-"], "objc_gc_compaction">,
|
||||
HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
|
||||
def objc_gc : Flag<["-"], "objc_gc">,
|
||||
HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
|
||||
def objc_gc_only : Flag<["-"], "objc_gc_only">,
|
||||
HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
|
|
@ -1,20 +0,0 @@
|
|||
add_subdirectory(MachO)
|
||||
add_subdirectory(YAML)
|
||||
|
||||
if (MSVC)
|
||||
add_definitions(-wd4062) # Suppress 'warning C4062: Enumerator has no associated handler in a switch statement.'
|
||||
endif()
|
||||
|
||||
add_lld_library(lldReaderWriter
|
||||
FileArchive.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/ReaderWriter
|
||||
|
||||
LINK_COMPONENTS
|
||||
Object
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
lldCore
|
||||
)
|
|
@ -1,227 +0,0 @@
|
|||
//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/ArchiveLibraryFile.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reader.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/Error.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using llvm::object::Archive;
|
||||
using llvm::file_magic;
|
||||
using llvm::identify_magic;
|
||||
|
||||
namespace lld {
|
||||
|
||||
namespace {
|
||||
|
||||
/// The FileArchive class represents an Archive Library file
|
||||
class FileArchive : public lld::ArchiveLibraryFile {
|
||||
public:
|
||||
FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®,
|
||||
StringRef path, bool logLoading)
|
||||
: ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
|
||||
_registry(reg), _logLoading(logLoading) {}
|
||||
|
||||
/// Check if any member of the archive contains an Atom with the
|
||||
/// specified name and return the File object for that member, or nullptr.
|
||||
File *find(StringRef name) override {
|
||||
auto member = _symbolMemberMap.find(name);
|
||||
if (member == _symbolMemberMap.end())
|
||||
return nullptr;
|
||||
Archive::Child c = member->second;
|
||||
|
||||
// Don't return a member already returned
|
||||
Expected<StringRef> buf = c.getBuffer();
|
||||
if (!buf) {
|
||||
// TODO: Actually report errors helpfully.
|
||||
consumeError(buf.takeError());
|
||||
return nullptr;
|
||||
}
|
||||
const char *memberStart = buf->data();
|
||||
if (_membersInstantiated.count(memberStart))
|
||||
return nullptr;
|
||||
_membersInstantiated.insert(memberStart);
|
||||
|
||||
std::unique_ptr<File> result;
|
||||
if (instantiateMember(c, result))
|
||||
return nullptr;
|
||||
|
||||
File *file = result.get();
|
||||
_filesReturned.push_back(std::move(result));
|
||||
|
||||
// Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive
|
||||
return file;
|
||||
}
|
||||
|
||||
/// parse each member
|
||||
std::error_code
|
||||
parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
|
||||
if (std::error_code ec = parse())
|
||||
return ec;
|
||||
llvm::Error err = llvm::Error::success();
|
||||
for (auto mf = _archive->child_begin(err), me = _archive->child_end();
|
||||
mf != me; ++mf) {
|
||||
std::unique_ptr<File> file;
|
||||
if (std::error_code ec = instantiateMember(*mf, file)) {
|
||||
// err is Success (or we wouldn't be in the loop body) but we can't
|
||||
// return without testing or consuming it.
|
||||
consumeError(std::move(err));
|
||||
return ec;
|
||||
}
|
||||
result.push_back(std::move(file));
|
||||
}
|
||||
if (err)
|
||||
return errorToErrorCode(std::move(err));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
const AtomRange<DefinedAtom> defined() const override {
|
||||
return _noDefinedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<UndefinedAtom> undefined() const override {
|
||||
return _noUndefinedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
|
||||
return _noSharedLibraryAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<AbsoluteAtom> absolute() const override {
|
||||
return _noAbsoluteAtoms;
|
||||
}
|
||||
|
||||
void clearAtoms() override {
|
||||
_noDefinedAtoms.clear();
|
||||
_noUndefinedAtoms.clear();
|
||||
_noSharedLibraryAtoms.clear();
|
||||
_noAbsoluteAtoms.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::error_code doParse() override {
|
||||
// Make Archive object which will be owned by FileArchive object.
|
||||
llvm::Error Err = llvm::Error::success();
|
||||
_archive.reset(new Archive(_mb->getMemBufferRef(), Err));
|
||||
if (Err)
|
||||
return errorToErrorCode(std::move(Err));
|
||||
std::error_code ec;
|
||||
if ((ec = buildTableOfContents()))
|
||||
return ec;
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
private:
|
||||
std::error_code instantiateMember(Archive::Child member,
|
||||
std::unique_ptr<File> &result) const {
|
||||
Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
|
||||
if (!mbOrErr)
|
||||
return errorToErrorCode(mbOrErr.takeError());
|
||||
llvm::MemoryBufferRef mb = mbOrErr.get();
|
||||
std::string memberPath = (_archive->getFileName() + "("
|
||||
+ mb.getBufferIdentifier() + ")").str();
|
||||
|
||||
if (_logLoading)
|
||||
llvm::errs() << memberPath << "\n";
|
||||
|
||||
std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
|
||||
mb.getBuffer(), mb.getBufferIdentifier(), false));
|
||||
|
||||
ErrorOr<std::unique_ptr<File>> fileOrErr =
|
||||
_registry.loadFile(std::move(memberMB));
|
||||
if (std::error_code ec = fileOrErr.getError())
|
||||
return ec;
|
||||
result = std::move(fileOrErr.get());
|
||||
if (std::error_code ec = result->parse())
|
||||
return ec;
|
||||
result->setArchivePath(_archive->getFileName());
|
||||
|
||||
// The memory buffer is co-owned by the archive file and the children,
|
||||
// so that the bufffer is deallocated when all the members are destructed.
|
||||
result->setSharedMemoryBuffer(_mb);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code buildTableOfContents() {
|
||||
DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
|
||||
<< "Table of contents for archive '"
|
||||
<< _archive->getFileName() << "':\n");
|
||||
for (const Archive::Symbol &sym : _archive->symbols()) {
|
||||
StringRef name = sym.getName();
|
||||
Expected<Archive::Child> memberOrErr = sym.getMember();
|
||||
if (!memberOrErr)
|
||||
return errorToErrorCode(memberOrErr.takeError());
|
||||
Archive::Child member = memberOrErr.get();
|
||||
DEBUG_WITH_TYPE("FileArchive",
|
||||
llvm::dbgs()
|
||||
<< llvm::format("0x%08llX ",
|
||||
member.getBuffer()->data())
|
||||
<< "'" << name << "'\n");
|
||||
_symbolMemberMap.insert(std::make_pair(name, member));
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
typedef std::unordered_map<StringRef, Archive::Child> MemberMap;
|
||||
typedef std::set<const char *> InstantiatedSet;
|
||||
|
||||
std::shared_ptr<MemoryBuffer> _mb;
|
||||
const Registry &_registry;
|
||||
std::unique_ptr<Archive> _archive;
|
||||
MemberMap _symbolMemberMap;
|
||||
InstantiatedSet _membersInstantiated;
|
||||
bool _logLoading;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
|
||||
std::vector<std::unique_ptr<File>> _filesReturned;
|
||||
};
|
||||
|
||||
class ArchiveReader : public Reader {
|
||||
public:
|
||||
ArchiveReader(bool logLoading) : _logLoading(logLoading) {}
|
||||
|
||||
bool canParse(file_magic magic, MemoryBufferRef) const override {
|
||||
return magic == file_magic::archive;
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®) const override {
|
||||
StringRef path = mb->getBufferIdentifier();
|
||||
std::unique_ptr<File> ret =
|
||||
std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
bool _logLoading;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void Registry::addSupportArchives(bool logLoading) {
|
||||
add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
|
||||
}
|
||||
|
||||
} // namespace lld
|
|
@ -1,171 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "Atoms.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
ArchHandler::ArchHandler() {
|
||||
}
|
||||
|
||||
ArchHandler::~ArchHandler() {
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create(
|
||||
MachOLinkingContext::Arch arch) {
|
||||
switch (arch) {
|
||||
case MachOLinkingContext::arch_x86_64:
|
||||
return create_x86_64();
|
||||
case MachOLinkingContext::arch_x86:
|
||||
return create_x86();
|
||||
case MachOLinkingContext::arch_armv6:
|
||||
case MachOLinkingContext::arch_armv7:
|
||||
case MachOLinkingContext::arch_armv7s:
|
||||
return create_arm();
|
||||
case MachOLinkingContext::arch_arm64:
|
||||
return create_arm64();
|
||||
default:
|
||||
llvm_unreachable("Unknown arch");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ArchHandler::isLazyPointer(const Reference &ref) {
|
||||
// A lazy bind entry is needed for a lazy pointer.
|
||||
const StubInfo &info = stubInfo();
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
if (ref.kindArch() != info.lazyPointerReferenceToFinal.arch)
|
||||
return false;
|
||||
return (ref.kindValue() == info.lazyPointerReferenceToFinal.kind);
|
||||
}
|
||||
|
||||
|
||||
ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) {
|
||||
assert((reloc.type & 0xFFF0) == 0);
|
||||
uint16_t result = reloc.type;
|
||||
if (reloc.scattered)
|
||||
result |= rScattered;
|
||||
if (reloc.pcRel)
|
||||
result |= rPcRel;
|
||||
if (reloc.isExtern)
|
||||
result |= rExtern;
|
||||
switch(reloc.length) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
result |= rLength2;
|
||||
break;
|
||||
case 2:
|
||||
result |= rLength4;
|
||||
break;
|
||||
case 3:
|
||||
result |= rLength8;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("bad r_length");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
normalized::Relocation
|
||||
ArchHandler::relocFromPattern(ArchHandler::RelocPattern pattern) {
|
||||
normalized::Relocation result;
|
||||
result.offset = 0;
|
||||
result.scattered = (pattern & rScattered);
|
||||
result.type = (RelocationInfoType)(pattern & 0xF);
|
||||
result.pcRel = (pattern & rPcRel);
|
||||
result.isExtern = (pattern & rExtern);
|
||||
result.value = 0;
|
||||
result.symbol = 0;
|
||||
switch (pattern & 0x300) {
|
||||
case rLength1:
|
||||
result.length = 0;
|
||||
break;
|
||||
case rLength2:
|
||||
result.length = 1;
|
||||
break;
|
||||
case rLength4:
|
||||
result.length = 2;
|
||||
break;
|
||||
case rLength8:
|
||||
result.length = 3;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ArchHandler::appendReloc(normalized::Relocations &relocs, uint32_t offset,
|
||||
uint32_t symbol, uint32_t value,
|
||||
RelocPattern pattern) {
|
||||
normalized::Relocation reloc = relocFromPattern(pattern);
|
||||
reloc.offset = offset;
|
||||
reloc.symbol = symbol;
|
||||
reloc.value = value;
|
||||
relocs.push_back(reloc);
|
||||
}
|
||||
|
||||
|
||||
int16_t ArchHandler::readS16(const uint8_t *addr, bool isBig) {
|
||||
return read16(addr, isBig);
|
||||
}
|
||||
|
||||
int32_t ArchHandler::readS32(const uint8_t *addr, bool isBig) {
|
||||
return read32(addr, isBig);
|
||||
}
|
||||
|
||||
uint32_t ArchHandler::readU32(const uint8_t *addr, bool isBig) {
|
||||
return read32(addr, isBig);
|
||||
}
|
||||
|
||||
int64_t ArchHandler::readS64(const uint8_t *addr, bool isBig) {
|
||||
return read64(addr, isBig);
|
||||
}
|
||||
|
||||
bool ArchHandler::isDwarfCIE(bool isBig, const DefinedAtom *atom) {
|
||||
assert(atom->contentType() == DefinedAtom::typeCFI);
|
||||
if (atom->rawContent().size() < sizeof(uint32_t))
|
||||
return false;
|
||||
uint32_t size = read32(atom->rawContent().data(), isBig);
|
||||
|
||||
uint32_t idOffset = sizeof(uint32_t);
|
||||
if (size == 0xffffffffU)
|
||||
idOffset += sizeof(uint64_t);
|
||||
|
||||
return read32(atom->rawContent().data() + idOffset, isBig) == 0;
|
||||
}
|
||||
|
||||
const Atom *ArchHandler::fdeTargetFunction(const DefinedAtom *fde) {
|
||||
for (auto ref : *fde) {
|
||||
if (ref->kindNamespace() == Reference::KindNamespace::mach_o &&
|
||||
ref->kindValue() == unwindRefToFunctionKind()) {
|
||||
assert(ref->kindArch() == kindArch() && "unexpected Reference arch");
|
||||
return ref->target();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler.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_ARCH_HANDLER_H
|
||||
#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
|
||||
|
||||
#include "Atoms.h"
|
||||
#include "File.h"
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
///
|
||||
/// The ArchHandler class handles all architecture specific aspects of
|
||||
/// mach-o linking.
|
||||
///
|
||||
class ArchHandler {
|
||||
public:
|
||||
virtual ~ArchHandler();
|
||||
|
||||
/// There is no public interface to subclasses of ArchHandler, so this
|
||||
/// is the only way to instantiate an ArchHandler.
|
||||
static std::unique_ptr<ArchHandler> create(MachOLinkingContext::Arch arch);
|
||||
|
||||
/// Get (arch specific) kind strings used by Registry.
|
||||
virtual const Registry::KindStrings *kindStrings() = 0;
|
||||
|
||||
/// Convert mach-o Arch to Reference::KindArch.
|
||||
virtual Reference::KindArch kindArch() = 0;
|
||||
|
||||
/// Used by StubPass to update References to shared library functions
|
||||
/// to be references to a stub.
|
||||
virtual bool isCallSite(const Reference &) = 0;
|
||||
|
||||
/// Used by GOTPass to locate GOT References
|
||||
virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Used by TLVPass to locate TLV References.
|
||||
virtual bool isTLVAccess(const Reference &) const { return false; }
|
||||
|
||||
/// Used by the TLVPass to update TLV References.
|
||||
virtual void updateReferenceToTLV(const Reference *) {}
|
||||
|
||||
/// Used by ShimPass to insert shims in branches that switch mode.
|
||||
virtual bool isNonCallBranch(const Reference &) = 0;
|
||||
|
||||
/// Used by GOTPass to update GOT References
|
||||
virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {}
|
||||
|
||||
/// Does this architecture make use of __unwind_info sections for exception
|
||||
/// handling? If so, it will need a separate pass to create them.
|
||||
virtual bool needsCompactUnwind() = 0;
|
||||
|
||||
/// Returns the kind of reference to use to synthesize a 32-bit image-offset
|
||||
/// value, used in the __unwind_info section.
|
||||
virtual Reference::KindValue imageOffsetKind() = 0;
|
||||
|
||||
/// Returns the kind of reference to use to synthesize a 32-bit image-offset
|
||||
/// indirect value. Used for personality functions in the __unwind_info
|
||||
/// section.
|
||||
virtual Reference::KindValue imageOffsetKindIndirect() = 0;
|
||||
|
||||
/// Architecture specific compact unwind type that signals __eh_frame should
|
||||
/// actually be used.
|
||||
virtual uint32_t dwarfCompactUnwindType() = 0;
|
||||
|
||||
/// Reference from an __eh_frame CIE atom to its personality function it's
|
||||
/// describing. Usually pointer-sized and PC-relative, but differs in whether
|
||||
/// it needs to be in relocatable objects.
|
||||
virtual Reference::KindValue unwindRefToPersonalityFunctionKind() = 0;
|
||||
|
||||
/// Reference from an __eh_frame FDE to the CIE it's based on.
|
||||
virtual Reference::KindValue unwindRefToCIEKind() = 0;
|
||||
|
||||
/// Reference from an __eh_frame FDE atom to the function it's
|
||||
/// describing. Usually pointer-sized and PC-relative, but differs in whether
|
||||
/// it needs to be in relocatable objects.
|
||||
virtual Reference::KindValue unwindRefToFunctionKind() = 0;
|
||||
|
||||
/// Reference from an __unwind_info entry of dwarfCompactUnwindType to the
|
||||
/// required __eh_frame entry. On current architectures, the low 24 bits
|
||||
/// represent the offset of the function's FDE entry from the start of
|
||||
/// __eh_frame.
|
||||
virtual Reference::KindValue unwindRefToEhFrameKind() = 0;
|
||||
|
||||
/// Returns a pointer sized reference kind. On 64-bit targets this will
|
||||
/// likely be something like pointer64, and pointer32 on 32-bit targets.
|
||||
virtual Reference::KindValue pointerKind() = 0;
|
||||
|
||||
virtual const Atom *fdeTargetFunction(const DefinedAtom *fde);
|
||||
|
||||
/// Used by normalizedFromAtoms() to know where to generated rebasing and
|
||||
/// binding info in final executables.
|
||||
virtual bool isPointer(const Reference &) = 0;
|
||||
|
||||
/// Used by normalizedFromAtoms() to know where to generated lazy binding
|
||||
/// info in final executables.
|
||||
virtual bool isLazyPointer(const Reference &);
|
||||
|
||||
/// Reference from an __stub_helper entry to the required offset of the
|
||||
/// lazy bind commands.
|
||||
virtual Reference::KindValue lazyImmediateLocationKind() = 0;
|
||||
|
||||
/// Returns true if the specified relocation is paired to the next relocation.
|
||||
virtual bool isPairedReloc(const normalized::Relocation &) = 0;
|
||||
|
||||
/// Prototype for a helper function. Given a sectionIndex and address,
|
||||
/// finds the atom and offset with that atom of that address.
|
||||
typedef std::function<llvm::Error (uint32_t sectionIndex, uint64_t addr,
|
||||
const lld::Atom **, Reference::Addend *)>
|
||||
FindAtomBySectionAndAddress;
|
||||
|
||||
/// Prototype for a helper function. Given a symbolIndex, finds the atom
|
||||
/// representing that symbol.
|
||||
typedef std::function<llvm::Error (uint32_t symbolIndex,
|
||||
const lld::Atom **)> FindAtomBySymbolIndex;
|
||||
|
||||
/// Analyzes a relocation from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual llvm::Error
|
||||
getReferenceInfo(const normalized::Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool isBigEndian,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) = 0;
|
||||
|
||||
/// Analyzes a pair of relocations from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual llvm::Error
|
||||
getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool isBig, bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) = 0;
|
||||
|
||||
/// Prototype for a helper function. Given an atom, finds the symbol table
|
||||
/// index for it in the output file.
|
||||
typedef std::function<uint32_t (const Atom &atom)> FindSymbolIndexForAtom;
|
||||
|
||||
/// Prototype for a helper function. Given an atom, finds the index
|
||||
/// of the section that will contain the atom.
|
||||
typedef std::function<uint32_t (const Atom &atom)> FindSectionIndexForAtom;
|
||||
|
||||
/// Prototype for a helper function. Given an atom, finds the address
|
||||
/// assigned to it in the output file.
|
||||
typedef std::function<uint64_t (const Atom &atom)> FindAddressForAtom;
|
||||
|
||||
/// Some architectures require local symbols on anonymous atoms.
|
||||
virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Copy raw content then apply all fixup References on an Atom.
|
||||
virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable,
|
||||
FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress,
|
||||
uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) = 0;
|
||||
|
||||
/// Used in -r mode to convert a Reference to a mach-o relocation.
|
||||
virtual void appendSectionRelocations(const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom,
|
||||
FindSectionIndexForAtom,
|
||||
FindAddressForAtom,
|
||||
normalized::Relocations&) = 0;
|
||||
|
||||
/// Add arch-specific References.
|
||||
virtual void addAdditionalReferences(MachODefinedAtom &atom) { }
|
||||
|
||||
// Add Reference for data-in-code marker.
|
||||
virtual void addDataInCodeReference(MachODefinedAtom &atom, uint32_t atomOff,
|
||||
uint16_t length, uint16_t kind) { }
|
||||
|
||||
/// Returns true if the specificed Reference value marks the start or end
|
||||
/// of a data-in-code range in an atom.
|
||||
virtual bool isDataInCodeTransition(Reference::KindValue refKind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns the Reference value for a Reference that marks that start of
|
||||
/// a data-in-code range.
|
||||
virtual Reference::KindValue dataInCodeTransitionStart(
|
||||
const MachODefinedAtom &atom) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns the Reference value for a Reference that marks that end of
|
||||
/// a data-in-code range.
|
||||
virtual Reference::KindValue dataInCodeTransitionEnd(
|
||||
const MachODefinedAtom &atom) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Only relevant for 32-bit arm archs.
|
||||
virtual bool isThumbFunction(const DefinedAtom &atom) { return false; }
|
||||
|
||||
/// Only relevant for 32-bit arm archs.
|
||||
virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
|
||||
const DefinedAtom &) {
|
||||
llvm_unreachable("shims only support on arm");
|
||||
}
|
||||
|
||||
/// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE).
|
||||
static bool isDwarfCIE(bool isBig, const DefinedAtom *atom);
|
||||
|
||||
struct ReferenceInfo {
|
||||
Reference::KindArch arch;
|
||||
uint16_t kind;
|
||||
uint32_t offset;
|
||||
int32_t addend;
|
||||
};
|
||||
|
||||
struct OptionalRefInfo {
|
||||
bool used;
|
||||
uint16_t kind;
|
||||
uint32_t offset;
|
||||
int32_t addend;
|
||||
};
|
||||
|
||||
/// Table of architecture specific information for creating stubs.
|
||||
struct StubInfo {
|
||||
const char* binderSymbolName;
|
||||
ReferenceInfo lazyPointerReferenceToHelper;
|
||||
ReferenceInfo lazyPointerReferenceToFinal;
|
||||
ReferenceInfo nonLazyPointerReferenceToBinder;
|
||||
uint8_t codeAlignment;
|
||||
|
||||
uint32_t stubSize;
|
||||
uint8_t stubBytes[16];
|
||||
ReferenceInfo stubReferenceToLP;
|
||||
OptionalRefInfo optStubReferenceToLP;
|
||||
|
||||
uint32_t stubHelperSize;
|
||||
uint8_t stubHelperBytes[16];
|
||||
ReferenceInfo stubHelperReferenceToImm;
|
||||
ReferenceInfo stubHelperReferenceToHelperCommon;
|
||||
|
||||
DefinedAtom::ContentType stubHelperImageCacheContentType;
|
||||
|
||||
uint32_t stubHelperCommonSize;
|
||||
uint8_t stubHelperCommonAlignment;
|
||||
uint8_t stubHelperCommonBytes[36];
|
||||
ReferenceInfo stubHelperCommonReferenceToCache;
|
||||
OptionalRefInfo optStubHelperCommonReferenceToCache;
|
||||
ReferenceInfo stubHelperCommonReferenceToBinder;
|
||||
OptionalRefInfo optStubHelperCommonReferenceToBinder;
|
||||
};
|
||||
|
||||
virtual const StubInfo &stubInfo() = 0;
|
||||
|
||||
protected:
|
||||
ArchHandler();
|
||||
|
||||
static std::unique_ptr<mach_o::ArchHandler> create_x86_64();
|
||||
static std::unique_ptr<mach_o::ArchHandler> create_x86();
|
||||
static std::unique_ptr<mach_o::ArchHandler> create_arm();
|
||||
static std::unique_ptr<mach_o::ArchHandler> create_arm64();
|
||||
|
||||
// Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
|
||||
typedef uint16_t RelocPattern;
|
||||
enum {
|
||||
rScattered = 0x8000,
|
||||
rPcRel = 0x4000,
|
||||
rExtern = 0x2000,
|
||||
rLength1 = 0x0000,
|
||||
rLength2 = 0x0100,
|
||||
rLength4 = 0x0200,
|
||||
rLength8 = 0x0300,
|
||||
rLenArmLo = rLength1,
|
||||
rLenArmHi = rLength2,
|
||||
rLenThmbLo = rLength4,
|
||||
rLenThmbHi = rLength8
|
||||
};
|
||||
/// Extract RelocPattern from normalized mach-o relocation.
|
||||
static RelocPattern relocPattern(const normalized::Relocation &reloc);
|
||||
/// Create normalized Relocation initialized from pattern.
|
||||
static normalized::Relocation relocFromPattern(RelocPattern pattern);
|
||||
/// One liner to add a relocation.
|
||||
static void appendReloc(normalized::Relocations &relocs, uint32_t offset,
|
||||
uint32_t symbol, uint32_t value,
|
||||
RelocPattern pattern);
|
||||
|
||||
|
||||
static int16_t readS16(const uint8_t *addr, bool isBig);
|
||||
static int32_t readS32(const uint8_t *addr, bool isBig);
|
||||
static uint32_t readU32(const uint8_t *addr, bool isBig);
|
||||
static int64_t readS64(const uint8_t *addr, bool isBig);
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
|
File diff suppressed because it is too large
Load Diff
|
@ -1,897 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "Atoms.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
using llvm::support::ulittle32_t;
|
||||
using llvm::support::ulittle64_t;
|
||||
|
||||
using llvm::support::little32_t;
|
||||
using llvm::support::little64_t;
|
||||
|
||||
class ArchHandler_arm64 : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_arm64() = default;
|
||||
~ArchHandler_arm64() override = default;
|
||||
|
||||
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
||||
|
||||
Reference::KindArch kindArch() override {
|
||||
return Reference::KindArch::AArch64;
|
||||
}
|
||||
|
||||
/// Used by GOTPass to locate GOT References
|
||||
bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
switch (ref.kindValue()) {
|
||||
case gotPage21:
|
||||
case gotOffset12:
|
||||
canBypassGOT = true;
|
||||
return true;
|
||||
case delta32ToGOT:
|
||||
case unwindCIEToPersonalityFunction:
|
||||
case imageOffsetGot:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by GOTPass to update GOT References.
|
||||
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
|
||||
// If GOT slot was instantiated, transform:
|
||||
// gotPage21/gotOffset12 -> page21/offset12scale8
|
||||
// If GOT slot optimized away, transform:
|
||||
// gotPage21/gotOffset12 -> page21/addOffset12
|
||||
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
|
||||
assert(ref->kindArch() == Reference::KindArch::AArch64);
|
||||
switch (ref->kindValue()) {
|
||||
case gotPage21:
|
||||
const_cast<Reference *>(ref)->setKindValue(page21);
|
||||
break;
|
||||
case gotOffset12:
|
||||
const_cast<Reference *>(ref)->setKindValue(targetNowGOT ?
|
||||
offset12scale8 : addOffset12);
|
||||
break;
|
||||
case delta32ToGOT:
|
||||
const_cast<Reference *>(ref)->setKindValue(delta32);
|
||||
break;
|
||||
case imageOffsetGot:
|
||||
const_cast<Reference *>(ref)->setKindValue(imageOffset);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Not a GOT reference");
|
||||
}
|
||||
}
|
||||
|
||||
const StubInfo &stubInfo() override { return _sStubInfo; }
|
||||
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isNonCallBranch(const Reference &) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isPairedReloc(const normalized::Relocation &) override;
|
||||
|
||||
bool needsCompactUnwind() override {
|
||||
return true;
|
||||
}
|
||||
Reference::KindValue imageOffsetKind() override {
|
||||
return imageOffset;
|
||||
}
|
||||
Reference::KindValue imageOffsetKindIndirect() override {
|
||||
return imageOffsetGot;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToPersonalityFunctionKind() override {
|
||||
return unwindCIEToPersonalityFunction;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToCIEKind() override {
|
||||
return negDelta32;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToFunctionKind() override {
|
||||
return unwindFDEToFunction;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToEhFrameKind() override {
|
||||
return unwindInfoToEhFrame;
|
||||
}
|
||||
|
||||
Reference::KindValue pointerKind() override {
|
||||
return pointer64;
|
||||
}
|
||||
|
||||
Reference::KindValue lazyImmediateLocationKind() override {
|
||||
return lazyImmediateLocation;
|
||||
}
|
||||
|
||||
uint32_t dwarfCompactUnwindType() override {
|
||||
return 0x03000000;
|
||||
}
|
||||
|
||||
llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool isBig,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
llvm::Error
|
||||
getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool isBig, bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
|
||||
bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
|
||||
return (atom->contentType() == DefinedAtom::typeCString);
|
||||
}
|
||||
|
||||
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
|
||||
FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress,
|
||||
uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
|
||||
|
||||
void appendSectionRelocations(const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom,
|
||||
normalized::Relocations &relocs) override;
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
|
||||
enum Arm64Kind : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
branch26, /// ex: bl _foo
|
||||
page21, /// ex: adrp x1, _foo@PAGE
|
||||
offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF]
|
||||
offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF]
|
||||
offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF]
|
||||
offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF]
|
||||
offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF]
|
||||
gotPage21, /// ex: adrp x1, _foo@GOTPAGE
|
||||
gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF]
|
||||
tlvPage21, /// ex: adrp x1, _foo@TLVPAGE
|
||||
tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF]
|
||||
|
||||
pointer64, /// ex: .quad _foo
|
||||
delta64, /// ex: .quad _foo - .
|
||||
delta32, /// ex: .long _foo - .
|
||||
negDelta32, /// ex: .long . - _foo
|
||||
pointer64ToGOT, /// ex: .quad _foo@GOT
|
||||
delta32ToGOT, /// ex: .long _foo@GOT - .
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
addOffset12, /// Location contains LDR to change into ADD.
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
imageOffset, /// Location contains offset of atom in final image
|
||||
imageOffsetGot, /// Location contains offset of GOT entry for atom in
|
||||
/// final image (typically personality function).
|
||||
unwindCIEToPersonalityFunction, /// Nearly delta32ToGOT, but cannot be
|
||||
/// rematerialized in relocatable object
|
||||
/// (yay for implicit contracts!).
|
||||
unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
|
||||
/// relocatable object (yay for implicit contracts!).
|
||||
unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
|
||||
/// refer to __eh_frame entry.
|
||||
};
|
||||
|
||||
void applyFixupFinal(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress, uint64_t imageBaseAddress,
|
||||
FindAddressForAtom findSectionAddress);
|
||||
|
||||
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress, bool targetUnnamed);
|
||||
|
||||
// Utility functions for inspecting/updating instructions.
|
||||
static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp);
|
||||
static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp);
|
||||
static Arm64Kind offset12KindFromInstruction(uint32_t instr);
|
||||
static uint32_t setImm12(uint32_t instr, uint32_t offset);
|
||||
};
|
||||
|
||||
const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid),
|
||||
LLD_KIND_STRING_ENTRY(branch26),
|
||||
LLD_KIND_STRING_ENTRY(page21),
|
||||
LLD_KIND_STRING_ENTRY(offset12),
|
||||
LLD_KIND_STRING_ENTRY(offset12scale2),
|
||||
LLD_KIND_STRING_ENTRY(offset12scale4),
|
||||
LLD_KIND_STRING_ENTRY(offset12scale8),
|
||||
LLD_KIND_STRING_ENTRY(offset12scale16),
|
||||
LLD_KIND_STRING_ENTRY(gotPage21),
|
||||
LLD_KIND_STRING_ENTRY(gotOffset12),
|
||||
LLD_KIND_STRING_ENTRY(tlvPage21),
|
||||
LLD_KIND_STRING_ENTRY(tlvOffset12),
|
||||
LLD_KIND_STRING_ENTRY(pointer64),
|
||||
LLD_KIND_STRING_ENTRY(delta64),
|
||||
LLD_KIND_STRING_ENTRY(delta32),
|
||||
LLD_KIND_STRING_ENTRY(negDelta32),
|
||||
LLD_KIND_STRING_ENTRY(pointer64ToGOT),
|
||||
LLD_KIND_STRING_ENTRY(delta32ToGOT),
|
||||
|
||||
LLD_KIND_STRING_ENTRY(addOffset12),
|
||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_ENTRY(imageOffset),
|
||||
LLD_KIND_STRING_ENTRY(imageOffsetGot),
|
||||
LLD_KIND_STRING_ENTRY(unwindCIEToPersonalityFunction),
|
||||
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
|
||||
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
|
||||
|
||||
LLD_KIND_STRING_END
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = {
|
||||
"dyld_stub_binder",
|
||||
|
||||
// Lazy pointer references
|
||||
{ Reference::KindArch::AArch64, pointer64, 0, 0 },
|
||||
{ Reference::KindArch::AArch64, lazyPointer, 0, 0 },
|
||||
|
||||
// GOT pointer to dyld_stub_binder
|
||||
{ Reference::KindArch::AArch64, pointer64, 0, 0 },
|
||||
|
||||
// arm64 code alignment 2^1
|
||||
1,
|
||||
|
||||
// Stub size and code
|
||||
12,
|
||||
{ 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page
|
||||
0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff]
|
||||
0x00, 0x02, 0x1F, 0xD6 }, // BR X16
|
||||
{ Reference::KindArch::AArch64, page21, 0, 0 },
|
||||
{ true, offset12scale8, 4, 0 },
|
||||
|
||||
// Stub Helper size and code
|
||||
12,
|
||||
{ 0x50, 0x00, 0x00, 0x18, // LDR W16, L0
|
||||
0x00, 0x00, 0x00, 0x14, // LDR B helperhelper
|
||||
0x00, 0x00, 0x00, 0x00 }, // L0: .long 0
|
||||
{ Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 },
|
||||
{ Reference::KindArch::AArch64, branch26, 4, 0 },
|
||||
|
||||
// Stub helper image cache content type
|
||||
DefinedAtom::typeGOT,
|
||||
|
||||
// Stub Helper-Common size and code
|
||||
24,
|
||||
// Stub helper alignment
|
||||
2,
|
||||
{ 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page
|
||||
0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff
|
||||
0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]!
|
||||
0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page
|
||||
0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff]
|
||||
0x00, 0x02, 0x1F, 0xD6 }, // BR X16
|
||||
{ Reference::KindArch::AArch64, page21, 0, 0 },
|
||||
{ true, offset12, 4, 0 },
|
||||
{ Reference::KindArch::AArch64, page21, 12, 0 },
|
||||
{ true, offset12scale8, 16, 0 }
|
||||
};
|
||||
|
||||
bool ArchHandler_arm64::isCallSite(const Reference &ref) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
return (ref.kindValue() == branch26);
|
||||
}
|
||||
|
||||
bool ArchHandler_arm64::isPointer(const Reference &ref) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
Reference::KindValue kind = ref.kindValue();
|
||||
return (kind == pointer64);
|
||||
}
|
||||
|
||||
bool ArchHandler_arm64::isPairedReloc(const Relocation &r) {
|
||||
return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR));
|
||||
}
|
||||
|
||||
uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr,
|
||||
int32_t displacement) {
|
||||
assert((displacement <= 134217727) && (displacement > (-134217728)) &&
|
||||
"arm64 branch out of range");
|
||||
return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF);
|
||||
}
|
||||
|
||||
uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction,
|
||||
int64_t displacement) {
|
||||
assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) &&
|
||||
"arm64 ADRP out of range");
|
||||
assert(((instruction & 0x9F000000) == 0x90000000) &&
|
||||
"reloc not on ADRP instruction");
|
||||
uint32_t immhi = (displacement >> 9) & (0x00FFFFE0);
|
||||
uint32_t immlo = (displacement << 17) & (0x60000000);
|
||||
return (instruction & 0x9F00001F) | immlo | immhi;
|
||||
}
|
||||
|
||||
ArchHandler_arm64::Arm64Kind
|
||||
ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) {
|
||||
if (instruction & 0x08000000) {
|
||||
switch ((instruction >> 30) & 0x3) {
|
||||
case 0:
|
||||
if ((instruction & 0x04800000) == 0x04800000)
|
||||
return offset12scale16;
|
||||
return offset12;
|
||||
case 1:
|
||||
return offset12scale2;
|
||||
case 2:
|
||||
return offset12scale4;
|
||||
case 3:
|
||||
return offset12scale8;
|
||||
}
|
||||
}
|
||||
return offset12;
|
||||
}
|
||||
|
||||
uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) {
|
||||
assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range");
|
||||
uint32_t imm12 = offset << 10;
|
||||
return (instruction & 0xFFC003FF) | imm12;
|
||||
}
|
||||
|
||||
llvm::Error ArchHandler_arm64::getReferenceInfo(
|
||||
const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool isBig,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
|
||||
const lld::Atom **target, Reference::Addend *addend) {
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
switch (relocPattern(reloc)) {
|
||||
case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4:
|
||||
// ex: bl _foo
|
||||
*kind = branch26;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4:
|
||||
// ex: adrp x1, _foo@PAGE
|
||||
*kind = page21;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4:
|
||||
// ex: ldr x0, [x1, _foo@PAGEOFF]
|
||||
*kind = offset12KindFromInstruction(*(const little32_t *)fixupContent);
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
|
||||
// ex: adrp x1, _foo@GOTPAGE
|
||||
*kind = gotPage21;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4:
|
||||
// ex: ldr x0, [x1, _foo@GOTPAGEOFF]
|
||||
*kind = gotOffset12;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
|
||||
// ex: adrp x1, _foo@TLVPAGE
|
||||
*kind = tlvPage21;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4:
|
||||
// ex: ldr x0, [x1, _foo@TLVPAGEOFF]
|
||||
*kind = tlvOffset12;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_UNSIGNED | rExtern | rLength8:
|
||||
// ex: .quad _foo + N
|
||||
*kind = pointer64;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = *(const little64_t *)fixupContent;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_UNSIGNED | rLength8:
|
||||
// ex: .quad Lfoo + N
|
||||
*kind = pointer64;
|
||||
return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent,
|
||||
target, addend);
|
||||
case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8:
|
||||
// ex: .quad _foo@GOT
|
||||
*kind = pointer64ToGOT;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4:
|
||||
// ex: .long _foo@GOT - .
|
||||
|
||||
// If we are in an .eh_frame section, then the kind of the relocation should
|
||||
// not be delta32ToGOT. It may instead be unwindCIEToPersonalityFunction.
|
||||
if (inAtom->contentType() == DefinedAtom::typeCFI)
|
||||
*kind = unwindCIEToPersonalityFunction;
|
||||
else
|
||||
*kind = delta32ToGOT;
|
||||
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return llvm::Error::success();
|
||||
default:
|
||||
return llvm::make_error<GenericError>("unsupported arm64 relocation type");
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Error ArchHandler_arm64::getPairReferenceInfo(
|
||||
const normalized::Relocation &reloc1, const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress,
|
||||
bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
|
||||
const lld::Atom **target, Reference::Addend *addend) {
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
|
||||
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4):
|
||||
// ex: bl _foo+8
|
||||
*kind = branch26;
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = reloc1.symbol;
|
||||
return llvm::Error::success();
|
||||
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
|
||||
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4):
|
||||
// ex: adrp x1, _foo@PAGE
|
||||
*kind = page21;
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = reloc1.symbol;
|
||||
return llvm::Error::success();
|
||||
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
|
||||
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): {
|
||||
// ex: ldr w0, [x1, _foo@PAGEOFF]
|
||||
uint32_t cont32 = (int32_t)*(const little32_t *)fixupContent;
|
||||
*kind = offset12KindFromInstruction(cont32);
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = reloc1.symbol;
|
||||
return llvm::Error::success();
|
||||
}
|
||||
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
||||
ARM64_RELOC_UNSIGNED | rExtern | rLength8):
|
||||
// ex: .quad _foo - .
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
|
||||
// If we are in an .eh_frame section, then the kind of the relocation should
|
||||
// not be delta64. It may instead be unwindFDEToFunction.
|
||||
if (inAtom->contentType() == DefinedAtom::typeCFI)
|
||||
*kind = unwindFDEToFunction;
|
||||
else
|
||||
*kind = delta64;
|
||||
|
||||
// The offsets of the 2 relocations must match
|
||||
if (reloc1.offset != reloc2.offset)
|
||||
return llvm::make_error<GenericError>(
|
||||
"paired relocs must have the same offset");
|
||||
*addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom;
|
||||
return llvm::Error::success();
|
||||
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
||||
ARM64_RELOC_UNSIGNED | rExtern | rLength4):
|
||||
// ex: .quad _foo - .
|
||||
*kind = delta32;
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom;
|
||||
return llvm::Error::success();
|
||||
default:
|
||||
return llvm::make_error<GenericError>("unsupported arm64 relocation pair");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_arm64::generateAtomContent(
|
||||
const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
|
||||
// Copy raw bytes.
|
||||
std::copy(atom.rawContent().begin(), atom.rawContent().end(),
|
||||
atomContentBuffer.begin());
|
||||
// Apply fix-ups.
|
||||
#ifndef NDEBUG
|
||||
if (atom.begin() != atom.end()) {
|
||||
DEBUG_WITH_TYPE("atom-content", llvm::dbgs()
|
||||
<< "Applying fixups to atom:\n"
|
||||
<< " address="
|
||||
<< llvm::format(" 0x%09lX", &atom)
|
||||
<< ", file=#"
|
||||
<< atom.file().ordinal()
|
||||
<< ", atom=#"
|
||||
<< atom.ordinal()
|
||||
<< ", name="
|
||||
<< atom.name()
|
||||
<< ", type="
|
||||
<< atom.contentType()
|
||||
<< "\n");
|
||||
}
|
||||
#endif
|
||||
for (const Reference *ref : atom) {
|
||||
uint32_t offset = ref->offsetInAtom();
|
||||
const Atom *target = ref->target();
|
||||
bool targetUnnamed = target->name().empty();
|
||||
uint64_t targetAddress = 0;
|
||||
if (isa<DefinedAtom>(target))
|
||||
targetAddress = findAddress(*target);
|
||||
uint64_t atomAddress = findAddress(atom);
|
||||
uint64_t fixupAddress = atomAddress + offset;
|
||||
if (relocatable) {
|
||||
applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
|
||||
targetAddress, atomAddress, targetUnnamed);
|
||||
} else {
|
||||
applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
|
||||
targetAddress, atomAddress, imageBaseAddress,
|
||||
findSectionAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress,
|
||||
uint64_t imageBaseAddress,
|
||||
FindAddressForAtom findSectionAddress) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
||||
int32_t displacement;
|
||||
uint32_t instruction;
|
||||
uint32_t value32;
|
||||
uint32_t value64;
|
||||
switch (static_cast<Arm64Kind>(ref.kindValue())) {
|
||||
case branch26:
|
||||
displacement = (targetAddress - fixupAddress) + ref.addend();
|
||||
*loc32 = setDisplacementInBranch26(*loc32, displacement);
|
||||
return;
|
||||
case page21:
|
||||
case gotPage21:
|
||||
case tlvPage21:
|
||||
displacement =
|
||||
((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096));
|
||||
*loc32 = setDisplacementInADRP(*loc32, displacement);
|
||||
return;
|
||||
case offset12:
|
||||
case gotOffset12:
|
||||
case tlvOffset12:
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
*loc32 = setImm12(*loc32, displacement);
|
||||
return;
|
||||
case offset12scale2:
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
assert(((displacement & 0x1) == 0) &&
|
||||
"scaled imm12 not accessing 2-byte aligneds");
|
||||
*loc32 = setImm12(*loc32, displacement >> 1);
|
||||
return;
|
||||
case offset12scale4:
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
assert(((displacement & 0x3) == 0) &&
|
||||
"scaled imm12 not accessing 4-byte aligned");
|
||||
*loc32 = setImm12(*loc32, displacement >> 2);
|
||||
return;
|
||||
case offset12scale8:
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
assert(((displacement & 0x7) == 0) &&
|
||||
"scaled imm12 not accessing 8-byte aligned");
|
||||
*loc32 = setImm12(*loc32, displacement >> 3);
|
||||
return;
|
||||
case offset12scale16:
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
assert(((displacement & 0xF) == 0) &&
|
||||
"scaled imm12 not accessing 16-byte aligned");
|
||||
*loc32 = setImm12(*loc32, displacement >> 4);
|
||||
return;
|
||||
case addOffset12:
|
||||
instruction = *loc32;
|
||||
assert(((instruction & 0xFFC00000) == 0xF9400000) &&
|
||||
"GOT reloc is not an LDR instruction");
|
||||
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
|
||||
value32 = 0x91000000 | (instruction & 0x000003FF);
|
||||
instruction = setImm12(value32, displacement);
|
||||
*loc32 = instruction;
|
||||
return;
|
||||
case pointer64:
|
||||
case pointer64ToGOT:
|
||||
*loc64 = targetAddress + ref.addend();
|
||||
return;
|
||||
case delta64:
|
||||
case unwindFDEToFunction:
|
||||
*loc64 = (targetAddress - fixupAddress) + ref.addend();
|
||||
return;
|
||||
case delta32:
|
||||
case delta32ToGOT:
|
||||
case unwindCIEToPersonalityFunction:
|
||||
*loc32 = (targetAddress - fixupAddress) + ref.addend();
|
||||
return;
|
||||
case negDelta32:
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
return;
|
||||
case lazyPointer:
|
||||
// Do nothing
|
||||
return;
|
||||
case lazyImmediateLocation:
|
||||
*loc32 = ref.addend();
|
||||
return;
|
||||
case imageOffset:
|
||||
*loc32 = (targetAddress - imageBaseAddress) + ref.addend();
|
||||
return;
|
||||
case imageOffsetGot:
|
||||
llvm_unreachable("imageOffsetGot should have been changed to imageOffset");
|
||||
break;
|
||||
case unwindInfoToEhFrame:
|
||||
value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
|
||||
assert(value64 < 0xffffffU && "offset in __eh_frame too large");
|
||||
*loc32 = (*loc32 & 0xff000000U) | value64;
|
||||
return;
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("invalid arm64 Reference Kind");
|
||||
}
|
||||
|
||||
void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref,
|
||||
uint8_t *loc,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress,
|
||||
bool targetUnnamed) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
||||
switch (static_cast<Arm64Kind>(ref.kindValue())) {
|
||||
case branch26:
|
||||
*loc32 = setDisplacementInBranch26(*loc32, 0);
|
||||
return;
|
||||
case page21:
|
||||
case gotPage21:
|
||||
case tlvPage21:
|
||||
*loc32 = setDisplacementInADRP(*loc32, 0);
|
||||
return;
|
||||
case offset12:
|
||||
case offset12scale2:
|
||||
case offset12scale4:
|
||||
case offset12scale8:
|
||||
case offset12scale16:
|
||||
case gotOffset12:
|
||||
case tlvOffset12:
|
||||
*loc32 = setImm12(*loc32, 0);
|
||||
return;
|
||||
case pointer64:
|
||||
if (targetUnnamed)
|
||||
*loc64 = targetAddress + ref.addend();
|
||||
else
|
||||
*loc64 = ref.addend();
|
||||
return;
|
||||
case delta64:
|
||||
*loc64 = ref.addend() + inAtomAddress - fixupAddress;
|
||||
return;
|
||||
case unwindFDEToFunction:
|
||||
// We don't emit unwindFDEToFunction in -r mode as they are implicitly
|
||||
// generated from the data in the __eh_frame section. So here we need
|
||||
// to use the targetAddress so that we can generate the full relocation
|
||||
// when we parse again later.
|
||||
*loc64 = targetAddress - fixupAddress;
|
||||
return;
|
||||
case delta32:
|
||||
*loc32 = ref.addend() + inAtomAddress - fixupAddress;
|
||||
return;
|
||||
case negDelta32:
|
||||
// We don't emit negDelta32 in -r mode as they are implicitly
|
||||
// generated from the data in the __eh_frame section. So here we need
|
||||
// to use the targetAddress so that we can generate the full relocation
|
||||
// when we parse again later.
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
return;
|
||||
case pointer64ToGOT:
|
||||
*loc64 = 0;
|
||||
return;
|
||||
case delta32ToGOT:
|
||||
*loc32 = inAtomAddress - fixupAddress;
|
||||
return;
|
||||
case unwindCIEToPersonalityFunction:
|
||||
// We don't emit unwindCIEToPersonalityFunction in -r mode as they are
|
||||
// implicitly generated from the data in the __eh_frame section. So here we
|
||||
// need to use the targetAddress so that we can generate the full relocation
|
||||
// when we parse again later.
|
||||
*loc32 = targetAddress - fixupAddress;
|
||||
return;
|
||||
case addOffset12:
|
||||
llvm_unreachable("lazy reference kind implies GOT pass was run");
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
||||
case imageOffset:
|
||||
case imageOffsetGot:
|
||||
case unwindInfoToEhFrame:
|
||||
llvm_unreachable("fixup implies __unwind_info");
|
||||
return;
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unknown arm64 Reference Kind");
|
||||
}
|
||||
|
||||
void ArchHandler_arm64::appendSectionRelocations(
|
||||
const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom, normalized::Relocations &relocs) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::AArch64);
|
||||
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
||||
switch (static_cast<Arm64Kind>(ref.kindValue())) {
|
||||
case branch26:
|
||||
if (ref.addend()) {
|
||||
appendReloc(relocs, sectionOffset, ref.addend(), 0,
|
||||
ARM64_RELOC_ADDEND | rLength4);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
|
||||
} else {
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
|
||||
}
|
||||
return;
|
||||
case page21:
|
||||
if (ref.addend()) {
|
||||
appendReloc(relocs, sectionOffset, ref.addend(), 0,
|
||||
ARM64_RELOC_ADDEND | rLength4);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
|
||||
} else {
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
|
||||
}
|
||||
return;
|
||||
case offset12:
|
||||
case offset12scale2:
|
||||
case offset12scale4:
|
||||
case offset12scale8:
|
||||
case offset12scale16:
|
||||
if (ref.addend()) {
|
||||
appendReloc(relocs, sectionOffset, ref.addend(), 0,
|
||||
ARM64_RELOC_ADDEND | rLength4);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
|
||||
} else {
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
|
||||
}
|
||||
return;
|
||||
case gotPage21:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
|
||||
return;
|
||||
case gotOffset12:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4);
|
||||
return;
|
||||
case tlvPage21:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
|
||||
return;
|
||||
case tlvOffset12:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4);
|
||||
return;
|
||||
case pointer64:
|
||||
if (ref.target()->name().empty())
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_UNSIGNED | rLength8);
|
||||
else
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_UNSIGNED | rExtern | rLength8);
|
||||
return;
|
||||
case delta64:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
ARM64_RELOC_SUBTRACTOR | rExtern | rLength8);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_UNSIGNED | rExtern | rLength8);
|
||||
return;
|
||||
case delta32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_UNSIGNED | rExtern | rLength4 );
|
||||
return;
|
||||
case pointer64ToGOT:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8);
|
||||
return;
|
||||
case delta32ToGOT:
|
||||
assert(ref.addend() == 0);
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4);
|
||||
return;
|
||||
case addOffset12:
|
||||
llvm_unreachable("lazy reference kind implies GOT pass was run");
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
||||
case imageOffset:
|
||||
case imageOffsetGot:
|
||||
llvm_unreachable("deltas from mach_header can only be in final images");
|
||||
case unwindCIEToPersonalityFunction:
|
||||
case unwindFDEToFunction:
|
||||
case unwindInfoToEhFrame:
|
||||
case negDelta32:
|
||||
// Do nothing.
|
||||
return;
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unknown arm64 Reference Kind");
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm64() {
|
||||
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm64());
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -1,643 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "Atoms.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
using llvm::support::ulittle16_t;
|
||||
using llvm::support::ulittle32_t;
|
||||
|
||||
using llvm::support::little16_t;
|
||||
using llvm::support::little32_t;
|
||||
|
||||
class ArchHandler_x86 : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_x86() = default;
|
||||
~ArchHandler_x86() override = default;
|
||||
|
||||
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
||||
|
||||
Reference::KindArch kindArch() override { return Reference::KindArch::x86; }
|
||||
|
||||
const StubInfo &stubInfo() override { return _sStubInfo; }
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isNonCallBranch(const Reference &) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isPairedReloc(const normalized::Relocation &) override;
|
||||
|
||||
bool needsCompactUnwind() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference::KindValue imageOffsetKind() override {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
Reference::KindValue imageOffsetKindIndirect() override {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToPersonalityFunctionKind() override {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToCIEKind() override {
|
||||
return negDelta32;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToFunctionKind() override{
|
||||
return delta32;
|
||||
}
|
||||
|
||||
Reference::KindValue lazyImmediateLocationKind() override {
|
||||
return lazyImmediateLocation;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToEhFrameKind() override {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
Reference::KindValue pointerKind() override {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
uint32_t dwarfCompactUnwindType() override {
|
||||
return 0x04000000U;
|
||||
}
|
||||
|
||||
llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
llvm::Error
|
||||
getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap, bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
|
||||
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
|
||||
FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress,
|
||||
uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
|
||||
|
||||
void appendSectionRelocations(const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom,
|
||||
normalized::Relocations &relocs) override;
|
||||
|
||||
bool isDataInCodeTransition(Reference::KindValue refKind) override {
|
||||
return refKind == modeCode || refKind == modeData;
|
||||
}
|
||||
|
||||
Reference::KindValue dataInCodeTransitionStart(
|
||||
const MachODefinedAtom &atom) override {
|
||||
return modeData;
|
||||
}
|
||||
|
||||
Reference::KindValue dataInCodeTransitionEnd(
|
||||
const MachODefinedAtom &atom) override {
|
||||
return modeCode;
|
||||
}
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
|
||||
enum X86Kind : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
modeCode, /// Content starting at this offset is code.
|
||||
modeData, /// Content starting at this offset is data.
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
branch32, /// ex: call _foo
|
||||
branch16, /// ex: callw _foo
|
||||
abs32, /// ex: movl _foo, %eax
|
||||
funcRel32, /// ex: movl _foo-L1(%eax), %eax
|
||||
pointer32, /// ex: .long _foo
|
||||
delta32, /// ex: .long _foo - .
|
||||
negDelta32, /// ex: .long . - _foo
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
};
|
||||
|
||||
static bool useExternalRelocationTo(const Atom &target);
|
||||
|
||||
void applyFixupFinal(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress);
|
||||
|
||||
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress);
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArchHandler_x86
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid),
|
||||
LLD_KIND_STRING_ENTRY(modeCode),
|
||||
LLD_KIND_STRING_ENTRY(modeData),
|
||||
LLD_KIND_STRING_ENTRY(branch32),
|
||||
LLD_KIND_STRING_ENTRY(branch16),
|
||||
LLD_KIND_STRING_ENTRY(abs32),
|
||||
LLD_KIND_STRING_ENTRY(funcRel32),
|
||||
LLD_KIND_STRING_ENTRY(pointer32),
|
||||
LLD_KIND_STRING_ENTRY(delta32),
|
||||
LLD_KIND_STRING_ENTRY(negDelta32),
|
||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_END
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
|
||||
"dyld_stub_binder",
|
||||
|
||||
// Lazy pointer references
|
||||
{ Reference::KindArch::x86, pointer32, 0, 0 },
|
||||
{ Reference::KindArch::x86, lazyPointer, 0, 0 },
|
||||
|
||||
// GOT pointer to dyld_stub_binder
|
||||
{ Reference::KindArch::x86, pointer32, 0, 0 },
|
||||
|
||||
// x86 code alignment
|
||||
1,
|
||||
|
||||
// Stub size and code
|
||||
6,
|
||||
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
|
||||
{ Reference::KindArch::x86, abs32, 2, 0 },
|
||||
{ false, 0, 0, 0 },
|
||||
|
||||
// Stub Helper size and code
|
||||
10,
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
|
||||
{ Reference::KindArch::x86, lazyImmediateLocation, 1, 0 },
|
||||
{ Reference::KindArch::x86, branch32, 6, 0 },
|
||||
|
||||
// Stub helper image cache content type
|
||||
DefinedAtom::typeNonLazyPointer,
|
||||
|
||||
// Stub Helper-Common size and code
|
||||
12,
|
||||
// Stub helper alignment
|
||||
2,
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
|
||||
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
|
||||
0x90 }, // nop
|
||||
{ Reference::KindArch::x86, abs32, 1, 0 },
|
||||
{ false, 0, 0, 0 },
|
||||
{ Reference::KindArch::x86, abs32, 7, 0 },
|
||||
{ false, 0, 0, 0 }
|
||||
};
|
||||
|
||||
bool ArchHandler_x86::isCallSite(const Reference &ref) {
|
||||
return (ref.kindValue() == branch32);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86::isPointer(const Reference &ref) {
|
||||
return (ref.kindValue() == pointer32);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) {
|
||||
if (!reloc.scattered)
|
||||
return false;
|
||||
return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) ||
|
||||
(reloc.type == GENERIC_RELOC_SECTDIFF);
|
||||
}
|
||||
|
||||
llvm::Error
|
||||
ArchHandler_x86::getReferenceInfo(const Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
DefinedAtom::ContentPermissions perms;
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
uint64_t targetAddress;
|
||||
switch (relocPattern(reloc)) {
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4:
|
||||
// ex: call _foo (and _foo undefined)
|
||||
*kind = branch32;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent;
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
|
||||
// ex: call _foo (and _foo defined)
|
||||
*kind = branch32;
|
||||
targetAddress =
|
||||
fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4:
|
||||
// ex: call _foo+n (and _foo defined)
|
||||
*kind = branch32;
|
||||
targetAddress =
|
||||
fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
|
||||
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
*addend = targetAddress - reloc.value;
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
|
||||
// ex: callw _foo (and _foo undefined)
|
||||
*kind = branch16;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent;
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
|
||||
// ex: callw _foo (and _foo defined)
|
||||
*kind = branch16;
|
||||
targetAddress =
|
||||
fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2:
|
||||
// ex: callw _foo+n (and _foo defined)
|
||||
*kind = branch16;
|
||||
targetAddress =
|
||||
fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
|
||||
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
*addend = targetAddress - reloc.value;
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rExtern | rLength4:
|
||||
// ex: movl _foo, %eax (and _foo undefined)
|
||||
// ex: .long _foo (and _foo undefined)
|
||||
perms = inAtom->permissions();
|
||||
*kind =
|
||||
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
|
||||
: pointer32;
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = *(const ulittle32_t *)fixupContent;
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rLength4:
|
||||
// ex: movl _foo, %eax (and _foo defined)
|
||||
// ex: .long _foo (and _foo defined)
|
||||
perms = inAtom->permissions();
|
||||
*kind =
|
||||
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
|
||||
: pointer32;
|
||||
targetAddress = *(const ulittle32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rScattered | rLength4:
|
||||
// ex: .long _foo+n (and _foo defined)
|
||||
perms = inAtom->permissions();
|
||||
*kind =
|
||||
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
|
||||
: pointer32;
|
||||
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
*addend = *(const ulittle32_t *)fixupContent - reloc.value;
|
||||
break;
|
||||
default:
|
||||
return llvm::make_error<GenericError>("unsupported i386 relocation type");
|
||||
}
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
llvm::Error
|
||||
ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddr,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
DefinedAtom::ContentPermissions perms = inAtom->permissions();
|
||||
uint32_t fromAddress;
|
||||
uint32_t toAddress;
|
||||
uint32_t value;
|
||||
const lld::Atom *fromTarget;
|
||||
Reference::Addend offsetInTo;
|
||||
Reference::Addend offsetInFrom;
|
||||
switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case ((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4):
|
||||
case ((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4):
|
||||
toAddress = reloc1.value;
|
||||
fromAddress = reloc2.value;
|
||||
value = *(const little32_t *)fixupContent;
|
||||
if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
|
||||
return ec;
|
||||
if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
|
||||
return ec;
|
||||
if (fromTarget != inAtom) {
|
||||
if (*target != inAtom)
|
||||
return llvm::make_error<GenericError>(
|
||||
"SECTDIFF relocation where neither target is in atom");
|
||||
*kind = negDelta32;
|
||||
*addend = toAddress - value - fromAddress;
|
||||
*target = fromTarget;
|
||||
} else {
|
||||
if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) {
|
||||
// SECTDIFF relocations are used in i386 codegen where the function
|
||||
// prolog does a CALL to the next instruction which POPs the return
|
||||
// address into EBX which becomes the pic-base register. The POP
|
||||
// instruction is label the used for the subtrahend in expressions.
|
||||
// The funcRel32 kind represents the 32-bit delta to some symbol from
|
||||
// the start of the function (atom) containing the funcRel32.
|
||||
*kind = funcRel32;
|
||||
uint32_t ta = fromAddress + value - toAddress;
|
||||
*addend = ta - offsetInFrom;
|
||||
} else {
|
||||
*kind = delta32;
|
||||
*addend = fromAddress + value - toAddress;
|
||||
}
|
||||
}
|
||||
return llvm::Error::success();
|
||||
break;
|
||||
default:
|
||||
return llvm::make_error<GenericError>("unsupported i386 relocation type");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom,
|
||||
bool relocatable,
|
||||
FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress,
|
||||
uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
|
||||
// Copy raw bytes.
|
||||
std::copy(atom.rawContent().begin(), atom.rawContent().end(),
|
||||
atomContentBuffer.begin());
|
||||
// Apply fix-ups.
|
||||
for (const Reference *ref : atom) {
|
||||
uint32_t offset = ref->offsetInAtom();
|
||||
const Atom *target = ref->target();
|
||||
uint64_t targetAddress = 0;
|
||||
if (isa<DefinedAtom>(target))
|
||||
targetAddress = findAddress(*target);
|
||||
uint64_t atomAddress = findAddress(atom);
|
||||
uint64_t fixupAddress = atomAddress + offset;
|
||||
if (relocatable) {
|
||||
applyFixupRelocatable(*ref, &atomContentBuffer[offset],
|
||||
fixupAddress, targetAddress,
|
||||
atomAddress);
|
||||
} else {
|
||||
applyFixupFinal(*ref, &atomContentBuffer[offset],
|
||||
fixupAddress, targetAddress,
|
||||
atomAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
switch (static_cast<X86Kind>(ref.kindValue())) {
|
||||
case branch32:
|
||||
*loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
|
||||
break;
|
||||
case branch16:
|
||||
*loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend();
|
||||
break;
|
||||
case pointer32:
|
||||
case abs32:
|
||||
*loc32 = targetAddress + ref.addend();
|
||||
break;
|
||||
case funcRel32:
|
||||
*loc32 = targetAddress - inAtomAddress + ref.addend();
|
||||
break;
|
||||
case delta32:
|
||||
*loc32 = targetAddress - fixupAddress + ref.addend();
|
||||
break;
|
||||
case negDelta32:
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
break;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case lazyPointer:
|
||||
// do nothing
|
||||
break;
|
||||
case lazyImmediateLocation:
|
||||
*loc32 = ref.addend();
|
||||
break;
|
||||
case invalid:
|
||||
llvm_unreachable("invalid x86 Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86::applyFixupRelocatable(const Reference &ref,
|
||||
uint8_t *loc,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86);
|
||||
bool useExternalReloc = useExternalRelocationTo(*ref.target());
|
||||
ulittle16_t *loc16 = reinterpret_cast<ulittle16_t *>(loc);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
switch (static_cast<X86Kind>(ref.kindValue())) {
|
||||
case branch32:
|
||||
if (useExternalReloc)
|
||||
*loc32 = ref.addend() - (fixupAddress + 4);
|
||||
else
|
||||
*loc32 =(targetAddress - (fixupAddress+4)) + ref.addend();
|
||||
break;
|
||||
case branch16:
|
||||
if (useExternalReloc)
|
||||
*loc16 = ref.addend() - (fixupAddress + 2);
|
||||
else
|
||||
*loc16 = (targetAddress - (fixupAddress+2)) + ref.addend();
|
||||
break;
|
||||
case pointer32:
|
||||
case abs32:
|
||||
*loc32 = targetAddress + ref.addend();
|
||||
break;
|
||||
case funcRel32:
|
||||
*loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME
|
||||
break;
|
||||
case delta32:
|
||||
*loc32 = targetAddress - fixupAddress + ref.addend();
|
||||
break;
|
||||
case negDelta32:
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
break;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
// do nothing
|
||||
break;
|
||||
case invalid:
|
||||
llvm_unreachable("invalid x86 Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) {
|
||||
// Undefined symbols are referenced via external relocations.
|
||||
if (isa<UndefinedAtom>(&target))
|
||||
return true;
|
||||
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
|
||||
switch (defAtom->merge()) {
|
||||
case DefinedAtom::mergeAsTentative:
|
||||
// Tentative definitions are referenced via external relocations.
|
||||
return true;
|
||||
case DefinedAtom::mergeAsWeak:
|
||||
case DefinedAtom::mergeAsWeakAndAddressUsed:
|
||||
// Global weak-defs are referenced via external relocations.
|
||||
return (defAtom->scope() == DefinedAtom::scopeGlobal);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Everything else is reference via an internal relocation.
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArchHandler_x86::appendSectionRelocations(
|
||||
const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom,
|
||||
normalized::Relocations &relocs) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86);
|
||||
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
||||
bool useExternalReloc = useExternalRelocationTo(*ref.target());
|
||||
switch (static_cast<X86Kind>(ref.kindValue())) {
|
||||
case modeCode:
|
||||
case modeData:
|
||||
break;
|
||||
case branch32:
|
||||
if (useExternalReloc) {
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4);
|
||||
} else {
|
||||
if (ref.addend() != 0)
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4);
|
||||
else
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
|
||||
GENERIC_RELOC_VANILLA | rPcRel | rLength4);
|
||||
}
|
||||
break;
|
||||
case branch16:
|
||||
if (useExternalReloc) {
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2);
|
||||
} else {
|
||||
if (ref.addend() != 0)
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2);
|
||||
else
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
|
||||
GENERIC_RELOC_VANILLA | rPcRel | rLength2);
|
||||
}
|
||||
break;
|
||||
case pointer32:
|
||||
case abs32:
|
||||
if (useExternalReloc)
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
GENERIC_RELOC_VANILLA | rExtern | rLength4);
|
||||
else {
|
||||
if (ref.addend() != 0)
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_VANILLA | rScattered | rLength4);
|
||||
else
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
GENERIC_RELOC_VANILLA | rLength4);
|
||||
}
|
||||
break;
|
||||
case funcRel32:
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(),
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4);
|
||||
break;
|
||||
case delta32:
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
|
||||
ref.offsetInAtom(),
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4);
|
||||
break;
|
||||
case negDelta32:
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
|
||||
ref.offsetInAtom(),
|
||||
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
|
||||
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4);
|
||||
break;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
||||
break;
|
||||
case invalid:
|
||||
llvm_unreachable("unknown x86 Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() {
|
||||
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86());
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -1,899 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "Atoms.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
using llvm::support::ulittle32_t;
|
||||
using llvm::support::ulittle64_t;
|
||||
|
||||
using llvm::support::little32_t;
|
||||
using llvm::support::little64_t;
|
||||
|
||||
class ArchHandler_x86_64 : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_x86_64() = default;
|
||||
~ArchHandler_x86_64() override = default;
|
||||
|
||||
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
||||
|
||||
Reference::KindArch kindArch() override {
|
||||
return Reference::KindArch::x86_64;
|
||||
}
|
||||
|
||||
/// Used by GOTPass to locate GOT References
|
||||
bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
switch (ref.kindValue()) {
|
||||
case ripRel32GotLoad:
|
||||
canBypassGOT = true;
|
||||
return true;
|
||||
case ripRel32Got:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
case imageOffsetGot:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isTLVAccess(const Reference &ref) const override {
|
||||
assert(ref.kindNamespace() == Reference::KindNamespace::mach_o);
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
return ref.kindValue() == ripRel32Tlv;
|
||||
}
|
||||
|
||||
void updateReferenceToTLV(const Reference *ref) override {
|
||||
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
|
||||
assert(ref->kindArch() == Reference::KindArch::x86_64);
|
||||
assert(ref->kindValue() == ripRel32Tlv);
|
||||
const_cast<Reference*>(ref)->setKindValue(ripRel32);
|
||||
}
|
||||
|
||||
/// Used by GOTPass to update GOT References
|
||||
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
|
||||
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
|
||||
assert(ref->kindArch() == Reference::KindArch::x86_64);
|
||||
|
||||
switch (ref->kindValue()) {
|
||||
case ripRel32Got:
|
||||
assert(targetNowGOT && "target must be GOT");
|
||||
LLVM_FALLTHROUGH;
|
||||
case ripRel32GotLoad:
|
||||
const_cast<Reference *>(ref)
|
||||
->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea);
|
||||
break;
|
||||
case imageOffsetGot:
|
||||
const_cast<Reference *>(ref)->setKindValue(imageOffset);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown GOT reference kind");
|
||||
}
|
||||
}
|
||||
|
||||
bool needsCompactUnwind() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference::KindValue imageOffsetKind() override {
|
||||
return imageOffset;
|
||||
}
|
||||
|
||||
Reference::KindValue imageOffsetKindIndirect() override {
|
||||
return imageOffsetGot;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToPersonalityFunctionKind() override {
|
||||
return ripRel32Got;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToCIEKind() override {
|
||||
return negDelta32;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToFunctionKind() override{
|
||||
return unwindFDEToFunction;
|
||||
}
|
||||
|
||||
Reference::KindValue lazyImmediateLocationKind() override {
|
||||
return lazyImmediateLocation;
|
||||
}
|
||||
|
||||
Reference::KindValue unwindRefToEhFrameKind() override {
|
||||
return unwindInfoToEhFrame;
|
||||
}
|
||||
|
||||
Reference::KindValue pointerKind() override {
|
||||
return pointer64;
|
||||
}
|
||||
|
||||
uint32_t dwarfCompactUnwindType() override {
|
||||
return 0x04000000U;
|
||||
}
|
||||
|
||||
const StubInfo &stubInfo() override { return _sStubInfo; }
|
||||
|
||||
bool isNonCallBranch(const Reference &) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isPairedReloc(const normalized::Relocation &) override;
|
||||
|
||||
llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
llvm::Error
|
||||
getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap, bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) override;
|
||||
|
||||
bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
|
||||
return (atom->contentType() == DefinedAtom::typeCString);
|
||||
}
|
||||
|
||||
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
|
||||
FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress,
|
||||
uint64_t imageBase,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
|
||||
|
||||
void appendSectionRelocations(const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom,
|
||||
normalized::Relocations &relocs) override;
|
||||
|
||||
bool isDataInCodeTransition(Reference::KindValue refKind) override {
|
||||
return refKind == modeCode || refKind == modeData;
|
||||
}
|
||||
|
||||
Reference::KindValue dataInCodeTransitionStart(
|
||||
const MachODefinedAtom &atom) override {
|
||||
return modeData;
|
||||
}
|
||||
|
||||
Reference::KindValue dataInCodeTransitionEnd(
|
||||
const MachODefinedAtom &atom) override {
|
||||
return modeCode;
|
||||
}
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
|
||||
enum X86_64Kind: Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
modeCode, /// Content starting at this offset is code.
|
||||
modeData, /// Content starting at this offset is data.
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
branch32, /// ex: call _foo
|
||||
ripRel32, /// ex: movq _foo(%rip), %rax
|
||||
ripRel32Minus1, /// ex: movb $0x12, _foo(%rip)
|
||||
ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip)
|
||||
ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip)
|
||||
ripRel32Anon, /// ex: movq L1(%rip), %rax
|
||||
ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip)
|
||||
ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip)
|
||||
ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip)
|
||||
ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
|
||||
ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
|
||||
ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi
|
||||
pointer64, /// ex: .quad _foo
|
||||
pointer64Anon, /// ex: .quad L1
|
||||
delta64, /// ex: .quad _foo - .
|
||||
delta32, /// ex: .long _foo - .
|
||||
delta64Anon, /// ex: .quad L1 - .
|
||||
delta32Anon, /// ex: .long L1 - .
|
||||
negDelta64, /// ex: .quad . - _foo
|
||||
negDelta32, /// ex: .long . - _foo
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so
|
||||
/// "movq _foo@GOTPCREL(%rip), %rax" can be changed
|
||||
/// to "leaq _foo(%rip), %rax
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
|
||||
imageOffset, /// Location contains offset of atom in final image
|
||||
imageOffsetGot, /// Location contains offset of GOT entry for atom in
|
||||
/// final image (typically personality function).
|
||||
unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
|
||||
/// relocatable object (yay for implicit contracts!).
|
||||
unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
|
||||
/// refer to __eh_frame entry.
|
||||
tlvInitSectionOffset /// Location contains offset tlv init-value atom
|
||||
/// within the __thread_data section.
|
||||
};
|
||||
|
||||
Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
|
||||
|
||||
void applyFixupFinal(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress, uint64_t imageBaseAddress,
|
||||
FindAddressForAtom findSectionAddress);
|
||||
|
||||
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress);
|
||||
};
|
||||
|
||||
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid),
|
||||
LLD_KIND_STRING_ENTRY(modeCode),
|
||||
LLD_KIND_STRING_ENTRY(modeData),
|
||||
LLD_KIND_STRING_ENTRY(branch32),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus1),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus2),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus4),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Got),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Tlv),
|
||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_ENTRY(pointer64),
|
||||
LLD_KIND_STRING_ENTRY(pointer64Anon),
|
||||
LLD_KIND_STRING_ENTRY(delta32),
|
||||
LLD_KIND_STRING_ENTRY(delta64),
|
||||
LLD_KIND_STRING_ENTRY(delta32Anon),
|
||||
LLD_KIND_STRING_ENTRY(delta64Anon),
|
||||
LLD_KIND_STRING_ENTRY(negDelta64),
|
||||
LLD_KIND_STRING_ENTRY(negDelta32),
|
||||
LLD_KIND_STRING_ENTRY(imageOffset),
|
||||
LLD_KIND_STRING_ENTRY(imageOffsetGot),
|
||||
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
|
||||
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
|
||||
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
|
||||
LLD_KIND_STRING_END
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
|
||||
"dyld_stub_binder",
|
||||
|
||||
// Lazy pointer references
|
||||
{ Reference::KindArch::x86_64, pointer64, 0, 0 },
|
||||
{ Reference::KindArch::x86_64, lazyPointer, 0, 0 },
|
||||
|
||||
// GOT pointer to dyld_stub_binder
|
||||
{ Reference::KindArch::x86_64, pointer64, 0, 0 },
|
||||
|
||||
// x86_64 code alignment 2^1
|
||||
1,
|
||||
|
||||
// Stub size and code
|
||||
6,
|
||||
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
|
||||
{ Reference::KindArch::x86_64, ripRel32, 2, 0 },
|
||||
{ false, 0, 0, 0 },
|
||||
|
||||
// Stub Helper size and code
|
||||
10,
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
|
||||
{ Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 },
|
||||
{ Reference::KindArch::x86_64, branch32, 6, 0 },
|
||||
|
||||
// Stub helper image cache content type
|
||||
DefinedAtom::typeNonLazyPointer,
|
||||
|
||||
// Stub Helper-Common size and code
|
||||
16,
|
||||
// Stub helper alignment
|
||||
2,
|
||||
{ 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11
|
||||
0x41, 0x53, // push %r11
|
||||
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip)
|
||||
0x90 }, // nop
|
||||
{ Reference::KindArch::x86_64, ripRel32, 3, 0 },
|
||||
{ false, 0, 0, 0 },
|
||||
{ Reference::KindArch::x86_64, ripRel32, 11, 0 },
|
||||
{ false, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
bool ArchHandler_x86_64::isCallSite(const Reference &ref) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
return (ref.kindValue() == branch32);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86_64::isPointer(const Reference &ref) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
Reference::KindValue kind = ref.kindValue();
|
||||
return (kind == pointer64 || kind == pointer64Anon);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) {
|
||||
return (reloc.type == X86_64_RELOC_SUBTRACTOR);
|
||||
}
|
||||
|
||||
Reference::KindValue
|
||||
ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) {
|
||||
switch(relocPattern(reloc)) {
|
||||
case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4:
|
||||
return branch32;
|
||||
case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4:
|
||||
return ripRel32;
|
||||
case X86_64_RELOC_SIGNED | rPcRel | rLength4:
|
||||
return ripRel32Anon;
|
||||
case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Minus1;
|
||||
case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4:
|
||||
return ripRel32Minus1Anon;
|
||||
case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Minus2;
|
||||
case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4:
|
||||
return ripRel32Minus2Anon;
|
||||
case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Minus4;
|
||||
case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4:
|
||||
return ripRel32Minus4Anon;
|
||||
case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4:
|
||||
return ripRel32GotLoad;
|
||||
case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Got;
|
||||
case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Tlv;
|
||||
case X86_64_RELOC_UNSIGNED | rExtern | rLength8:
|
||||
return pointer64;
|
||||
case X86_64_RELOC_UNSIGNED | rLength8:
|
||||
return pointer64Anon;
|
||||
default:
|
||||
return invalid;
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Error
|
||||
ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
*kind = kindFromReloc(reloc);
|
||||
if (*kind == invalid)
|
||||
return llvm::make_error<GenericError>("unknown type");
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
uint64_t targetAddress;
|
||||
switch (*kind) {
|
||||
case branch32:
|
||||
case ripRel32:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = *(const little32_t *)fixupContent;
|
||||
return llvm::Error::success();
|
||||
case ripRel32Minus1:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = (int32_t)*(const little32_t *)fixupContent + 1;
|
||||
return llvm::Error::success();
|
||||
case ripRel32Minus2:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = (int32_t)*(const little32_t *)fixupContent + 2;
|
||||
return llvm::Error::success();
|
||||
case ripRel32Minus4:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = (int32_t)*(const little32_t *)fixupContent + 4;
|
||||
return llvm::Error::success();
|
||||
case ripRel32Anon:
|
||||
targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ripRel32Minus1Anon:
|
||||
targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ripRel32Minus2Anon:
|
||||
targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ripRel32Minus4Anon:
|
||||
targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ripRel32GotLoad:
|
||||
case ripRel32Got:
|
||||
case ripRel32Tlv:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = *(const little32_t *)fixupContent;
|
||||
return llvm::Error::success();
|
||||
case tlvInitSectionOffset:
|
||||
case pointer64:
|
||||
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
// If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's
|
||||
// initial value) we need to handle it specially.
|
||||
if (inAtom->contentType() == DefinedAtom::typeThunkTLV &&
|
||||
offsetInAtom == 16) {
|
||||
*kind = tlvInitSectionOffset;
|
||||
assert(*addend == 0 && "TLV-init has non-zero addend?");
|
||||
} else
|
||||
*addend = *(const little64_t *)fixupContent;
|
||||
return llvm::Error::success();
|
||||
case pointer64Anon:
|
||||
targetAddress = *(const little64_t *)fixupContent;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
default:
|
||||
llvm_unreachable("bad reloc kind");
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Error
|
||||
ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
bool scatterable,
|
||||
FindAtomBySectionAndAddress atomFromAddress,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
uint64_t targetAddress;
|
||||
const lld::Atom *fromTarget;
|
||||
if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget))
|
||||
return ec;
|
||||
|
||||
switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength8): {
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent;
|
||||
if (inAtom == fromTarget) {
|
||||
if (inAtom->contentType() == DefinedAtom::typeCFI)
|
||||
*kind = unwindFDEToFunction;
|
||||
else
|
||||
*kind = delta64;
|
||||
*addend = encodedAddend + offsetInAtom;
|
||||
} else if (inAtom == *target) {
|
||||
*kind = negDelta64;
|
||||
*addend = encodedAddend - offsetInAtom;
|
||||
*target = fromTarget;
|
||||
} else
|
||||
return llvm::make_error<GenericError>("Invalid pointer diff");
|
||||
return llvm::Error::success();
|
||||
}
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength4): {
|
||||
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent;
|
||||
if (inAtom == fromTarget) {
|
||||
*kind = delta32;
|
||||
*addend = encodedAddend + offsetInAtom;
|
||||
} else if (inAtom == *target) {
|
||||
*kind = negDelta32;
|
||||
*addend = encodedAddend - offsetInAtom;
|
||||
*target = fromTarget;
|
||||
} else
|
||||
return llvm::make_error<GenericError>("Invalid pointer diff");
|
||||
return llvm::Error::success();
|
||||
}
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rLength8):
|
||||
if (fromTarget != inAtom)
|
||||
return llvm::make_error<GenericError>("pointer diff not in base atom");
|
||||
*kind = delta64Anon;
|
||||
targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent;
|
||||
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rLength4):
|
||||
if (fromTarget != inAtom)
|
||||
return llvm::make_error<GenericError>("pointer diff not in base atom");
|
||||
*kind = delta32Anon;
|
||||
targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent;
|
||||
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
||||
default:
|
||||
return llvm::make_error<GenericError>("unknown pair");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86_64::generateAtomContent(
|
||||
const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
|
||||
FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
|
||||
llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
|
||||
// Copy raw bytes.
|
||||
std::copy(atom.rawContent().begin(), atom.rawContent().end(),
|
||||
atomContentBuffer.begin());
|
||||
// Apply fix-ups.
|
||||
for (const Reference *ref : atom) {
|
||||
uint32_t offset = ref->offsetInAtom();
|
||||
const Atom *target = ref->target();
|
||||
uint64_t targetAddress = 0;
|
||||
if (isa<DefinedAtom>(target))
|
||||
targetAddress = findAddress(*target);
|
||||
uint64_t atomAddress = findAddress(atom);
|
||||
uint64_t fixupAddress = atomAddress + offset;
|
||||
if (relocatable) {
|
||||
applyFixupRelocatable(*ref, &atomContentBuffer[offset],
|
||||
fixupAddress, targetAddress,
|
||||
atomAddress);
|
||||
} else {
|
||||
applyFixupFinal(*ref, &atomContentBuffer[offset],
|
||||
fixupAddress, targetAddress,
|
||||
atomAddress, imageBaseAddress, findSectionAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86_64::applyFixupFinal(
|
||||
const Reference &ref, uint8_t *loc, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress,
|
||||
FindAddressForAtom findSectionAddress) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
||||
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
||||
case branch32:
|
||||
case ripRel32:
|
||||
case ripRel32Anon:
|
||||
case ripRel32Got:
|
||||
case ripRel32GotLoad:
|
||||
case ripRel32Tlv:
|
||||
*loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
|
||||
return;
|
||||
case pointer64:
|
||||
case pointer64Anon:
|
||||
*loc64 = targetAddress + ref.addend();
|
||||
return;
|
||||
case tlvInitSectionOffset:
|
||||
*loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus1:
|
||||
case ripRel32Minus1Anon:
|
||||
*loc32 = targetAddress - (fixupAddress + 5) + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus2:
|
||||
case ripRel32Minus2Anon:
|
||||
*loc32 = targetAddress - (fixupAddress + 6) + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus4:
|
||||
case ripRel32Minus4Anon:
|
||||
*loc32 = targetAddress - (fixupAddress + 8) + ref.addend();
|
||||
return;
|
||||
case delta32:
|
||||
case delta32Anon:
|
||||
*loc32 = targetAddress - fixupAddress + ref.addend();
|
||||
return;
|
||||
case delta64:
|
||||
case delta64Anon:
|
||||
case unwindFDEToFunction:
|
||||
*loc64 = targetAddress - fixupAddress + ref.addend();
|
||||
return;
|
||||
case ripRel32GotLoadNowLea:
|
||||
// Change MOVQ to LEA
|
||||
assert(loc[-2] == 0x8B);
|
||||
loc[-2] = 0x8D;
|
||||
*loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
|
||||
return;
|
||||
case negDelta64:
|
||||
*loc64 = fixupAddress - targetAddress + ref.addend();
|
||||
return;
|
||||
case negDelta32:
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
return;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case lazyPointer:
|
||||
// Do nothing
|
||||
return;
|
||||
case lazyImmediateLocation:
|
||||
*loc32 = ref.addend();
|
||||
return;
|
||||
case imageOffset:
|
||||
case imageOffsetGot:
|
||||
*loc32 = (targetAddress - imageBaseAddress) + ref.addend();
|
||||
return;
|
||||
case unwindInfoToEhFrame: {
|
||||
uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
|
||||
assert(val < 0xffffffU && "offset in __eh_frame too large");
|
||||
*loc32 = (*loc32 & 0xff000000U) | val;
|
||||
return;
|
||||
}
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("invalid x86_64 Reference Kind");
|
||||
}
|
||||
|
||||
void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
|
||||
uint8_t *loc,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
||||
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
||||
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
||||
case branch32:
|
||||
case ripRel32:
|
||||
case ripRel32Got:
|
||||
case ripRel32GotLoad:
|
||||
case ripRel32Tlv:
|
||||
*loc32 = ref.addend();
|
||||
return;
|
||||
case ripRel32Anon:
|
||||
*loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
|
||||
return;
|
||||
case tlvInitSectionOffset:
|
||||
case pointer64:
|
||||
*loc64 = ref.addend();
|
||||
return;
|
||||
case pointer64Anon:
|
||||
*loc64 = targetAddress + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus1:
|
||||
*loc32 = ref.addend() - 1;
|
||||
return;
|
||||
case ripRel32Minus1Anon:
|
||||
*loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus2:
|
||||
*loc32 = ref.addend() - 2;
|
||||
return;
|
||||
case ripRel32Minus2Anon:
|
||||
*loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend();
|
||||
return;
|
||||
case ripRel32Minus4:
|
||||
*loc32 = ref.addend() - 4;
|
||||
return;
|
||||
case ripRel32Minus4Anon:
|
||||
*loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend();
|
||||
return;
|
||||
case delta32:
|
||||
*loc32 = ref.addend() + inAtomAddress - fixupAddress;
|
||||
return;
|
||||
case delta32Anon:
|
||||
// The value we write here should be the delta to the target
|
||||
// after taking in to account the difference from the fixup back to the
|
||||
// last defined label
|
||||
// ie, if we have:
|
||||
// _base: ...
|
||||
// Lfixup: .quad Ltarget - .
|
||||
// ...
|
||||
// Ltarget:
|
||||
//
|
||||
// Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
|
||||
*loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
|
||||
return;
|
||||
case delta64:
|
||||
*loc64 = ref.addend() + inAtomAddress - fixupAddress;
|
||||
return;
|
||||
case delta64Anon:
|
||||
// The value we write here should be the delta to the target
|
||||
// after taking in to account the difference from the fixup back to the
|
||||
// last defined label
|
||||
// ie, if we have:
|
||||
// _base: ...
|
||||
// Lfixup: .quad Ltarget - .
|
||||
// ...
|
||||
// Ltarget:
|
||||
//
|
||||
// Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
|
||||
*loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
|
||||
return;
|
||||
case negDelta64:
|
||||
*loc64 = ref.addend() + fixupAddress - inAtomAddress;
|
||||
return;
|
||||
case negDelta32:
|
||||
*loc32 = ref.addend() + fixupAddress - inAtomAddress;
|
||||
return;
|
||||
case ripRel32GotLoadNowLea:
|
||||
llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
|
||||
return;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
||||
return;
|
||||
case imageOffset:
|
||||
case imageOffsetGot:
|
||||
case unwindInfoToEhFrame:
|
||||
llvm_unreachable("fixup implies __unwind_info");
|
||||
return;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case unwindFDEToFunction:
|
||||
// Do nothing for now
|
||||
return;
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unknown x86_64 Reference Kind");
|
||||
}
|
||||
|
||||
void ArchHandler_x86_64::appendSectionRelocations(
|
||||
const DefinedAtom &atom,
|
||||
uint64_t atomSectionOffset,
|
||||
const Reference &ref,
|
||||
FindSymbolIndexForAtom symbolIndexForAtom,
|
||||
FindSectionIndexForAtom sectionIndexForAtom,
|
||||
FindAddressForAtom addressForAtom,
|
||||
normalized::Relocations &relocs) {
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
||||
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
||||
case modeCode:
|
||||
case modeData:
|
||||
return;
|
||||
case branch32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
|
||||
return;
|
||||
case ripRel32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32Anon:
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED | rPcRel | rLength4 );
|
||||
return;
|
||||
case ripRel32Got:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32GotLoad:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32Tlv:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case tlvInitSectionOffset:
|
||||
case pointer64:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength8);
|
||||
return;
|
||||
case pointer64Anon:
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rLength8);
|
||||
return;
|
||||
case ripRel32Minus1:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32Minus1Anon:
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 );
|
||||
return;
|
||||
case ripRel32Minus2:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32Minus2Anon:
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 );
|
||||
return;
|
||||
case ripRel32Minus4:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 );
|
||||
return;
|
||||
case ripRel32Minus4Anon:
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 );
|
||||
return;
|
||||
case delta32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
|
||||
return;
|
||||
case delta32Anon:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rLength4 );
|
||||
return;
|
||||
case delta64:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
|
||||
return;
|
||||
case delta64Anon:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
||||
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_UNSIGNED | rLength8 );
|
||||
return;
|
||||
case unwindFDEToFunction:
|
||||
case unwindInfoToEhFrame:
|
||||
return;
|
||||
case negDelta32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
|
||||
return;
|
||||
case negDelta64:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
|
||||
return;
|
||||
case ripRel32GotLoadNowLea:
|
||||
llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
|
||||
return;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
||||
return;
|
||||
case imageOffset:
|
||||
case imageOffsetGot:
|
||||
llvm_unreachable("__unwind_info references should have been resolved");
|
||||
return;
|
||||
case invalid:
|
||||
// Fall into llvm_unreachable().
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unknown x86_64 Reference Kind");
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() {
|
||||
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64());
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -1,180 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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_ATOMS_H
|
||||
#define LLD_READER_WRITER_MACHO_ATOMS_H
|
||||
|
||||
#include "lld/Core/Atom.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace lld {
|
||||
|
||||
class File;
|
||||
|
||||
namespace mach_o {
|
||||
|
||||
class MachODefinedAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
MachODefinedAtom(const File &f, const StringRef name, Scope scope,
|
||||
ContentType type, Merge merge, bool thumb, bool noDeadStrip,
|
||||
const ArrayRef<uint8_t> content, Alignment align)
|
||||
: SimpleDefinedAtom(f), _name(name), _content(content),
|
||||
_align(align), _contentType(type), _scope(scope), _merge(merge),
|
||||
_thumb(thumb), _noDeadStrip(noDeadStrip) {}
|
||||
|
||||
// Constructor for zero-fill content
|
||||
MachODefinedAtom(const File &f, const StringRef name, Scope scope,
|
||||
ContentType type, uint64_t size, bool noDeadStrip,
|
||||
Alignment align)
|
||||
: SimpleDefinedAtom(f), _name(name),
|
||||
_content(ArrayRef<uint8_t>(nullptr, size)), _align(align),
|
||||
_contentType(type), _scope(scope), _merge(mergeNo), _thumb(false),
|
||||
_noDeadStrip(noDeadStrip) {}
|
||||
|
||||
~MachODefinedAtom() override = default;
|
||||
|
||||
uint64_t size() const override { return _content.size(); }
|
||||
|
||||
ContentType contentType() const override { return _contentType; }
|
||||
|
||||
Alignment alignment() const override { return _align; }
|
||||
|
||||
StringRef name() const override { return _name; }
|
||||
|
||||
Scope scope() const override { return _scope; }
|
||||
|
||||
Merge merge() const override { return _merge; }
|
||||
|
||||
DeadStripKind deadStrip() const override {
|
||||
if (_contentType == DefinedAtom::typeInitializerPtr)
|
||||
return deadStripNever;
|
||||
if (_contentType == DefinedAtom::typeTerminatorPtr)
|
||||
return deadStripNever;
|
||||
if (_noDeadStrip)
|
||||
return deadStripNever;
|
||||
return deadStripNormal;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
// Note: Zerofill atoms have a content pointer which is null.
|
||||
return _content;
|
||||
}
|
||||
|
||||
bool isThumb() const { return _thumb; }
|
||||
|
||||
private:
|
||||
const StringRef _name;
|
||||
const ArrayRef<uint8_t> _content;
|
||||
const DefinedAtom::Alignment _align;
|
||||
const ContentType _contentType;
|
||||
const Scope _scope;
|
||||
const Merge _merge;
|
||||
const bool _thumb;
|
||||
const bool _noDeadStrip;
|
||||
};
|
||||
|
||||
class MachODefinedCustomSectionAtom : public MachODefinedAtom {
|
||||
public:
|
||||
MachODefinedCustomSectionAtom(const File &f, const StringRef name,
|
||||
Scope scope, ContentType type, Merge merge,
|
||||
bool thumb, bool noDeadStrip,
|
||||
const ArrayRef<uint8_t> content,
|
||||
StringRef sectionName, Alignment align)
|
||||
: MachODefinedAtom(f, name, scope, type, merge, thumb, noDeadStrip,
|
||||
content, align),
|
||||
_sectionName(sectionName) {}
|
||||
|
||||
~MachODefinedCustomSectionAtom() override = default;
|
||||
|
||||
SectionChoice sectionChoice() const override {
|
||||
return DefinedAtom::sectionCustomRequired;
|
||||
}
|
||||
|
||||
StringRef customSectionName() const override {
|
||||
return _sectionName;
|
||||
}
|
||||
private:
|
||||
StringRef _sectionName;
|
||||
};
|
||||
|
||||
class MachOTentativeDefAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope,
|
||||
uint64_t size, DefinedAtom::Alignment align)
|
||||
: SimpleDefinedAtom(f), _name(std::string(name)), _scope(scope),
|
||||
_size(size), _align(align) {}
|
||||
|
||||
~MachOTentativeDefAtom() override = default;
|
||||
|
||||
uint64_t size() const override { return _size; }
|
||||
|
||||
Merge merge() const override { return DefinedAtom::mergeAsTentative; }
|
||||
|
||||
ContentType contentType() const override { return DefinedAtom::typeZeroFill; }
|
||||
|
||||
Alignment alignment() const override { return _align; }
|
||||
|
||||
StringRef name() const override { return _name; }
|
||||
|
||||
Scope scope() const override { return _scope; }
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); }
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
const Scope _scope;
|
||||
const uint64_t _size;
|
||||
const DefinedAtom::Alignment _align;
|
||||
};
|
||||
|
||||
class MachOSharedLibraryAtom : public SharedLibraryAtom {
|
||||
public:
|
||||
MachOSharedLibraryAtom(const File &file, StringRef name,
|
||||
StringRef dylibInstallName, bool weakDef)
|
||||
: SharedLibraryAtom(), _file(file), _name(name),
|
||||
_dylibInstallName(dylibInstallName) {}
|
||||
~MachOSharedLibraryAtom() override = default;
|
||||
|
||||
StringRef loadName() const override { return _dylibInstallName; }
|
||||
|
||||
bool canBeNullAtRuntime() const override {
|
||||
// FIXME: this may actually be changeable. For now, all symbols are strongly
|
||||
// defined though.
|
||||
return false;
|
||||
}
|
||||
|
||||
const File &file() const override { return _file; }
|
||||
|
||||
StringRef name() const override { return _name; }
|
||||
|
||||
Type type() const override {
|
||||
// Unused in MachO (I think).
|
||||
return Type::Unknown;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
// Unused in MachO (I think)
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const File &_file;
|
||||
StringRef _name;
|
||||
StringRef _dylibInstallName;
|
||||
};
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_ATOMS_H
|
|
@ -1,36 +0,0 @@
|
|||
add_lld_library(lldMachOOld
|
||||
ArchHandler.cpp
|
||||
ArchHandler_arm.cpp
|
||||
ArchHandler_arm64.cpp
|
||||
ArchHandler_x86.cpp
|
||||
ArchHandler_x86_64.cpp
|
||||
CompactUnwindPass.cpp
|
||||
GOTPass.cpp
|
||||
LayoutPass.cpp
|
||||
MachOLinkingContext.cpp
|
||||
MachONormalizedFileBinaryReader.cpp
|
||||
MachONormalizedFileBinaryWriter.cpp
|
||||
MachONormalizedFileFromAtoms.cpp
|
||||
MachONormalizedFileToAtoms.cpp
|
||||
MachONormalizedFileYAML.cpp
|
||||
ObjCPass.cpp
|
||||
ShimPass.cpp
|
||||
StubsPass.cpp
|
||||
TLVPass.cpp
|
||||
WriterMachO.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
DebugInfoDWARF
|
||||
Demangle
|
||||
Object
|
||||
Support
|
||||
TextAPI
|
||||
|
||||
LINK_LIBS
|
||||
lldCommon
|
||||
lldCore
|
||||
lldYAML
|
||||
${LLVM_PTHREAD_LIB}
|
||||
)
|
||||
|
||||
include_directories(.)
|
|
@ -1,580 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file A pass to convert MachO's __compact_unwind sections into the final
|
||||
/// __unwind_info format used during runtime. See
|
||||
/// mach-o/compact_unwind_encoding.h for more details on the formats involved.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
||||
#define DEBUG_TYPE "macho-compact-unwind"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
namespace {
|
||||
struct CompactUnwindEntry {
|
||||
const Atom *rangeStart;
|
||||
const Atom *personalityFunction;
|
||||
const Atom *lsdaLocation;
|
||||
const Atom *ehFrame;
|
||||
|
||||
uint32_t rangeLength;
|
||||
|
||||
// There are 3 types of compact unwind entry, distinguished by the encoding
|
||||
// value: 0 indicates a function with no unwind info;
|
||||
// _archHandler.dwarfCompactUnwindType() indicates that the entry defers to
|
||||
// __eh_frame, and that the ehFrame entry will be valid; any other value is a
|
||||
// real compact unwind entry -- personalityFunction will be set and
|
||||
// lsdaLocation may be.
|
||||
uint32_t encoding;
|
||||
|
||||
CompactUnwindEntry(const DefinedAtom *function)
|
||||
: rangeStart(function), personalityFunction(nullptr),
|
||||
lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(function->size()),
|
||||
encoding(0) {}
|
||||
|
||||
CompactUnwindEntry()
|
||||
: rangeStart(nullptr), personalityFunction(nullptr),
|
||||
lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(0), encoding(0) {}
|
||||
};
|
||||
|
||||
struct UnwindInfoPage {
|
||||
ArrayRef<CompactUnwindEntry> entries;
|
||||
};
|
||||
}
|
||||
|
||||
class UnwindInfoAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
UnwindInfoAtom(ArchHandler &archHandler, const File &file, bool isBig,
|
||||
std::vector<const Atom *> &personalities,
|
||||
std::vector<uint32_t> &commonEncodings,
|
||||
std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs)
|
||||
: SimpleDefinedAtom(file), _archHandler(archHandler),
|
||||
_commonEncodingsOffset(7 * sizeof(uint32_t)),
|
||||
_personalityArrayOffset(_commonEncodingsOffset +
|
||||
commonEncodings.size() * sizeof(uint32_t)),
|
||||
_topLevelIndexOffset(_personalityArrayOffset +
|
||||
personalities.size() * sizeof(uint32_t)),
|
||||
_lsdaIndexOffset(_topLevelIndexOffset +
|
||||
3 * (pages.size() + 1) * sizeof(uint32_t)),
|
||||
_firstPageOffset(_lsdaIndexOffset + 2 * numLSDAs * sizeof(uint32_t)),
|
||||
_isBig(isBig) {
|
||||
|
||||
addHeader(commonEncodings.size(), personalities.size(), pages.size());
|
||||
addCommonEncodings(commonEncodings);
|
||||
addPersonalityFunctions(personalities);
|
||||
addTopLevelIndexes(pages);
|
||||
addLSDAIndexes(pages, numLSDAs);
|
||||
addSecondLevelPages(pages);
|
||||
}
|
||||
|
||||
~UnwindInfoAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeProcessedUnwindInfo;
|
||||
}
|
||||
|
||||
Alignment alignment() const override { return 4; }
|
||||
|
||||
uint64_t size() const override { return _contents.size(); }
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR__;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override { return _contents; }
|
||||
|
||||
void addHeader(uint32_t numCommon, uint32_t numPersonalities,
|
||||
uint32_t numPages) {
|
||||
using normalized::write32;
|
||||
|
||||
uint32_t headerSize = 7 * sizeof(uint32_t);
|
||||
_contents.resize(headerSize);
|
||||
|
||||
uint8_t *headerEntries = _contents.data();
|
||||
// version
|
||||
write32(headerEntries, 1, _isBig);
|
||||
// commonEncodingsArraySectionOffset
|
||||
write32(headerEntries + sizeof(uint32_t), _commonEncodingsOffset, _isBig);
|
||||
// commonEncodingsArrayCount
|
||||
write32(headerEntries + 2 * sizeof(uint32_t), numCommon, _isBig);
|
||||
// personalityArraySectionOffset
|
||||
write32(headerEntries + 3 * sizeof(uint32_t), _personalityArrayOffset,
|
||||
_isBig);
|
||||
// personalityArrayCount
|
||||
write32(headerEntries + 4 * sizeof(uint32_t), numPersonalities, _isBig);
|
||||
// indexSectionOffset
|
||||
write32(headerEntries + 5 * sizeof(uint32_t), _topLevelIndexOffset, _isBig);
|
||||
// indexCount
|
||||
write32(headerEntries + 6 * sizeof(uint32_t), numPages + 1, _isBig);
|
||||
}
|
||||
|
||||
/// Add the list of common encodings to the section; this is simply an array
|
||||
/// of uint32_t compact values. Size has already been specified in the header.
|
||||
void addCommonEncodings(std::vector<uint32_t> &commonEncodings) {
|
||||
using normalized::write32;
|
||||
|
||||
_contents.resize(_commonEncodingsOffset +
|
||||
commonEncodings.size() * sizeof(uint32_t));
|
||||
uint8_t *commonEncodingsArea =
|
||||
reinterpret_cast<uint8_t *>(_contents.data() + _commonEncodingsOffset);
|
||||
|
||||
for (uint32_t encoding : commonEncodings) {
|
||||
write32(commonEncodingsArea, encoding, _isBig);
|
||||
commonEncodingsArea += sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
void addPersonalityFunctions(std::vector<const Atom *> personalities) {
|
||||
_contents.resize(_personalityArrayOffset +
|
||||
personalities.size() * sizeof(uint32_t));
|
||||
|
||||
for (unsigned i = 0; i < personalities.size(); ++i)
|
||||
addImageReferenceIndirect(_personalityArrayOffset + i * sizeof(uint32_t),
|
||||
personalities[i]);
|
||||
}
|
||||
|
||||
void addTopLevelIndexes(std::vector<UnwindInfoPage> &pages) {
|
||||
using normalized::write32;
|
||||
|
||||
uint32_t numIndexes = pages.size() + 1;
|
||||
_contents.resize(_topLevelIndexOffset + numIndexes * 3 * sizeof(uint32_t));
|
||||
|
||||
uint32_t pageLoc = _firstPageOffset;
|
||||
|
||||
// The most difficult job here is calculating the LSDAs; everything else
|
||||
// follows fairly naturally, but we can't state where the first
|
||||
uint8_t *indexData = &_contents[_topLevelIndexOffset];
|
||||
uint32_t numLSDAs = 0;
|
||||
for (unsigned i = 0; i < pages.size(); ++i) {
|
||||
// functionOffset
|
||||
addImageReference(_topLevelIndexOffset + 3 * i * sizeof(uint32_t),
|
||||
pages[i].entries[0].rangeStart);
|
||||
// secondLevelPagesSectionOffset
|
||||
write32(indexData + (3 * i + 1) * sizeof(uint32_t), pageLoc, _isBig);
|
||||
write32(indexData + (3 * i + 2) * sizeof(uint32_t),
|
||||
_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
|
||||
|
||||
for (auto &entry : pages[i].entries)
|
||||
if (entry.lsdaLocation)
|
||||
++numLSDAs;
|
||||
}
|
||||
|
||||
// Finally, write out the final sentinel index
|
||||
auto &finalEntry = pages[pages.size() - 1].entries.back();
|
||||
addImageReference(_topLevelIndexOffset +
|
||||
3 * pages.size() * sizeof(uint32_t),
|
||||
finalEntry.rangeStart, finalEntry.rangeLength);
|
||||
// secondLevelPagesSectionOffset => 0
|
||||
write32(indexData + (3 * pages.size() + 2) * sizeof(uint32_t),
|
||||
_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
|
||||
}
|
||||
|
||||
void addLSDAIndexes(std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) {
|
||||
_contents.resize(_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t));
|
||||
|
||||
uint32_t curOffset = _lsdaIndexOffset;
|
||||
for (auto &page : pages) {
|
||||
for (auto &entry : page.entries) {
|
||||
if (!entry.lsdaLocation)
|
||||
continue;
|
||||
|
||||
addImageReference(curOffset, entry.rangeStart);
|
||||
addImageReference(curOffset + sizeof(uint32_t), entry.lsdaLocation);
|
||||
curOffset += 2 * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addSecondLevelPages(std::vector<UnwindInfoPage> &pages) {
|
||||
for (auto &page : pages) {
|
||||
addRegularSecondLevelPage(page);
|
||||
}
|
||||
}
|
||||
|
||||
void addRegularSecondLevelPage(const UnwindInfoPage &page) {
|
||||
uint32_t curPageOffset = _contents.size();
|
||||
const int16_t headerSize = sizeof(uint32_t) + 2 * sizeof(uint16_t);
|
||||
uint32_t curPageSize =
|
||||
headerSize + 2 * page.entries.size() * sizeof(uint32_t);
|
||||
_contents.resize(curPageOffset + curPageSize);
|
||||
|
||||
using normalized::write32;
|
||||
using normalized::write16;
|
||||
// 2 => regular page
|
||||
write32(&_contents[curPageOffset], 2, _isBig);
|
||||
// offset of 1st entry
|
||||
write16(&_contents[curPageOffset + 4], headerSize, _isBig);
|
||||
write16(&_contents[curPageOffset + 6], page.entries.size(), _isBig);
|
||||
|
||||
uint32_t pagePos = curPageOffset + headerSize;
|
||||
for (auto &entry : page.entries) {
|
||||
addImageReference(pagePos, entry.rangeStart);
|
||||
|
||||
write32(_contents.data() + pagePos + sizeof(uint32_t), entry.encoding,
|
||||
_isBig);
|
||||
if ((entry.encoding & 0x0f000000U) ==
|
||||
_archHandler.dwarfCompactUnwindType())
|
||||
addEhFrameReference(pagePos + sizeof(uint32_t), entry.ehFrame);
|
||||
|
||||
pagePos += 2 * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
|
||||
void addEhFrameReference(uint32_t offset, const Atom *dest,
|
||||
Reference::Addend addend = 0) {
|
||||
addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
|
||||
_archHandler.unwindRefToEhFrameKind(), offset, dest, addend);
|
||||
}
|
||||
|
||||
void addImageReference(uint32_t offset, const Atom *dest,
|
||||
Reference::Addend addend = 0) {
|
||||
addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
|
||||
_archHandler.imageOffsetKind(), offset, dest, addend);
|
||||
}
|
||||
|
||||
void addImageReferenceIndirect(uint32_t offset, const Atom *dest) {
|
||||
addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
|
||||
_archHandler.imageOffsetKindIndirect(), offset, dest, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
std::vector<uint8_t> _contents;
|
||||
uint32_t _commonEncodingsOffset;
|
||||
uint32_t _personalityArrayOffset;
|
||||
uint32_t _topLevelIndexOffset;
|
||||
uint32_t _lsdaIndexOffset;
|
||||
uint32_t _firstPageOffset;
|
||||
bool _isBig;
|
||||
};
|
||||
|
||||
/// Pass for instantiating and optimizing GOT slots.
|
||||
///
|
||||
class CompactUnwindPass : public Pass {
|
||||
public:
|
||||
CompactUnwindPass(const MachOLinkingContext &context)
|
||||
: _ctx(context), _archHandler(_ctx.archHandler()),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o Compact Unwind Pass>")),
|
||||
_isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
LLVM_DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n");
|
||||
|
||||
std::map<const Atom *, CompactUnwindEntry> unwindLocs;
|
||||
std::map<const Atom *, const Atom *> dwarfFrames;
|
||||
std::vector<const Atom *> personalities;
|
||||
uint32_t numLSDAs = 0;
|
||||
|
||||
// First collect all __compact_unwind and __eh_frame entries, addressable by
|
||||
// the function referred to.
|
||||
collectCompactUnwindEntries(mergedFile, unwindLocs, personalities,
|
||||
numLSDAs);
|
||||
|
||||
collectDwarfFrameEntries(mergedFile, dwarfFrames);
|
||||
|
||||
// Skip rest of pass if no unwind info.
|
||||
if (unwindLocs.empty() && dwarfFrames.empty())
|
||||
return llvm::Error::success();
|
||||
|
||||
// FIXME: if there are more than 4 personality functions then we need to
|
||||
// defer to DWARF info for the ones we don't put in the list. They should
|
||||
// also probably be sorted by frequency.
|
||||
assert(personalities.size() <= 4);
|
||||
|
||||
// TODO: Find common encodings for use by compressed pages.
|
||||
std::vector<uint32_t> commonEncodings;
|
||||
|
||||
// Now sort the entries by final address and fixup the compact encoding to
|
||||
// its final form (i.e. set personality function bits & create DWARF
|
||||
// references where needed).
|
||||
std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries(
|
||||
mergedFile, unwindLocs, personalities, dwarfFrames);
|
||||
|
||||
// Remove any unused eh-frame atoms.
|
||||
pruneUnusedEHFrames(mergedFile, unwindInfos, unwindLocs, dwarfFrames);
|
||||
|
||||
// Finally, we can start creating pages based on these entries.
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << " Splitting entries into pages\n");
|
||||
// FIXME: we split the entries into pages naively: lots of 4k pages followed
|
||||
// by a small one. ld64 tried to minimize space and align them to real 4k
|
||||
// boundaries. That might be worth doing, or perhaps we could perform some
|
||||
// minor balancing for expected number of lookups.
|
||||
std::vector<UnwindInfoPage> pages;
|
||||
auto remainingInfos = llvm::makeArrayRef(unwindInfos);
|
||||
do {
|
||||
pages.push_back(UnwindInfoPage());
|
||||
|
||||
// FIXME: we only create regular pages at the moment. These can hold up to
|
||||
// 1021 entries according to the documentation.
|
||||
unsigned entriesInPage = std::min(1021U, (unsigned)remainingInfos.size());
|
||||
|
||||
pages.back().entries = remainingInfos.slice(0, entriesInPage);
|
||||
remainingInfos = remainingInfos.slice(entriesInPage);
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " Page from "
|
||||
<< pages.back().entries[0].rangeStart->name() << " to "
|
||||
<< pages.back().entries.back().rangeStart->name() << " + "
|
||||
<< llvm::format("0x%x",
|
||||
pages.back().entries.back().rangeLength)
|
||||
<< " has " << entriesInPage << " entries\n");
|
||||
} while (!remainingInfos.empty());
|
||||
|
||||
auto *unwind = new (_file.allocator())
|
||||
UnwindInfoAtom(_archHandler, _file, _isBig, personalities,
|
||||
commonEncodings, pages, numLSDAs);
|
||||
mergedFile.addAtom(*unwind);
|
||||
|
||||
// Finally, remove all __compact_unwind atoms now that we've processed them.
|
||||
mergedFile.removeDefinedAtomsIf([](const DefinedAtom *atom) {
|
||||
return atom->contentType() == DefinedAtom::typeCompactUnwindInfo;
|
||||
});
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
void collectCompactUnwindEntries(
|
||||
const SimpleFile &mergedFile,
|
||||
std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
|
||||
std::vector<const Atom *> &personalities, uint32_t &numLSDAs) {
|
||||
LLVM_DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n");
|
||||
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo)
|
||||
continue;
|
||||
|
||||
auto unwindEntry = extractCompactUnwindEntry(atom);
|
||||
unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry));
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << " Entry for "
|
||||
<< unwindEntry.rangeStart->name() << ", encoding="
|
||||
<< llvm::format("0x%08x", unwindEntry.encoding));
|
||||
if (unwindEntry.personalityFunction)
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< ", personality="
|
||||
<< unwindEntry.personalityFunction->name()
|
||||
<< ", lsdaLoc=" << unwindEntry.lsdaLocation->name());
|
||||
LLVM_DEBUG(llvm::dbgs() << '\n');
|
||||
|
||||
// Count number of LSDAs we see, since we need to know how big the index
|
||||
// will be while laying out the section.
|
||||
if (unwindEntry.lsdaLocation)
|
||||
++numLSDAs;
|
||||
|
||||
// Gather the personality functions now, so that they're in deterministic
|
||||
// order (derived from the DefinedAtom order).
|
||||
if (unwindEntry.personalityFunction &&
|
||||
!llvm::count(personalities, unwindEntry.personalityFunction))
|
||||
personalities.push_back(unwindEntry.personalityFunction);
|
||||
}
|
||||
}
|
||||
|
||||
CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) {
|
||||
CompactUnwindEntry entry;
|
||||
|
||||
for (const Reference *ref : *atom) {
|
||||
switch (ref->offsetInAtom()) {
|
||||
case 0:
|
||||
// FIXME: there could legitimately be functions with multiple encoding
|
||||
// entries. However, nothing produces them at the moment.
|
||||
assert(ref->addend() == 0 && "unexpected offset into function");
|
||||
entry.rangeStart = ref->target();
|
||||
break;
|
||||
case 0x10:
|
||||
assert(ref->addend() == 0 && "unexpected offset into personality fn");
|
||||
entry.personalityFunction = ref->target();
|
||||
break;
|
||||
case 0x18:
|
||||
assert(ref->addend() == 0 && "unexpected offset into LSDA atom");
|
||||
entry.lsdaLocation = ref->target();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (atom->rawContent().size() < 4 * sizeof(uint32_t))
|
||||
return entry;
|
||||
|
||||
using normalized::read32;
|
||||
entry.rangeLength =
|
||||
read32(atom->rawContent().data() + 2 * sizeof(uint32_t), _isBig);
|
||||
entry.encoding =
|
||||
read32(atom->rawContent().data() + 3 * sizeof(uint32_t), _isBig);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
collectDwarfFrameEntries(const SimpleFile &mergedFile,
|
||||
std::map<const Atom *, const Atom *> &dwarfFrames) {
|
||||
for (const DefinedAtom *ehFrameAtom : mergedFile.defined()) {
|
||||
if (ehFrameAtom->contentType() != DefinedAtom::typeCFI)
|
||||
continue;
|
||||
if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom))
|
||||
continue;
|
||||
|
||||
if (const Atom *function = _archHandler.fdeTargetFunction(ehFrameAtom))
|
||||
dwarfFrames[function] = ehFrameAtom;
|
||||
}
|
||||
}
|
||||
|
||||
/// Every atom defined in __TEXT,__text needs an entry in the final
|
||||
/// __unwind_info section (in order). These comes from two sources:
|
||||
/// + Input __compact_unwind sections where possible (after adding the
|
||||
/// personality function offset which is only known now).
|
||||
/// + A synthesised reference to __eh_frame if there's no __compact_unwind
|
||||
/// or too many personality functions to be accommodated.
|
||||
std::vector<CompactUnwindEntry> createUnwindInfoEntries(
|
||||
const SimpleFile &mergedFile,
|
||||
const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
|
||||
const std::vector<const Atom *> &personalities,
|
||||
const std::map<const Atom *, const Atom *> &dwarfFrames) {
|
||||
std::vector<CompactUnwindEntry> unwindInfos;
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n");
|
||||
// The final order in the __unwind_info section must be derived from the
|
||||
// order of typeCode atoms, since that's how they'll be put into the object
|
||||
// file eventually (yuck!).
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
if (atom->contentType() != DefinedAtom::typeCode)
|
||||
continue;
|
||||
|
||||
unwindInfos.push_back(finalizeUnwindInfoEntryForAtom(
|
||||
atom, unwindLocs, personalities, dwarfFrames));
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " Entry for " << atom->name() << ", final encoding="
|
||||
<< llvm::format("0x%08x", unwindInfos.back().encoding)
|
||||
<< '\n');
|
||||
}
|
||||
|
||||
return unwindInfos;
|
||||
}
|
||||
|
||||
/// Remove unused EH frames.
|
||||
///
|
||||
/// An EH frame is considered unused if there is a corresponding compact
|
||||
/// unwind atom that doesn't require the EH frame.
|
||||
void pruneUnusedEHFrames(
|
||||
SimpleFile &mergedFile,
|
||||
const std::vector<CompactUnwindEntry> &unwindInfos,
|
||||
const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
|
||||
const std::map<const Atom *, const Atom *> &dwarfFrames) {
|
||||
|
||||
// Worklist of all 'used' FDEs.
|
||||
std::vector<const DefinedAtom *> usedDwarfWorklist;
|
||||
|
||||
// We have to check two conditions when building the worklist:
|
||||
// (1) EH frames used by compact unwind entries.
|
||||
for (auto &entry : unwindInfos)
|
||||
if (entry.ehFrame)
|
||||
usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.ehFrame));
|
||||
|
||||
// (2) EH frames that reference functions with no corresponding compact
|
||||
// unwind info.
|
||||
for (auto &entry : dwarfFrames)
|
||||
if (!unwindLocs.count(entry.first))
|
||||
usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.second));
|
||||
|
||||
// Add all transitively referenced CFI atoms by processing the worklist.
|
||||
std::set<const Atom *> usedDwarfFrames;
|
||||
while (!usedDwarfWorklist.empty()) {
|
||||
const DefinedAtom *cfiAtom = usedDwarfWorklist.back();
|
||||
usedDwarfWorklist.pop_back();
|
||||
usedDwarfFrames.insert(cfiAtom);
|
||||
for (const auto *ref : *cfiAtom) {
|
||||
const DefinedAtom *cfiTarget = dyn_cast<DefinedAtom>(ref->target());
|
||||
if (cfiTarget->contentType() == DefinedAtom::typeCFI)
|
||||
usedDwarfWorklist.push_back(cfiTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, delete all unreferenced CFI atoms.
|
||||
mergedFile.removeDefinedAtomsIf([&](const DefinedAtom *atom) {
|
||||
if ((atom->contentType() == DefinedAtom::typeCFI) &&
|
||||
!usedDwarfFrames.count(atom))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
CompactUnwindEntry finalizeUnwindInfoEntryForAtom(
|
||||
const DefinedAtom *function,
|
||||
const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
|
||||
const std::vector<const Atom *> &personalities,
|
||||
const std::map<const Atom *, const Atom *> &dwarfFrames) {
|
||||
auto unwindLoc = unwindLocs.find(function);
|
||||
|
||||
CompactUnwindEntry entry;
|
||||
if (unwindLoc == unwindLocs.end()) {
|
||||
// Default entry has correct encoding (0 => no unwind), but we need to
|
||||
// synthesise the function.
|
||||
entry.rangeStart = function;
|
||||
entry.rangeLength = function->size();
|
||||
} else
|
||||
entry = unwindLoc->second;
|
||||
|
||||
|
||||
// If there's no __compact_unwind entry, or it explicitly says to use
|
||||
// __eh_frame, we need to try and fill in the correct DWARF atom.
|
||||
if (entry.encoding == _archHandler.dwarfCompactUnwindType() ||
|
||||
entry.encoding == 0) {
|
||||
auto dwarfFrame = dwarfFrames.find(function);
|
||||
if (dwarfFrame != dwarfFrames.end()) {
|
||||
entry.encoding = _archHandler.dwarfCompactUnwindType();
|
||||
entry.ehFrame = dwarfFrame->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto personality = llvm::find(personalities, entry.personalityFunction);
|
||||
uint32_t personalityIdx = personality == personalities.end()
|
||||
? 0
|
||||
: personality - personalities.begin() + 1;
|
||||
|
||||
// FIXME: We should also use DWARF when there isn't enough room for the
|
||||
// personality function in the compact encoding.
|
||||
assert(personalityIdx < 4 && "too many personality functions");
|
||||
|
||||
entry.encoding |= personalityIdx << 28;
|
||||
|
||||
if (entry.lsdaLocation)
|
||||
entry.encoding |= 1U << 30;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
MachOFile &_file;
|
||||
bool _isBig;
|
||||
};
|
||||
|
||||
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsCompactUnwindPass());
|
||||
pm.add(std::make_unique<CompactUnwindPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,106 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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_DEBUGINFO_H
|
||||
#define LLD_READER_WRITER_MACHO_DEBUGINFO_H
|
||||
|
||||
#include "lld/Core/Atom.h"
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class DebugInfo {
|
||||
public:
|
||||
enum class Kind {
|
||||
Dwarf,
|
||||
Stabs
|
||||
};
|
||||
|
||||
Kind kind() const { return _kind; }
|
||||
|
||||
void setAllocator(std::unique_ptr<llvm::BumpPtrAllocator> allocator) {
|
||||
_allocator = std::move(allocator);
|
||||
}
|
||||
|
||||
protected:
|
||||
DebugInfo(Kind kind) : _kind(kind) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::BumpPtrAllocator> _allocator;
|
||||
Kind _kind;
|
||||
};
|
||||
|
||||
struct TranslationUnitSource {
|
||||
StringRef name;
|
||||
StringRef path;
|
||||
};
|
||||
|
||||
class DwarfDebugInfo : public DebugInfo {
|
||||
public:
|
||||
DwarfDebugInfo(TranslationUnitSource tu)
|
||||
: DebugInfo(Kind::Dwarf), _tu(std::move(tu)) {}
|
||||
|
||||
static inline bool classof(const DebugInfo *di) {
|
||||
return di->kind() == Kind::Dwarf;
|
||||
}
|
||||
|
||||
const TranslationUnitSource &translationUnitSource() const { return _tu; }
|
||||
|
||||
private:
|
||||
TranslationUnitSource _tu;
|
||||
};
|
||||
|
||||
struct Stab {
|
||||
Stab(const Atom* atom, uint8_t type, uint8_t other, uint16_t desc,
|
||||
uint32_t value, StringRef str)
|
||||
: atom(atom), type(type), other(other), desc(desc), value(value),
|
||||
str(str) {}
|
||||
|
||||
const class Atom* atom;
|
||||
uint8_t type;
|
||||
uint8_t other;
|
||||
uint16_t desc;
|
||||
uint32_t value;
|
||||
StringRef str;
|
||||
};
|
||||
|
||||
inline raw_ostream& operator<<(raw_ostream &os, Stab &s) {
|
||||
os << "Stab -- atom: " << llvm::format("%p", s.atom) << ", type: " << (uint32_t)s.type
|
||||
<< ", other: " << (uint32_t)s.other << ", desc: " << s.desc << ", value: " << s.value
|
||||
<< ", str: '" << s.str << "'";
|
||||
return os;
|
||||
}
|
||||
|
||||
class StabsDebugInfo : public DebugInfo {
|
||||
public:
|
||||
|
||||
typedef std::vector<Stab> StabsList;
|
||||
|
||||
StabsDebugInfo(StabsList stabs)
|
||||
: DebugInfo(Kind::Stabs), _stabs(std::move(stabs)) {}
|
||||
|
||||
static inline bool classof(const DebugInfo *di) {
|
||||
return di->kind() == Kind::Stabs;
|
||||
}
|
||||
|
||||
const StabsList& stabs() const { return _stabs; }
|
||||
|
||||
public:
|
||||
StabsList _stabs;
|
||||
};
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_DEBUGINFO_H
|
|
@ -1,154 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/ExecutableAtoms.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_EXECUTABLE_ATOMS_H
|
||||
#define LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
|
||||
|
||||
#include "Atoms.h"
|
||||
#include "File.h"
|
||||
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/LinkingContext.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/Core/UndefinedAtom.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
//
|
||||
// CEntryFile adds an UndefinedAtom for "_main" so that the Resolving
|
||||
// phase will fail if "_main" is undefined.
|
||||
//
|
||||
class CEntryFile : public SimpleFile {
|
||||
public:
|
||||
CEntryFile(const MachOLinkingContext &context)
|
||||
: SimpleFile("C entry", kindCEntryObject),
|
||||
_undefMain(*this, context.entrySymbolName()) {
|
||||
this->addAtom(_undefMain);
|
||||
}
|
||||
|
||||
private:
|
||||
SimpleUndefinedAtom _undefMain;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// StubHelperFile adds an UndefinedAtom for "dyld_stub_binder" so that
|
||||
// the Resolveing phase will fail if "dyld_stub_binder" is undefined.
|
||||
//
|
||||
class StubHelperFile : public SimpleFile {
|
||||
public:
|
||||
StubHelperFile(const MachOLinkingContext &context)
|
||||
: SimpleFile("stub runtime", kindStubHelperObject),
|
||||
_undefBinder(*this, context.binderSymbolName()) {
|
||||
this->addAtom(_undefBinder);
|
||||
}
|
||||
|
||||
private:
|
||||
SimpleUndefinedAtom _undefBinder;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// MachHeaderAliasFile lazily instantiates the magic symbols that mark the start
|
||||
// of the mach_header for final linked images.
|
||||
//
|
||||
class MachHeaderAliasFile : public SimpleFile {
|
||||
public:
|
||||
MachHeaderAliasFile(const MachOLinkingContext &context)
|
||||
: SimpleFile("mach_header symbols", kindHeaderObject) {
|
||||
StringRef machHeaderSymbolName;
|
||||
DefinedAtom::Scope symbolScope = DefinedAtom::scopeLinkageUnit;
|
||||
StringRef dsoHandleName;
|
||||
switch (context.outputMachOType()) {
|
||||
case llvm::MachO::MH_OBJECT:
|
||||
machHeaderSymbolName = "__mh_object_header";
|
||||
break;
|
||||
case llvm::MachO::MH_EXECUTE:
|
||||
machHeaderSymbolName = "__mh_execute_header";
|
||||
symbolScope = DefinedAtom::scopeGlobal;
|
||||
dsoHandleName = "___dso_handle";
|
||||
break;
|
||||
case llvm::MachO::MH_FVMLIB:
|
||||
llvm_unreachable("no mach_header symbol for file type");
|
||||
case llvm::MachO::MH_CORE:
|
||||
llvm_unreachable("no mach_header symbol for file type");
|
||||
case llvm::MachO::MH_PRELOAD:
|
||||
llvm_unreachable("no mach_header symbol for file type");
|
||||
case llvm::MachO::MH_DYLIB:
|
||||
machHeaderSymbolName = "__mh_dylib_header";
|
||||
dsoHandleName = "___dso_handle";
|
||||
break;
|
||||
case llvm::MachO::MH_DYLINKER:
|
||||
machHeaderSymbolName = "__mh_dylinker_header";
|
||||
dsoHandleName = "___dso_handle";
|
||||
break;
|
||||
case llvm::MachO::MH_BUNDLE:
|
||||
machHeaderSymbolName = "__mh_bundle_header";
|
||||
dsoHandleName = "___dso_handle";
|
||||
break;
|
||||
case llvm::MachO::MH_DYLIB_STUB:
|
||||
llvm_unreachable("no mach_header symbol for file type");
|
||||
case llvm::MachO::MH_DSYM:
|
||||
llvm_unreachable("no mach_header symbol for file type");
|
||||
case llvm::MachO::MH_KEXT_BUNDLE:
|
||||
dsoHandleName = "___dso_handle";
|
||||
break;
|
||||
}
|
||||
if (!machHeaderSymbolName.empty())
|
||||
_definedAtoms.push_back(new (allocator()) MachODefinedAtom(
|
||||
*this, machHeaderSymbolName, symbolScope,
|
||||
DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false,
|
||||
true /* noDeadStrip */,
|
||||
ArrayRef<uint8_t>(), DefinedAtom::Alignment(4096)));
|
||||
|
||||
if (!dsoHandleName.empty())
|
||||
_definedAtoms.push_back(new (allocator()) MachODefinedAtom(
|
||||
*this, dsoHandleName, DefinedAtom::scopeLinkageUnit,
|
||||
DefinedAtom::typeDSOHandle, DefinedAtom::mergeNo, false,
|
||||
true /* noDeadStrip */,
|
||||
ArrayRef<uint8_t>(), DefinedAtom::Alignment(1)));
|
||||
}
|
||||
|
||||
const AtomRange<DefinedAtom> defined() const override {
|
||||
return _definedAtoms;
|
||||
}
|
||||
const AtomRange<UndefinedAtom> undefined() const override {
|
||||
return _noUndefinedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
|
||||
return _noSharedLibraryAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<AbsoluteAtom> absolute() const override {
|
||||
return _noAbsoluteAtoms;
|
||||
}
|
||||
|
||||
void clearAtoms() override {
|
||||
_definedAtoms.clear();
|
||||
_noUndefinedAtoms.clear();
|
||||
_noSharedLibraryAtoms.clear();
|
||||
_noAbsoluteAtoms.clear();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
mutable AtomVector<DefinedAtom> _definedAtoms;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
|
|
@ -1,467 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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_FILE_H
|
||||
#define LLD_READER_WRITER_MACHO_FILE_H
|
||||
|
||||
#include "Atoms.h"
|
||||
#include "DebugInfo.h"
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/TextAPI/InterfaceFile.h"
|
||||
#include "llvm/TextAPI/TextAPIReader.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
using lld::mach_o::normalized::Section;
|
||||
|
||||
class MachOFile : public SimpleFile {
|
||||
public:
|
||||
|
||||
/// Real file constructor - for on-disk files.
|
||||
MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
|
||||
: SimpleFile(mb->getBufferIdentifier(), File::kindMachObject),
|
||||
_mb(std::move(mb)), _ctx(ctx) {}
|
||||
|
||||
/// Dummy file constructor - for virtual files.
|
||||
MachOFile(StringRef path)
|
||||
: SimpleFile(path, File::kindMachObject) {}
|
||||
|
||||
void addDefinedAtom(StringRef name, Atom::Scope scope,
|
||||
DefinedAtom::ContentType type, DefinedAtom::Merge merge,
|
||||
uint64_t sectionOffset, uint64_t contentSize, bool thumb,
|
||||
bool noDeadStrip, bool copyRefs,
|
||||
const Section *inSection) {
|
||||
assert(sectionOffset+contentSize <= inSection->content.size());
|
||||
ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
|
||||
contentSize);
|
||||
if (copyRefs) {
|
||||
// Make a copy of the atom's name and content that is owned by this file.
|
||||
name = name.copy(allocator());
|
||||
content = content.copy(allocator());
|
||||
}
|
||||
DefinedAtom::Alignment align(
|
||||
inSection->alignment,
|
||||
sectionOffset % inSection->alignment);
|
||||
auto *atom =
|
||||
new (allocator()) MachODefinedAtom(*this, name, scope, type, merge,
|
||||
thumb, noDeadStrip, content, align);
|
||||
addAtomForSection(inSection, atom, sectionOffset);
|
||||
}
|
||||
|
||||
void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope,
|
||||
DefinedAtom::ContentType type, DefinedAtom::Merge merge,
|
||||
bool thumb, bool noDeadStrip, uint64_t sectionOffset,
|
||||
uint64_t contentSize, StringRef sectionName,
|
||||
bool copyRefs, const Section *inSection) {
|
||||
assert(sectionOffset+contentSize <= inSection->content.size());
|
||||
ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
|
||||
contentSize);
|
||||
if (copyRefs) {
|
||||
// Make a copy of the atom's name and content that is owned by this file.
|
||||
name = name.copy(allocator());
|
||||
content = content.copy(allocator());
|
||||
sectionName = sectionName.copy(allocator());
|
||||
}
|
||||
DefinedAtom::Alignment align(
|
||||
inSection->alignment,
|
||||
sectionOffset % inSection->alignment);
|
||||
auto *atom =
|
||||
new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type,
|
||||
merge, thumb,
|
||||
noDeadStrip, content,
|
||||
sectionName, align);
|
||||
addAtomForSection(inSection, atom, sectionOffset);
|
||||
}
|
||||
|
||||
void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope,
|
||||
uint64_t sectionOffset, uint64_t size,
|
||||
bool noDeadStrip, bool copyRefs,
|
||||
const Section *inSection) {
|
||||
if (copyRefs) {
|
||||
// Make a copy of the atom's name and content that is owned by this file.
|
||||
name = name.copy(allocator());
|
||||
}
|
||||
DefinedAtom::Alignment align(
|
||||
inSection->alignment,
|
||||
sectionOffset % inSection->alignment);
|
||||
|
||||
DefinedAtom::ContentType type = DefinedAtom::typeUnknown;
|
||||
switch (inSection->type) {
|
||||
case llvm::MachO::S_ZEROFILL:
|
||||
type = DefinedAtom::typeZeroFill;
|
||||
break;
|
||||
case llvm::MachO::S_THREAD_LOCAL_ZEROFILL:
|
||||
type = DefinedAtom::typeTLVInitialZeroFill;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unrecognized zero-fill section");
|
||||
}
|
||||
|
||||
auto *atom =
|
||||
new (allocator()) MachODefinedAtom(*this, name, scope, type, size,
|
||||
noDeadStrip, align);
|
||||
addAtomForSection(inSection, atom, sectionOffset);
|
||||
}
|
||||
|
||||
void addUndefinedAtom(StringRef name, bool copyRefs) {
|
||||
if (copyRefs) {
|
||||
// Make a copy of the atom's name that is owned by this file.
|
||||
name = name.copy(allocator());
|
||||
}
|
||||
auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name);
|
||||
addAtom(*atom);
|
||||
_undefAtoms[name] = atom;
|
||||
}
|
||||
|
||||
void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size,
|
||||
DefinedAtom::Alignment align, bool copyRefs) {
|
||||
if (copyRefs) {
|
||||
// Make a copy of the atom's name that is owned by this file.
|
||||
name = name.copy(allocator());
|
||||
}
|
||||
auto *atom =
|
||||
new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align);
|
||||
addAtom(*atom);
|
||||
_undefAtoms[name] = atom;
|
||||
}
|
||||
|
||||
/// Search this file for the atom from 'section' that covers
|
||||
/// 'offsetInSect'. Returns nullptr is no atom found.
|
||||
MachODefinedAtom *findAtomCoveringAddress(const Section §ion,
|
||||
uint64_t offsetInSect,
|
||||
uint32_t *foundOffsetAtom=nullptr) {
|
||||
const auto &pos = _sectionAtoms.find(§ion);
|
||||
if (pos == _sectionAtoms.end())
|
||||
return nullptr;
|
||||
const auto &vec = pos->second;
|
||||
assert(offsetInSect < section.content.size());
|
||||
// Vector of atoms for section are already sorted, so do binary search.
|
||||
const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect,
|
||||
[offsetInSect](const SectionOffsetAndAtom &ao,
|
||||
uint64_t targetAddr) -> bool {
|
||||
// Each atom has a start offset of its slice of the
|
||||
// section's content. This compare function must return true
|
||||
// iff the atom's range is before the offset being searched for.
|
||||
uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size();
|
||||
return (atomsEndOffset <= offsetInSect);
|
||||
});
|
||||
if (atomPos == vec.end())
|
||||
return nullptr;
|
||||
if (foundOffsetAtom)
|
||||
*foundOffsetAtom = offsetInSect - atomPos->offset;
|
||||
return atomPos->atom;
|
||||
}
|
||||
|
||||
/// Searches this file for an UndefinedAtom named 'name'. Returns
|
||||
/// nullptr is no such atom found.
|
||||
const lld::Atom *findUndefAtom(StringRef name) {
|
||||
auto pos = _undefAtoms.find(name);
|
||||
if (pos == _undefAtoms.end())
|
||||
return nullptr;
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor;
|
||||
|
||||
void eachDefinedAtom(DefinedAtomVisitor vistor) {
|
||||
for (auto §AndAtoms : _sectionAtoms) {
|
||||
for (auto &offAndAtom : sectAndAtoms.second) {
|
||||
vistor(offAndAtom.atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
|
||||
SectionAtomVisitor;
|
||||
|
||||
void eachAtomInSection(const Section §ion, SectionAtomVisitor visitor) {
|
||||
auto pos = _sectionAtoms.find(§ion);
|
||||
if (pos == _sectionAtoms.end())
|
||||
return;
|
||||
auto vec = pos->second;
|
||||
|
||||
for (auto &offAndAtom : vec)
|
||||
visitor(offAndAtom.atom, offAndAtom.offset);
|
||||
}
|
||||
|
||||
MachOLinkingContext::Arch arch() const { return _arch; }
|
||||
void setArch(MachOLinkingContext::Arch arch) { _arch = arch; }
|
||||
|
||||
MachOLinkingContext::OS OS() const { return _os; }
|
||||
void setOS(MachOLinkingContext::OS os) { _os = os; }
|
||||
|
||||
MachOLinkingContext::ObjCConstraint objcConstraint() const {
|
||||
return _objcConstraint;
|
||||
}
|
||||
void setObjcConstraint(MachOLinkingContext::ObjCConstraint v) {
|
||||
_objcConstraint = v;
|
||||
}
|
||||
|
||||
uint32_t minVersion() const { return _minVersion; }
|
||||
void setMinVersion(uint32_t v) { _minVersion = v; }
|
||||
|
||||
LoadCommandType minVersionLoadCommandKind() const {
|
||||
return _minVersionLoadCommandKind;
|
||||
}
|
||||
void setMinVersionLoadCommandKind(LoadCommandType v) {
|
||||
_minVersionLoadCommandKind = v;
|
||||
}
|
||||
|
||||
uint32_t swiftVersion() const { return _swiftVersion; }
|
||||
void setSwiftVersion(uint32_t v) { _swiftVersion = v; }
|
||||
|
||||
bool subsectionsViaSymbols() const {
|
||||
return _flags & llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
|
||||
}
|
||||
void setFlags(normalized::FileFlags v) { _flags = v; }
|
||||
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||
static inline bool classof(const File *F) {
|
||||
return F->kind() == File::kindMachObject;
|
||||
}
|
||||
|
||||
void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) {
|
||||
_debugInfo = std::move(debugInfo);
|
||||
}
|
||||
|
||||
DebugInfo* debugInfo() const { return _debugInfo.get(); }
|
||||
std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); }
|
||||
|
||||
protected:
|
||||
std::error_code doParse() override {
|
||||
// Convert binary file to normalized mach-o.
|
||||
auto normFile = normalized::readBinary(_mb, _ctx->arch());
|
||||
if (auto ec = normFile.takeError())
|
||||
return llvm::errorToErrorCode(std::move(ec));
|
||||
// Convert normalized mach-o to atoms.
|
||||
if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false))
|
||||
return llvm::errorToErrorCode(std::move(ec));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
private:
|
||||
struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; };
|
||||
|
||||
void addAtomForSection(const Section *inSection, MachODefinedAtom* atom,
|
||||
uint64_t sectionOffset) {
|
||||
SectionOffsetAndAtom offAndAtom;
|
||||
offAndAtom.offset = sectionOffset;
|
||||
offAndAtom.atom = atom;
|
||||
_sectionAtoms[inSection].push_back(offAndAtom);
|
||||
addAtom(*atom);
|
||||
}
|
||||
|
||||
typedef llvm::DenseMap<const normalized::Section *,
|
||||
std::vector<SectionOffsetAndAtom>> SectionToAtoms;
|
||||
typedef llvm::StringMap<const lld::Atom *> NameToAtom;
|
||||
|
||||
std::unique_ptr<MemoryBuffer> _mb;
|
||||
MachOLinkingContext *_ctx;
|
||||
SectionToAtoms _sectionAtoms;
|
||||
NameToAtom _undefAtoms;
|
||||
MachOLinkingContext::Arch _arch = MachOLinkingContext::arch_unknown;
|
||||
MachOLinkingContext::OS _os = MachOLinkingContext::OS::unknown;
|
||||
uint32_t _minVersion = 0;
|
||||
LoadCommandType _minVersionLoadCommandKind = (LoadCommandType)0;
|
||||
MachOLinkingContext::ObjCConstraint _objcConstraint =
|
||||
MachOLinkingContext::objc_unknown;
|
||||
uint32_t _swiftVersion = 0;
|
||||
normalized::FileFlags _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
|
||||
std::unique_ptr<DebugInfo> _debugInfo;
|
||||
};
|
||||
|
||||
class MachODylibFile : public SharedLibraryFile {
|
||||
public:
|
||||
MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
|
||||
: SharedLibraryFile(mb->getBufferIdentifier()),
|
||||
_mb(std::move(mb)), _ctx(ctx) {}
|
||||
|
||||
MachODylibFile(StringRef path) : SharedLibraryFile(path) {}
|
||||
|
||||
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
|
||||
// Pass down _installName so that if this requested symbol
|
||||
// is re-exported through this dylib, the SharedLibraryAtom's loadName()
|
||||
// is this dylib installName and not the implementation dylib's.
|
||||
// NOTE: isData is not needed for dylibs (it matters for static libs).
|
||||
return exports(name, _installName);
|
||||
}
|
||||
|
||||
/// Adds symbol name that this dylib exports. The corresponding
|
||||
/// SharedLibraryAtom is created lazily (since most symbols are not used).
|
||||
void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) {
|
||||
if (copyRefs) {
|
||||
name = name.copy(allocator());
|
||||
}
|
||||
AtomAndFlags info(weakDef);
|
||||
_nameToAtom[name] = info;
|
||||
}
|
||||
|
||||
void addReExportedDylib(StringRef dylibPath) {
|
||||
_reExportedDylibs.emplace_back(dylibPath);
|
||||
}
|
||||
|
||||
StringRef installName() const { return _installName; }
|
||||
uint32_t currentVersion() { return _currentVersion; }
|
||||
uint32_t compatVersion() { return _compatVersion; }
|
||||
|
||||
void setInstallName(StringRef name) { _installName = name; }
|
||||
void setCompatVersion(uint32_t version) { _compatVersion = version; }
|
||||
void setCurrentVersion(uint32_t version) { _currentVersion = version; }
|
||||
|
||||
typedef std::function<MachODylibFile *(StringRef)> FindDylib;
|
||||
|
||||
void loadReExportedDylibs(FindDylib find) {
|
||||
for (ReExportedDylib &entry : _reExportedDylibs) {
|
||||
if (!entry.file)
|
||||
entry.file = find(entry.path);
|
||||
}
|
||||
}
|
||||
|
||||
StringRef getDSOName() const override { return _installName; }
|
||||
|
||||
std::error_code doParse() override {
|
||||
// Convert binary file to normalized mach-o.
|
||||
auto normFile = normalized::readBinary(_mb, _ctx->arch());
|
||||
if (auto ec = normFile.takeError())
|
||||
return llvm::errorToErrorCode(std::move(ec));
|
||||
// Convert normalized mach-o to atoms.
|
||||
if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false))
|
||||
return llvm::errorToErrorCode(std::move(ec));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
protected:
|
||||
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
|
||||
StringRef installName) const {
|
||||
// First, check if requested symbol is directly implemented by this dylib.
|
||||
auto entry = _nameToAtom.find(name);
|
||||
if (entry != _nameToAtom.end()) {
|
||||
// FIXME: Make this map a set and only used in assert builds.
|
||||
// Note, its safe to assert here as the resolver is the only client of
|
||||
// this API and it only requests exports for undefined symbols.
|
||||
// If we return from here we are no longer undefined so we should never
|
||||
// get here again.
|
||||
assert(!entry->second.atom && "Duplicate shared library export");
|
||||
bool weakDef = entry->second.weakDef;
|
||||
auto *atom = new (allocator()) MachOSharedLibraryAtom(*this, name,
|
||||
installName,
|
||||
weakDef);
|
||||
entry->second.atom = atom;
|
||||
return atom;
|
||||
}
|
||||
|
||||
// Next, check if symbol is implemented in some re-exported dylib.
|
||||
for (const ReExportedDylib &dylib : _reExportedDylibs) {
|
||||
assert(dylib.file);
|
||||
auto atom = dylib.file->exports(name, installName);
|
||||
if (atom.get())
|
||||
return atom;
|
||||
}
|
||||
|
||||
// Symbol not exported or re-exported by this dylib.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct ReExportedDylib {
|
||||
ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
|
||||
ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
|
||||
StringRef path;
|
||||
MachODylibFile *file;
|
||||
};
|
||||
|
||||
struct AtomAndFlags {
|
||||
AtomAndFlags() : atom(nullptr), weakDef(false) { }
|
||||
AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { }
|
||||
const SharedLibraryAtom *atom;
|
||||
bool weakDef;
|
||||
};
|
||||
|
||||
std::unique_ptr<MemoryBuffer> _mb;
|
||||
MachOLinkingContext *_ctx;
|
||||
StringRef _installName;
|
||||
uint32_t _currentVersion;
|
||||
uint32_t _compatVersion;
|
||||
std::vector<ReExportedDylib> _reExportedDylibs;
|
||||
mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
|
||||
};
|
||||
|
||||
class TAPIFile : public MachODylibFile {
|
||||
public:
|
||||
|
||||
TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
|
||||
: MachODylibFile(std::move(mb), ctx) {}
|
||||
|
||||
std::error_code doParse() override {
|
||||
|
||||
llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
|
||||
llvm::MachO::TextAPIReader::get(*_mb);
|
||||
if (!result)
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
|
||||
std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
|
||||
return loadFromInterface(*interface);
|
||||
}
|
||||
|
||||
private:
|
||||
std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
|
||||
llvm::MachO::Architecture arch;
|
||||
switch(_ctx->arch()) {
|
||||
case MachOLinkingContext::arch_x86:
|
||||
arch = llvm::MachO::AK_i386;
|
||||
break;
|
||||
case MachOLinkingContext::arch_x86_64:
|
||||
arch = llvm::MachO::AK_x86_64;
|
||||
break;
|
||||
case MachOLinkingContext::arch_arm64:
|
||||
arch = llvm::MachO::AK_arm64;
|
||||
break;
|
||||
default:
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
setInstallName(interface.getInstallName().copy(allocator()));
|
||||
// TODO(compnerd) filter out symbols based on the target platform
|
||||
for (const auto symbol : interface.symbols())
|
||||
if (symbol->getArchitectures().has(arch))
|
||||
addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
|
||||
|
||||
for (const llvm::MachO::InterfaceFileRef &reexport :
|
||||
interface.reexportedLibraries())
|
||||
addReExportedDylib(reexport.getInstallName().copy(allocator()));
|
||||
|
||||
for (const auto& document : interface.documents()) {
|
||||
for (auto& reexport : _reExportedDylibs) {
|
||||
if (reexport.path != document->getInstallName())
|
||||
continue;
|
||||
assert(!reexport.file);
|
||||
_ownedFiles.push_back(std::make_unique<TAPIFile>(
|
||||
MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
|
||||
reexport.file = _ownedFiles.back().get();
|
||||
std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
|
||||
};
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_FILE_H
|
|
@ -1,62 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/FlatNamespaceFile.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_FLAT_NAMESPACE_FILE_H
|
||||
#define LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
|
||||
|
||||
#include "Atoms.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// A FlateNamespaceFile instance may be added as a resolution source of last
|
||||
// resort, depending on how -flat_namespace and -undefined are set.
|
||||
//
|
||||
class FlatNamespaceFile : public SharedLibraryFile {
|
||||
public:
|
||||
FlatNamespaceFile(const MachOLinkingContext &context)
|
||||
: SharedLibraryFile("flat namespace") { }
|
||||
|
||||
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
|
||||
return new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(),
|
||||
false);
|
||||
}
|
||||
|
||||
StringRef getDSOName() const override { return "flat-namespace"; }
|
||||
|
||||
const AtomRange<DefinedAtom> defined() const override {
|
||||
return _noDefinedAtoms;
|
||||
}
|
||||
const AtomRange<UndefinedAtom> undefined() const override {
|
||||
return _noUndefinedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
|
||||
return _noSharedLibraryAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<AbsoluteAtom> absolute() const override {
|
||||
return _noAbsoluteAtoms;
|
||||
}
|
||||
|
||||
void clearAtoms() override {
|
||||
_noDefinedAtoms.clear();
|
||||
_noUndefinedAtoms.clear();
|
||||
_noSharedLibraryAtoms.clear();
|
||||
_noAbsoluteAtoms.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
|
|
@ -1,183 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This linker pass transforms all GOT kind references to real references.
|
||||
/// That is, in assembly you can write something like:
|
||||
/// movq foo@GOTPCREL(%rip), %rax
|
||||
/// which means you want to load a pointer to "foo" out of the GOT (global
|
||||
/// Offsets Table). In the object file, the Atom containing this instruction
|
||||
/// has a Reference whose target is an Atom named "foo" and the Reference
|
||||
/// kind is a GOT load. The linker needs to instantiate a pointer sized
|
||||
/// GOT entry. This is done be creating a GOT Atom to represent that pointer
|
||||
/// sized data in this pass, and altering the Atom graph so the Reference now
|
||||
/// points to the GOT Atom entry (corresponding to "foo") and changing the
|
||||
/// Reference Kind to reflect it is now pointing to a GOT entry (rather
|
||||
/// then needing a GOT entry).
|
||||
///
|
||||
/// There is one optimization the linker can do here. If the target of the GOT
|
||||
/// is in the same linkage unit and does not need to be interposable, and
|
||||
/// the GOT use is just a load (not some other operation), this pass can
|
||||
/// transform that load into an LEA (add). This optimizes away one memory load
|
||||
/// which at runtime that could stall the pipeline. This optimization only
|
||||
/// works for architectures in which a (GOT) load instruction can be change to
|
||||
/// an LEA instruction that is the same size. The method isGOTAccess() should
|
||||
/// only return true for "canBypassGOT" if this optimization is supported.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// GOT Entry Atom created by the GOT pass.
|
||||
//
|
||||
class GOTEntryAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
GOTEntryAtom(const File &file, bool is64, StringRef name)
|
||||
: SimpleDefinedAtom(file), _is64(is64), _name(name) { }
|
||||
|
||||
~GOTEntryAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeGOT;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
StringRef slotName() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
StringRef _name;
|
||||
};
|
||||
|
||||
/// Pass for instantiating and optimizing GOT slots.
|
||||
///
|
||||
class GOTPass : public Pass {
|
||||
public:
|
||||
GOTPass(const MachOLinkingContext &context)
|
||||
: _ctx(context), _archHandler(_ctx.archHandler()),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at instructions accessing the GOT.
|
||||
bool canBypassGOT;
|
||||
if (!_archHandler.isGOTAccess(*ref, canBypassGOT))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
|
||||
if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) {
|
||||
// Update reference kind to reflect that target is a direct access.
|
||||
_archHandler.updateReferenceToGOT(ref, false);
|
||||
} else {
|
||||
// Replace the target with a reference to a GOT entry.
|
||||
const DefinedAtom *gotEntry = makeGOTEntry(target);
|
||||
const_cast<Reference *>(ref)->setTarget(gotEntry);
|
||||
// Update reference kind to reflect that target is now a GOT entry.
|
||||
_archHandler.updateReferenceToGOT(ref, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and add all created GOT Atoms to merged file
|
||||
std::vector<const GOTEntryAtom *> entries;
|
||||
entries.reserve(_targetToGOT.size());
|
||||
for (auto &it : _targetToGOT)
|
||||
entries.push_back(it.second);
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](const GOTEntryAtom *left, const GOTEntryAtom *right) {
|
||||
return (left->slotName().compare(right->slotName()) < 0);
|
||||
});
|
||||
for (const GOTEntryAtom *slot : entries)
|
||||
mergedFile.addAtom(*slot);
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) {
|
||||
// Accesses to shared library symbols must go through GOT.
|
||||
if (isa<SharedLibraryAtom>(target))
|
||||
return true;
|
||||
// Accesses to interposable symbols in same linkage unit must also go
|
||||
// through GOT.
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget != nullptr &&
|
||||
defTarget->interposable() != DefinedAtom::interposeNo) {
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
return true;
|
||||
}
|
||||
// Target does not require indirection. So, if instruction allows GOT to be
|
||||
// by-passed, do that optimization and don't create GOT entry.
|
||||
return !canBypassGOT;
|
||||
}
|
||||
|
||||
const DefinedAtom *makeGOTEntry(const Atom *target) {
|
||||
auto pos = _targetToGOT.find(target);
|
||||
if (pos == _targetToGOT.end()) {
|
||||
auto *gotEntry = new (_file.allocator())
|
||||
GOTEntryAtom(_file, _ctx.is64Bit(), target->name());
|
||||
_targetToGOT[target] = gotEntry;
|
||||
const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo().
|
||||
nonLazyPointerReferenceToBinder;
|
||||
gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
|
||||
nlInfo.kind, 0, target, 0);
|
||||
return gotEntry;
|
||||
}
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
MachOFile &_file;
|
||||
llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT;
|
||||
};
|
||||
|
||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsGOTPass());
|
||||
pm.add(std::make_unique<GOTPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,490 +0,0 @@
|
|||
//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LayoutPass.h"
|
||||
#include "lld/Core/Instrumentation.h"
|
||||
#include "lld/Core/PassManager.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
using namespace lld;
|
||||
|
||||
#define DEBUG_TYPE "LayoutPass"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
static bool compareAtoms(const LayoutPass::SortKey &,
|
||||
const LayoutPass::SortKey &,
|
||||
LayoutPass::SortOverride customSorter);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Return "reason (leftval, rightval)"
|
||||
static std::string formatReason(StringRef reason, int leftVal, int rightVal) {
|
||||
return (Twine(reason) + " (" + Twine(leftVal) + ", " + Twine(rightVal) + ")")
|
||||
.str();
|
||||
}
|
||||
|
||||
// Less-than relationship of two atoms must be transitive, which is, if a < b
|
||||
// and b < c, a < c must be true. This function checks the transitivity by
|
||||
// checking the sort results.
|
||||
static void checkTransitivity(std::vector<LayoutPass::SortKey> &vec,
|
||||
LayoutPass::SortOverride customSorter) {
|
||||
for (auto i = vec.begin(), e = vec.end(); (i + 1) != e; ++i) {
|
||||
for (auto j = i + 1; j != e; ++j) {
|
||||
assert(compareAtoms(*i, *j, customSorter));
|
||||
assert(!compareAtoms(*j, *i, customSorter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to check follow-on graph.
|
||||
typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
|
||||
|
||||
static std::string atomToDebugString(const Atom *atom) {
|
||||
const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom);
|
||||
std::string str;
|
||||
llvm::raw_string_ostream s(str);
|
||||
if (definedAtom->name().empty())
|
||||
s << "<anonymous " << definedAtom << ">";
|
||||
else
|
||||
s << definedAtom->name();
|
||||
s << " in ";
|
||||
if (definedAtom->customSectionName().empty())
|
||||
s << "<anonymous>";
|
||||
else
|
||||
s << definedAtom->customSectionName();
|
||||
s.flush();
|
||||
return str;
|
||||
}
|
||||
|
||||
static void showCycleDetectedError(const Registry ®istry,
|
||||
AtomToAtomT &followOnNexts,
|
||||
const DefinedAtom *atom) {
|
||||
const DefinedAtom *start = atom;
|
||||
llvm::dbgs() << "There's a cycle in a follow-on chain!\n";
|
||||
do {
|
||||
llvm::dbgs() << " " << atomToDebugString(atom) << "\n";
|
||||
for (const Reference *ref : *atom) {
|
||||
StringRef kindValStr;
|
||||
if (!registry.referenceKindToString(ref->kindNamespace(), ref->kindArch(),
|
||||
ref->kindValue(), kindValStr)) {
|
||||
kindValStr = "<unknown>";
|
||||
}
|
||||
llvm::dbgs() << " " << kindValStr
|
||||
<< ": " << atomToDebugString(ref->target()) << "\n";
|
||||
}
|
||||
atom = followOnNexts[atom];
|
||||
} while (atom != start);
|
||||
llvm::report_fatal_error("Cycle detected");
|
||||
}
|
||||
|
||||
/// Exit if there's a cycle in a followon chain reachable from the
|
||||
/// given root atom. Uses the tortoise and hare algorithm to detect a
|
||||
/// cycle.
|
||||
static void checkNoCycleInFollowonChain(const Registry ®istry,
|
||||
AtomToAtomT &followOnNexts,
|
||||
const DefinedAtom *root) {
|
||||
const DefinedAtom *tortoise = root;
|
||||
const DefinedAtom *hare = followOnNexts[root];
|
||||
while (true) {
|
||||
if (!tortoise || !hare)
|
||||
return;
|
||||
if (tortoise == hare)
|
||||
showCycleDetectedError(registry, followOnNexts, tortoise);
|
||||
tortoise = followOnNexts[tortoise];
|
||||
hare = followOnNexts[followOnNexts[hare]];
|
||||
}
|
||||
}
|
||||
|
||||
static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots,
|
||||
const DefinedAtom *atom) {
|
||||
if (!atom) return;
|
||||
auto i = followOnRoots.find(atom);
|
||||
if (i == followOnRoots.end()) {
|
||||
llvm_unreachable(((Twine("Atom <") + atomToDebugString(atom) +
|
||||
"> has no follow-on root!"))
|
||||
.str()
|
||||
.c_str());
|
||||
}
|
||||
const DefinedAtom *ap = i->second;
|
||||
while (true) {
|
||||
const DefinedAtom *next = followOnRoots[ap];
|
||||
if (!next) {
|
||||
llvm_unreachable((Twine("Atom <" + atomToDebugString(atom) +
|
||||
"> is not reachable from its root!"))
|
||||
.str()
|
||||
.c_str());
|
||||
}
|
||||
if (next == ap)
|
||||
return;
|
||||
ap = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void printDefinedAtoms(const File::AtomRange<DefinedAtom> &atomRange) {
|
||||
for (const DefinedAtom *atom : atomRange) {
|
||||
llvm::dbgs() << " file=" << atom->file().path()
|
||||
<< ", name=" << atom->name()
|
||||
<< ", size=" << atom->size()
|
||||
<< ", type=" << atom->contentType()
|
||||
<< ", ordinal=" << atom->ordinal()
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that the followon chain is sane. Should not be called in
|
||||
/// release binary.
|
||||
void LayoutPass::checkFollowonChain(const File::AtomRange<DefinedAtom> &range) {
|
||||
ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain");
|
||||
|
||||
// Verify that there's no cycle in follow-on chain.
|
||||
std::set<const DefinedAtom *> roots;
|
||||
for (const auto &ai : _followOnRoots)
|
||||
roots.insert(ai.second);
|
||||
for (const DefinedAtom *root : roots)
|
||||
checkNoCycleInFollowonChain(_registry, _followOnNexts, root);
|
||||
|
||||
// Verify that all the atoms in followOnNexts have references to
|
||||
// their roots.
|
||||
for (const auto &ai : _followOnNexts) {
|
||||
checkReachabilityFromRoot(_followOnRoots, ai.first);
|
||||
checkReachabilityFromRoot(_followOnRoots, ai.second);
|
||||
}
|
||||
}
|
||||
#endif // #ifndef NDEBUG
|
||||
|
||||
/// The function compares atoms by sorting atoms in the following order
|
||||
/// a) Sorts atoms by their ordinal overrides (layout-after/ingroup)
|
||||
/// b) Sorts atoms by their permissions
|
||||
/// c) Sorts atoms by their content
|
||||
/// d) Sorts atoms by custom sorter
|
||||
/// e) Sorts atoms on how they appear using File Ordinality
|
||||
/// f) Sorts atoms on how they appear within the File
|
||||
static bool compareAtomsSub(const LayoutPass::SortKey &lc,
|
||||
const LayoutPass::SortKey &rc,
|
||||
LayoutPass::SortOverride customSorter,
|
||||
std::string &reason) {
|
||||
const DefinedAtom *left = lc._atom.get();
|
||||
const DefinedAtom *right = rc._atom.get();
|
||||
if (left == right) {
|
||||
reason = "same";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the root of the chain if it is a part of a follow-on chain.
|
||||
const DefinedAtom *leftRoot = lc._root;
|
||||
const DefinedAtom *rightRoot = rc._root;
|
||||
|
||||
// Sort atoms by their ordinal overrides only if they fall in the same
|
||||
// chain.
|
||||
if (leftRoot == rightRoot) {
|
||||
LLVM_DEBUG(reason = formatReason("override", lc._override, rc._override));
|
||||
return lc._override < rc._override;
|
||||
}
|
||||
|
||||
// Sort same permissions together.
|
||||
DefinedAtom::ContentPermissions leftPerms = leftRoot->permissions();
|
||||
DefinedAtom::ContentPermissions rightPerms = rightRoot->permissions();
|
||||
|
||||
if (leftPerms != rightPerms) {
|
||||
LLVM_DEBUG(
|
||||
reason = formatReason("contentPerms", (int)leftPerms, (int)rightPerms));
|
||||
return leftPerms < rightPerms;
|
||||
}
|
||||
|
||||
// Sort same content types together.
|
||||
DefinedAtom::ContentType leftType = leftRoot->contentType();
|
||||
DefinedAtom::ContentType rightType = rightRoot->contentType();
|
||||
|
||||
if (leftType != rightType) {
|
||||
LLVM_DEBUG(reason =
|
||||
formatReason("contentType", (int)leftType, (int)rightType));
|
||||
return leftType < rightType;
|
||||
}
|
||||
|
||||
// Use custom sorter if supplied.
|
||||
if (customSorter) {
|
||||
bool leftBeforeRight;
|
||||
if (customSorter(leftRoot, rightRoot, leftBeforeRight))
|
||||
return leftBeforeRight;
|
||||
}
|
||||
|
||||
// Sort by .o order.
|
||||
const File *leftFile = &leftRoot->file();
|
||||
const File *rightFile = &rightRoot->file();
|
||||
|
||||
if (leftFile != rightFile) {
|
||||
LLVM_DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(),
|
||||
(int)rightFile->ordinal()));
|
||||
return leftFile->ordinal() < rightFile->ordinal();
|
||||
}
|
||||
|
||||
// Sort by atom order with .o file.
|
||||
uint64_t leftOrdinal = leftRoot->ordinal();
|
||||
uint64_t rightOrdinal = rightRoot->ordinal();
|
||||
|
||||
if (leftOrdinal != rightOrdinal) {
|
||||
LLVM_DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(),
|
||||
(int)rightRoot->ordinal()));
|
||||
return leftOrdinal < rightOrdinal;
|
||||
}
|
||||
|
||||
llvm::errs() << "Unordered: <" << left->name() << "> <" << right->name()
|
||||
<< ">\n";
|
||||
llvm_unreachable("Atoms with Same Ordinal!");
|
||||
}
|
||||
|
||||
static bool compareAtoms(const LayoutPass::SortKey &lc,
|
||||
const LayoutPass::SortKey &rc,
|
||||
LayoutPass::SortOverride customSorter) {
|
||||
std::string reason;
|
||||
bool result = compareAtomsSub(lc, rc, customSorter, reason);
|
||||
LLVM_DEBUG({
|
||||
StringRef comp = result ? "<" : ">=";
|
||||
llvm::dbgs() << "Layout: '" << lc._atom.get()->name()
|
||||
<< "' " << comp << " '"
|
||||
<< rc._atom.get()->name() << "' (" << reason << ")\n";
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
LayoutPass::LayoutPass(const Registry ®istry, SortOverride sorter)
|
||||
: _registry(registry), _customSorter(std::move(sorter)) {}
|
||||
|
||||
// Returns the atom immediately followed by the given atom in the followon
|
||||
// chain.
|
||||
const DefinedAtom *LayoutPass::findAtomFollowedBy(
|
||||
const DefinedAtom *targetAtom) {
|
||||
// Start from the beginning of the chain and follow the chain until
|
||||
// we find the targetChain.
|
||||
const DefinedAtom *atom = _followOnRoots[targetAtom];
|
||||
while (true) {
|
||||
const DefinedAtom *prevAtom = atom;
|
||||
AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
|
||||
// The target atom must be in the chain of its root.
|
||||
assert(targetFollowOnAtomsIter != _followOnNexts.end());
|
||||
atom = targetFollowOnAtomsIter->second;
|
||||
if (atom == targetAtom)
|
||||
return prevAtom;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all the atoms followed by the given target atom are of size zero.
|
||||
// When this method is called, an atom being added is not of size zero and
|
||||
// will be added to the head of the followon chain. All the atoms between the
|
||||
// atom and the targetAtom (specified by layout-after) need to be of size zero
|
||||
// in this case. Otherwise the desired layout is impossible.
|
||||
bool LayoutPass::checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom) {
|
||||
const DefinedAtom *atom = _followOnRoots[targetAtom];
|
||||
while (true) {
|
||||
if (atom == targetAtom)
|
||||
return true;
|
||||
if (atom->size() != 0)
|
||||
// TODO: print warning that an impossible layout is being desired by the
|
||||
// user.
|
||||
return false;
|
||||
AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
|
||||
// The target atom must be in the chain of its root.
|
||||
assert(targetFollowOnAtomsIter != _followOnNexts.end());
|
||||
atom = targetFollowOnAtomsIter->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the root of all atoms in targetAtom's chain to the given root.
|
||||
void LayoutPass::setChainRoot(const DefinedAtom *targetAtom,
|
||||
const DefinedAtom *root) {
|
||||
// Walk through the followon chain and override each node's root.
|
||||
while (true) {
|
||||
_followOnRoots[targetAtom] = root;
|
||||
AtomToAtomT::iterator targetFollowOnAtomsIter =
|
||||
_followOnNexts.find(targetAtom);
|
||||
if (targetFollowOnAtomsIter == _followOnNexts.end())
|
||||
return;
|
||||
targetAtom = targetFollowOnAtomsIter->second;
|
||||
}
|
||||
}
|
||||
|
||||
/// This pass builds the followon tables described by two DenseMaps
|
||||
/// followOnRoots and followonNexts.
|
||||
/// The followOnRoots map contains a mapping of a DefinedAtom to its root
|
||||
/// The followOnNexts map contains a mapping of what DefinedAtom follows the
|
||||
/// current Atom
|
||||
/// The algorithm follows a very simple approach
|
||||
/// a) If the atom is first seen, then make that as the root atom
|
||||
/// b) The targetAtom which this Atom contains, has the root thats set to the
|
||||
/// root of the current atom
|
||||
/// c) If the targetAtom is part of a different tree and the root of the
|
||||
/// targetAtom is itself, Chain all the atoms that are contained in the tree
|
||||
/// to the current Tree
|
||||
/// d) If the targetAtom is part of a different chain and the root of the
|
||||
/// targetAtom until the targetAtom has all atoms of size 0, then chain the
|
||||
/// targetAtoms and its tree to the current chain
|
||||
void LayoutPass::buildFollowOnTable(const File::AtomRange<DefinedAtom> &range) {
|
||||
ScopedTask task(getDefaultDomain(), "LayoutPass::buildFollowOnTable");
|
||||
// Set the initial size of the followon and the followonNext hash to the
|
||||
// number of atoms that we have.
|
||||
_followOnRoots.reserve(range.size());
|
||||
_followOnNexts.reserve(range.size());
|
||||
for (const DefinedAtom *ai : range) {
|
||||
for (const Reference *r : *ai) {
|
||||
if (r->kindNamespace() != lld::Reference::KindNamespace::all ||
|
||||
r->kindValue() != lld::Reference::kindLayoutAfter)
|
||||
continue;
|
||||
const DefinedAtom *targetAtom = dyn_cast<DefinedAtom>(r->target());
|
||||
_followOnNexts[ai] = targetAtom;
|
||||
|
||||
// If we find a followon for the first time, let's make that atom as the
|
||||
// root atom.
|
||||
if (_followOnRoots.count(ai) == 0)
|
||||
_followOnRoots[ai] = ai;
|
||||
|
||||
auto iter = _followOnRoots.find(targetAtom);
|
||||
if (iter == _followOnRoots.end()) {
|
||||
// If the targetAtom is not a root of any chain, let's make the root of
|
||||
// the targetAtom to the root of the current chain.
|
||||
|
||||
// The expression m[i] = m[j] where m is a DenseMap and i != j is not
|
||||
// safe. m[j] returns a reference, which would be invalidated when a
|
||||
// rehashing occurs. If rehashing occurs to make room for m[i], m[j]
|
||||
// becomes invalid, and that invalid reference would be used as the RHS
|
||||
// value of the expression.
|
||||
// Copy the value to workaround.
|
||||
const DefinedAtom *tmp = _followOnRoots[ai];
|
||||
_followOnRoots[targetAtom] = tmp;
|
||||
continue;
|
||||
}
|
||||
if (iter->second == targetAtom) {
|
||||
// If the targetAtom is the root of a chain, the chain becomes part of
|
||||
// the current chain. Rewrite the subchain's root to the current
|
||||
// chain's root.
|
||||
setChainRoot(targetAtom, _followOnRoots[ai]);
|
||||
continue;
|
||||
}
|
||||
// The targetAtom is already a part of a chain. If the current atom is
|
||||
// of size zero, we can insert it in the middle of the chain just
|
||||
// before the target atom, while not breaking other atom's followon
|
||||
// relationships. If it's not, we can only insert the current atom at
|
||||
// the beginning of the chain. All the atoms followed by the target
|
||||
// atom must be of size zero in that case to satisfy the followon
|
||||
// relationships.
|
||||
size_t currentAtomSize = ai->size();
|
||||
if (currentAtomSize == 0) {
|
||||
const DefinedAtom *targetPrevAtom = findAtomFollowedBy(targetAtom);
|
||||
_followOnNexts[targetPrevAtom] = ai;
|
||||
const DefinedAtom *tmp = _followOnRoots[targetPrevAtom];
|
||||
_followOnRoots[ai] = tmp;
|
||||
continue;
|
||||
}
|
||||
if (!checkAllPrevAtomsZeroSize(targetAtom))
|
||||
break;
|
||||
_followOnNexts[ai] = _followOnRoots[targetAtom];
|
||||
setChainRoot(_followOnRoots[targetAtom], _followOnRoots[ai]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build an ordinal override map by traversing the followon chain, and
|
||||
/// assigning ordinals to each atom, if the atoms have their ordinals
|
||||
/// already assigned skip the atom and move to the next. This is the
|
||||
/// main map thats used to sort the atoms while comparing two atoms together
|
||||
void
|
||||
LayoutPass::buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range) {
|
||||
ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap");
|
||||
uint64_t index = 0;
|
||||
for (const DefinedAtom *ai : range) {
|
||||
const DefinedAtom *atom = ai;
|
||||
if (_ordinalOverrideMap.find(atom) != _ordinalOverrideMap.end())
|
||||
continue;
|
||||
AtomToAtomT::iterator start = _followOnRoots.find(atom);
|
||||
if (start == _followOnRoots.end())
|
||||
continue;
|
||||
for (const DefinedAtom *nextAtom = start->second; nextAtom;
|
||||
nextAtom = _followOnNexts[nextAtom]) {
|
||||
AtomToOrdinalT::iterator pos = _ordinalOverrideMap.find(nextAtom);
|
||||
if (pos == _ordinalOverrideMap.end())
|
||||
_ordinalOverrideMap[nextAtom] = index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LayoutPass::SortKey>
|
||||
LayoutPass::decorate(File::AtomRange<DefinedAtom> &atomRange) const {
|
||||
std::vector<SortKey> ret;
|
||||
for (OwningAtomPtr<DefinedAtom> &atom : atomRange.owning_ptrs()) {
|
||||
auto ri = _followOnRoots.find(atom.get());
|
||||
auto oi = _ordinalOverrideMap.find(atom.get());
|
||||
const auto *root = (ri == _followOnRoots.end()) ? atom.get() : ri->second;
|
||||
uint64_t override = (oi == _ordinalOverrideMap.end()) ? 0 : oi->second;
|
||||
ret.push_back(SortKey(std::move(atom), root, override));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LayoutPass::undecorate(File::AtomRange<DefinedAtom> &atomRange,
|
||||
std::vector<SortKey> &keys) const {
|
||||
size_t i = 0;
|
||||
for (SortKey &k : keys)
|
||||
atomRange[i++] = std::move(k._atom);
|
||||
}
|
||||
|
||||
/// Perform the actual pass
|
||||
llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "******** Laying out atoms:\n");
|
||||
// sort the atoms
|
||||
ScopedTask task(getDefaultDomain(), "LayoutPass");
|
||||
File::AtomRange<DefinedAtom> atomRange = mergedFile.defined();
|
||||
|
||||
// Build follow on tables
|
||||
buildFollowOnTable(atomRange);
|
||||
|
||||
// Check the structure of followon graph if running in debug mode.
|
||||
LLVM_DEBUG(checkFollowonChain(atomRange));
|
||||
|
||||
// Build override maps
|
||||
buildOrdinalOverrideMap(atomRange);
|
||||
|
||||
LLVM_DEBUG({
|
||||
llvm::dbgs() << "unsorted atoms:\n";
|
||||
printDefinedAtoms(atomRange);
|
||||
});
|
||||
|
||||
std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
|
||||
llvm::parallelSort(
|
||||
vec,
|
||||
[&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
|
||||
return compareAtoms(l, r, _customSorter);
|
||||
});
|
||||
LLVM_DEBUG(checkTransitivity(vec, _customSorter));
|
||||
undecorate(atomRange, vec);
|
||||
|
||||
LLVM_DEBUG({
|
||||
llvm::dbgs() << "sorted atoms:\n";
|
||||
printDefinedAtoms(atomRange);
|
||||
});
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n");
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
pm.add(std::make_unique<LayoutPass>(
|
||||
ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
|
||||
bool & leftBeforeRight) ->bool {
|
||||
return ctx.customAtomOrderer(left, right, leftBeforeRight);
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -1,118 +0,0 @@
|
|||
//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===//
|
||||
//
|
||||
// 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_LAYOUT_PASS_H
|
||||
#define LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
|
||||
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Pass.h"
|
||||
#include "lld/Core/Reader.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
class DefinedAtom;
|
||||
class SimpleFile;
|
||||
|
||||
namespace mach_o {
|
||||
|
||||
/// This linker pass does the layout of the atoms. The pass is done after the
|
||||
/// order their .o files were found on the command line, then by order of the
|
||||
/// atoms (address) in the .o file. But some atoms have a preferred location
|
||||
/// in their section (such as pinned to the start or end of the section), so
|
||||
/// the sort must take that into account too.
|
||||
class LayoutPass : public Pass {
|
||||
public:
|
||||
struct SortKey {
|
||||
SortKey(OwningAtomPtr<DefinedAtom> &&atom,
|
||||
const DefinedAtom *root, uint64_t override)
|
||||
: _atom(std::move(atom)), _root(root), _override(override) {}
|
||||
OwningAtomPtr<DefinedAtom> _atom;
|
||||
const DefinedAtom *_root;
|
||||
uint64_t _override;
|
||||
|
||||
// Note, these are only here to appease MSVC bots which didn't like
|
||||
// the same methods being implemented/deleted in OwningAtomPtr.
|
||||
SortKey(SortKey &&key) : _atom(std::move(key._atom)), _root(key._root),
|
||||
_override(key._override) {
|
||||
key._root = nullptr;
|
||||
}
|
||||
|
||||
SortKey &operator=(SortKey &&key) {
|
||||
_atom = std::move(key._atom);
|
||||
_root = key._root;
|
||||
key._root = nullptr;
|
||||
_override = key._override;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
SortKey(const SortKey &) = delete;
|
||||
void operator=(const SortKey&) = delete;
|
||||
};
|
||||
|
||||
typedef std::function<bool (const DefinedAtom *left, const DefinedAtom *right,
|
||||
bool &leftBeforeRight)> SortOverride;
|
||||
|
||||
LayoutPass(const Registry ®istry, SortOverride sorter);
|
||||
|
||||
/// Sorts atoms in mergedFile by content type then by command line order.
|
||||
llvm::Error perform(SimpleFile &mergedFile) override;
|
||||
|
||||
~LayoutPass() override = default;
|
||||
|
||||
private:
|
||||
// Build the followOn atoms chain as specified by the kindLayoutAfter
|
||||
// reference type
|
||||
void buildFollowOnTable(const File::AtomRange<DefinedAtom> &range);
|
||||
|
||||
// Build a map of Atoms to ordinals for sorting the atoms
|
||||
void buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range);
|
||||
|
||||
const Registry &_registry;
|
||||
SortOverride _customSorter;
|
||||
|
||||
typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
|
||||
typedef llvm::DenseMap<const DefinedAtom *, uint64_t> AtomToOrdinalT;
|
||||
|
||||
// A map to be used to sort atoms. It represents the order of atoms in the
|
||||
// result; if Atom X is mapped to atom Y in this map, X will be located
|
||||
// immediately before Y in the output file. Y might be mapped to another
|
||||
// atom, constructing a follow-on chain. An atom cannot be mapped to more
|
||||
// than one atom unless all but one atom are of size zero.
|
||||
AtomToAtomT _followOnNexts;
|
||||
|
||||
// A map to be used to sort atoms. It's a map from an atom to its root of
|
||||
// follow-on chain. A root atom is mapped to itself. If an atom is not in
|
||||
// _followOnNexts, the atom is not in this map, and vice versa.
|
||||
AtomToAtomT _followOnRoots;
|
||||
|
||||
AtomToOrdinalT _ordinalOverrideMap;
|
||||
|
||||
// Helper methods for buildFollowOnTable().
|
||||
const DefinedAtom *findAtomFollowedBy(const DefinedAtom *targetAtom);
|
||||
bool checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom);
|
||||
|
||||
void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root);
|
||||
|
||||
std::vector<SortKey> decorate(File::AtomRange<DefinedAtom> &atomRange) const;
|
||||
|
||||
void undecorate(File::AtomRange<DefinedAtom> &atomRange,
|
||||
std::vector<SortKey> &keys) const;
|
||||
|
||||
// Check if the follow-on graph is a correct structure. For debugging only.
|
||||
void checkFollowonChain(const File::AtomRange<DefinedAtom> &range);
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
|
File diff suppressed because it is too large
Load Diff
|
@ -1,336 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/MachONormalizedFile.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
///
|
||||
/// \file These data structures comprise the "normalized" view of
|
||||
/// mach-o object files. The normalized view is an in-memory only data structure
|
||||
/// which is always in native endianness and pointer size.
|
||||
///
|
||||
/// The normalized view easily converts to and from YAML using YAML I/O.
|
||||
///
|
||||
/// The normalized view converts to and from binary mach-o object files using
|
||||
/// the writeBinary() and readBinary() functions.
|
||||
///
|
||||
/// The normalized view converts to and from lld::Atoms using the
|
||||
/// normalizedToAtoms() and normalizedFromAtoms().
|
||||
///
|
||||
/// Overall, the conversion paths available look like:
|
||||
///
|
||||
/// +---------------+
|
||||
/// | binary mach-o |
|
||||
/// +---------------+
|
||||
/// ^
|
||||
/// |
|
||||
/// v
|
||||
/// +------------+ +------+
|
||||
/// | normalized | <-> | yaml |
|
||||
/// +------------+ +------+
|
||||
/// ^
|
||||
/// |
|
||||
/// v
|
||||
/// +-------+
|
||||
/// | Atoms |
|
||||
/// +-------+
|
||||
///
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
|
||||
#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
|
||||
|
||||
#include "DebugInfo.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
using llvm::BumpPtrAllocator;
|
||||
using llvm::yaml::Hex64;
|
||||
using llvm::yaml::Hex32;
|
||||
using llvm::yaml::Hex16;
|
||||
using llvm::yaml::Hex8;
|
||||
using llvm::yaml::SequenceTraits;
|
||||
using llvm::MachO::HeaderFileType;
|
||||
using llvm::MachO::BindType;
|
||||
using llvm::MachO::RebaseType;
|
||||
using llvm::MachO::NListType;
|
||||
using llvm::MachO::RelocationInfoType;
|
||||
using llvm::MachO::SectionType;
|
||||
using llvm::MachO::LoadCommandType;
|
||||
using llvm::MachO::ExportSymbolKind;
|
||||
using llvm::MachO::DataRegionType;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
namespace normalized {
|
||||
|
||||
|
||||
/// The real mach-o relocation record is 8-bytes on disk and is
|
||||
/// encoded in one of two different bit-field patterns. This
|
||||
/// normalized form has the union of all possible fields.
|
||||
struct Relocation {
|
||||
Relocation() : offset(0), scattered(false),
|
||||
type(llvm::MachO::GENERIC_RELOC_VANILLA),
|
||||
length(0), pcRel(false), isExtern(false), value(0),
|
||||
symbol(0) { }
|
||||
|
||||
Hex32 offset;
|
||||
bool scattered;
|
||||
RelocationInfoType type;
|
||||
uint8_t length;
|
||||
bool pcRel;
|
||||
bool isExtern;
|
||||
Hex32 value;
|
||||
uint32_t symbol;
|
||||
};
|
||||
|
||||
/// A typedef so that YAML I/O can treat this vector as a sequence.
|
||||
typedef std::vector<Relocation> Relocations;
|
||||
|
||||
/// A typedef so that YAML I/O can process the raw bytes in a section.
|
||||
typedef std::vector<Hex8> ContentBytes;
|
||||
|
||||
/// A typedef so that YAML I/O can treat indirect symbols as a flow sequence.
|
||||
typedef std::vector<uint32_t> IndirectSymbols;
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode section attributes.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, SectionAttr)
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode section alignment.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint16_t, SectionAlignment)
|
||||
|
||||
/// Mach-O has a 32-bit and 64-bit section record. This normalized form
|
||||
/// can support either kind.
|
||||
struct Section {
|
||||
Section() : type(llvm::MachO::S_REGULAR),
|
||||
attributes(0), alignment(1), address(0) { }
|
||||
|
||||
StringRef segmentName;
|
||||
StringRef sectionName;
|
||||
SectionType type;
|
||||
SectionAttr attributes;
|
||||
SectionAlignment alignment;
|
||||
Hex64 address;
|
||||
ArrayRef<uint8_t> content;
|
||||
Relocations relocations;
|
||||
IndirectSymbols indirectSymbols;
|
||||
};
|
||||
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode the scope bits of an nlist.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint8_t, SymbolScope)
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode the desc bits of an nlist.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint16_t, SymbolDesc)
|
||||
|
||||
/// Mach-O has a 32-bit and 64-bit symbol table entry (nlist), and the symbol
|
||||
/// type and scope and mixed in the same n_type field. This normalized form
|
||||
/// works for any pointer size and separates out the type and scope.
|
||||
struct Symbol {
|
||||
Symbol() : type(llvm::MachO::N_UNDF), scope(0), sect(0), desc(0), value(0) { }
|
||||
|
||||
StringRef name;
|
||||
NListType type;
|
||||
SymbolScope scope;
|
||||
uint8_t sect;
|
||||
SymbolDesc desc;
|
||||
Hex64 value;
|
||||
};
|
||||
|
||||
/// Check whether the given section type indicates a zero-filled section.
|
||||
// FIXME: Utility functions of this kind should probably be moved into
|
||||
// llvm/Support.
|
||||
inline bool isZeroFillSection(SectionType T) {
|
||||
return (T == llvm::MachO::S_ZEROFILL ||
|
||||
T == llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
|
||||
}
|
||||
|
||||
/// A typedef so that YAML I/O can (de/en)code the protection bits of a segment.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, VMProtect)
|
||||
|
||||
/// A typedef to hold verions X.Y.X packed into 32-bit xxxx.yy.zz
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, PackedVersion)
|
||||
|
||||
/// Segments are only used in normalized final linked images (not in relocatable
|
||||
/// object files). They specify how a range of the file is loaded.
|
||||
struct Segment {
|
||||
StringRef name;
|
||||
Hex64 address;
|
||||
Hex64 size;
|
||||
VMProtect init_access;
|
||||
VMProtect max_access;
|
||||
};
|
||||
|
||||
/// Only used in normalized final linked images to specify on which dylibs
|
||||
/// it depends.
|
||||
struct DependentDylib {
|
||||
StringRef path;
|
||||
LoadCommandType kind;
|
||||
PackedVersion compatVersion;
|
||||
PackedVersion currentVersion;
|
||||
};
|
||||
|
||||
/// A normalized rebasing entry. Only used in normalized final linked images.
|
||||
struct RebaseLocation {
|
||||
Hex32 segOffset;
|
||||
uint8_t segIndex;
|
||||
RebaseType kind;
|
||||
};
|
||||
|
||||
/// A normalized binding entry. Only used in normalized final linked images.
|
||||
struct BindLocation {
|
||||
Hex32 segOffset;
|
||||
uint8_t segIndex;
|
||||
BindType kind;
|
||||
bool canBeNull;
|
||||
int ordinal;
|
||||
StringRef symbolName;
|
||||
Hex64 addend;
|
||||
};
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode export flags.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, ExportFlags)
|
||||
|
||||
/// A normalized export entry. Only used in normalized final linked images.
|
||||
struct Export {
|
||||
StringRef name;
|
||||
Hex64 offset;
|
||||
ExportSymbolKind kind;
|
||||
ExportFlags flags;
|
||||
Hex32 otherOffset;
|
||||
StringRef otherName;
|
||||
};
|
||||
|
||||
/// A normalized data-in-code entry.
|
||||
struct DataInCode {
|
||||
Hex32 offset;
|
||||
Hex16 length;
|
||||
DataRegionType kind;
|
||||
};
|
||||
|
||||
/// A typedef so that YAML I/O can encode/decode mach_header.flags.
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags)
|
||||
|
||||
///
|
||||
struct NormalizedFile {
|
||||
MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
|
||||
HeaderFileType fileType = llvm::MachO::MH_OBJECT;
|
||||
FileFlags flags = 0;
|
||||
std::vector<Segment> segments; // Not used in object files.
|
||||
std::vector<Section> sections;
|
||||
|
||||
// Symbols sorted by kind.
|
||||
std::vector<Symbol> localSymbols;
|
||||
std::vector<Symbol> globalSymbols;
|
||||
std::vector<Symbol> undefinedSymbols;
|
||||
std::vector<Symbol> stabsSymbols;
|
||||
|
||||
// Maps to load commands with no LINKEDIT content (final linked images only).
|
||||
std::vector<DependentDylib> dependentDylibs;
|
||||
StringRef installName; // dylibs only
|
||||
PackedVersion compatVersion = 0; // dylibs only
|
||||
PackedVersion currentVersion = 0; // dylibs only
|
||||
bool hasUUID = false;
|
||||
bool hasMinVersionLoadCommand = false;
|
||||
bool generateDataInCodeLoadCommand = false;
|
||||
std::vector<StringRef> rpaths;
|
||||
Hex64 entryAddress = 0;
|
||||
Hex64 stackSize = 0;
|
||||
MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown;
|
||||
Hex64 sourceVersion = 0;
|
||||
PackedVersion minOSverson = 0;
|
||||
PackedVersion sdkVersion = 0;
|
||||
LoadCommandType minOSVersionKind = (LoadCommandType)0;
|
||||
|
||||
// Maps to load commands with LINKEDIT content (final linked images only).
|
||||
Hex32 pageSize = 0;
|
||||
std::vector<RebaseLocation> rebasingInfo;
|
||||
std::vector<BindLocation> bindingInfo;
|
||||
std::vector<BindLocation> weakBindingInfo;
|
||||
std::vector<BindLocation> lazyBindingInfo;
|
||||
std::vector<Export> exportInfo;
|
||||
std::vector<uint8_t> functionStarts;
|
||||
std::vector<DataInCode> dataInCode;
|
||||
|
||||
// TODO:
|
||||
// code-signature
|
||||
// split-seg-info
|
||||
// function-starts
|
||||
|
||||
// For any allocations in this struct which need to be owned by this struct.
|
||||
BumpPtrAllocator ownedAllocations;
|
||||
};
|
||||
|
||||
/// Tests if a file is a non-fat mach-o object file.
|
||||
bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch);
|
||||
|
||||
/// If the buffer is a fat file with the request arch, then this function
|
||||
/// returns true with 'offset' and 'size' set to location of the arch slice
|
||||
/// within the buffer. Otherwise returns false;
|
||||
bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
|
||||
uint32_t &offset, uint32_t &size);
|
||||
|
||||
/// Reads a mach-o file and produces an in-memory normalized view.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>>
|
||||
readBinary(std::unique_ptr<MemoryBuffer> &mb,
|
||||
const MachOLinkingContext::Arch arch);
|
||||
|
||||
/// Takes in-memory normalized view and writes a mach-o object file.
|
||||
llvm::Error writeBinary(const NormalizedFile &file, StringRef path);
|
||||
|
||||
size_t headerAndLoadCommandsSize(const NormalizedFile &file,
|
||||
bool includeFunctionStarts);
|
||||
|
||||
|
||||
/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>>
|
||||
readYaml(std::unique_ptr<MemoryBuffer> &mb);
|
||||
|
||||
/// Writes a yaml encoded mach-o files given an in-memory normalized view.
|
||||
std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out);
|
||||
|
||||
llvm::Error
|
||||
normalizedObjectToAtoms(MachOFile *file,
|
||||
const NormalizedFile &normalizedFile,
|
||||
bool copyRefs);
|
||||
|
||||
llvm::Error
|
||||
normalizedDylibToAtoms(MachODylibFile *file,
|
||||
const NormalizedFile &normalizedFile,
|
||||
bool copyRefs);
|
||||
|
||||
/// Takes in-memory normalized dylib or object and parses it into lld::File
|
||||
llvm::Expected<std::unique_ptr<lld::File>>
|
||||
normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
|
||||
bool copyRefs);
|
||||
|
||||
/// Takes atoms and generates a normalized macho-o view.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>>
|
||||
normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &ctxt);
|
||||
|
||||
|
||||
} // namespace normalized
|
||||
|
||||
/// Class for interfacing mach-o yaml files into generic yaml parsing
|
||||
class MachOYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
|
||||
public:
|
||||
MachOYamlIOTaggedDocumentHandler(MachOLinkingContext::Arch arch)
|
||||
: _arch(arch) { }
|
||||
bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override;
|
||||
private:
|
||||
const MachOLinkingContext::Arch _arch;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
|
|
@ -1,614 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
///
|
||||
/// \file For mach-o object files, this implementation converts from
|
||||
/// mach-o on-disk binary format to in-memory normalized mach-o.
|
||||
///
|
||||
/// +---------------+
|
||||
/// | binary mach-o |
|
||||
/// +---------------+
|
||||
/// |
|
||||
/// |
|
||||
/// v
|
||||
/// +------------+
|
||||
/// | normalized |
|
||||
/// +------------+
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/Object/MachO.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using llvm::object::ExportEntry;
|
||||
using llvm::file_magic;
|
||||
using llvm::object::MachOObjectFile;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
namespace normalized {
|
||||
|
||||
// Utility to call a lambda expression on each load command.
|
||||
static llvm::Error forEachLoadCommand(
|
||||
StringRef lcRange, unsigned lcCount, bool isBig, bool is64,
|
||||
std::function<bool(uint32_t cmd, uint32_t size, const char *lc)> func) {
|
||||
const char* p = lcRange.begin();
|
||||
for (unsigned i=0; i < lcCount; ++i) {
|
||||
const load_command *lc = reinterpret_cast<const load_command*>(p);
|
||||
load_command lcCopy;
|
||||
const load_command *slc = lc;
|
||||
if (isBig != llvm::sys::IsBigEndianHost) {
|
||||
memcpy(&lcCopy, lc, sizeof(load_command));
|
||||
swapStruct(lcCopy);
|
||||
slc = &lcCopy;
|
||||
}
|
||||
if ( (p + slc->cmdsize) > lcRange.end() )
|
||||
return llvm::make_error<GenericError>("Load command exceeds range");
|
||||
|
||||
if (func(slc->cmd, slc->cmdsize, p))
|
||||
return llvm::Error::success();
|
||||
|
||||
p += slc->cmdsize;
|
||||
}
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
static std::error_code appendRelocations(Relocations &relocs, StringRef buffer,
|
||||
bool bigEndian,
|
||||
uint32_t reloff, uint32_t nreloc) {
|
||||
if ((reloff + nreloc*8) > buffer.size())
|
||||
return make_error_code(llvm::errc::executable_format_error);
|
||||
const any_relocation_info* relocsArray =
|
||||
reinterpret_cast<const any_relocation_info*>(buffer.begin()+reloff);
|
||||
|
||||
for(uint32_t i=0; i < nreloc; ++i) {
|
||||
relocs.push_back(unpackRelocation(relocsArray[i], bigEndian));
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static std::error_code
|
||||
appendIndirectSymbols(IndirectSymbols &isyms, StringRef buffer, bool isBig,
|
||||
uint32_t istOffset, uint32_t istCount,
|
||||
uint32_t startIndex, uint32_t count) {
|
||||
if ((istOffset + istCount*4) > buffer.size())
|
||||
return make_error_code(llvm::errc::executable_format_error);
|
||||
if (startIndex+count > istCount)
|
||||
return make_error_code(llvm::errc::executable_format_error);
|
||||
const uint8_t *indirectSymbolArray = (const uint8_t *)buffer.data();
|
||||
|
||||
for(uint32_t i=0; i < count; ++i) {
|
||||
isyms.push_back(read32(
|
||||
indirectSymbolArray + (startIndex + i) * sizeof(uint32_t), isBig));
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
|
||||
template <typename T> static T readBigEndian(T t) {
|
||||
if (llvm::sys::IsLittleEndianHost)
|
||||
llvm::sys::swapByteOrder(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
static bool isMachOHeader(const mach_header *mh, bool &is64, bool &isBig) {
|
||||
switch (read32(&mh->magic, false)) {
|
||||
case llvm::MachO::MH_MAGIC:
|
||||
is64 = false;
|
||||
isBig = false;
|
||||
return true;
|
||||
case llvm::MachO::MH_MAGIC_64:
|
||||
is64 = true;
|
||||
isBig = false;
|
||||
return true;
|
||||
case llvm::MachO::MH_CIGAM:
|
||||
is64 = false;
|
||||
isBig = true;
|
||||
return true;
|
||||
case llvm::MachO::MH_CIGAM_64:
|
||||
is64 = true;
|
||||
isBig = true;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) {
|
||||
// Try opening and mapping file at path.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path);
|
||||
if (b.getError())
|
||||
return false;
|
||||
|
||||
// If file length < 32 it is too small to be mach-o object file.
|
||||
StringRef fileBuffer = b->get()->getBuffer();
|
||||
if (fileBuffer.size() < 32)
|
||||
return false;
|
||||
|
||||
// If file buffer does not start with MH_MAGIC (and variants), not obj file.
|
||||
const mach_header *mh = reinterpret_cast<const mach_header *>(
|
||||
fileBuffer.begin());
|
||||
bool is64, isBig;
|
||||
if (!isMachOHeader(mh, is64, isBig))
|
||||
return false;
|
||||
|
||||
// If not MH_OBJECT, not object file.
|
||||
if (read32(&mh->filetype, isBig) != MH_OBJECT)
|
||||
return false;
|
||||
|
||||
// Lookup up arch from cpu/subtype pair.
|
||||
arch = MachOLinkingContext::archFromCpuType(
|
||||
read32(&mh->cputype, isBig),
|
||||
read32(&mh->cpusubtype, isBig));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
|
||||
uint32_t &offset, uint32_t &size) {
|
||||
const char *start = mb.getBufferStart();
|
||||
const llvm::MachO::fat_header *fh =
|
||||
reinterpret_cast<const llvm::MachO::fat_header *>(start);
|
||||
if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC)
|
||||
return false;
|
||||
uint32_t nfat_arch = readBigEndian(fh->nfat_arch);
|
||||
const fat_arch *fstart =
|
||||
reinterpret_cast<const fat_arch *>(start + sizeof(fat_header));
|
||||
const fat_arch *fend =
|
||||
reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) +
|
||||
sizeof(fat_arch) * nfat_arch);
|
||||
const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch);
|
||||
const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch);
|
||||
for (const fat_arch *fa = fstart; fa < fend; ++fa) {
|
||||
if ((readBigEndian(fa->cputype) == reqCpuType) &&
|
||||
(readBigEndian(fa->cpusubtype) == reqCpuSubtype)) {
|
||||
offset = readBigEndian(fa->offset);
|
||||
size = readBigEndian(fa->size);
|
||||
if ((offset + size) > mb.getBufferSize())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Reads a mach-o file and produces an in-memory normalized view.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>>
|
||||
readBinary(std::unique_ptr<MemoryBuffer> &mb,
|
||||
const MachOLinkingContext::Arch arch) {
|
||||
// Make empty NormalizedFile.
|
||||
std::unique_ptr<NormalizedFile> f(new NormalizedFile());
|
||||
|
||||
const char *start = mb->getBufferStart();
|
||||
size_t objSize = mb->getBufferSize();
|
||||
const mach_header *mh = reinterpret_cast<const mach_header *>(start);
|
||||
|
||||
uint32_t sliceOffset;
|
||||
uint32_t sliceSize;
|
||||
if (sliceFromFatFile(mb->getMemBufferRef(), arch, sliceOffset, sliceSize)) {
|
||||
start = &start[sliceOffset];
|
||||
objSize = sliceSize;
|
||||
mh = reinterpret_cast<const mach_header *>(start);
|
||||
}
|
||||
|
||||
// Determine endianness and pointer size for mach-o file.
|
||||
bool is64, isBig;
|
||||
if (!isMachOHeader(mh, is64, isBig))
|
||||
return llvm::make_error<GenericError>("File is not a mach-o");
|
||||
|
||||
// Endian swap header, if needed.
|
||||
mach_header headerCopy;
|
||||
const mach_header *smh = mh;
|
||||
if (isBig != llvm::sys::IsBigEndianHost) {
|
||||
memcpy(&headerCopy, mh, sizeof(mach_header));
|
||||
swapStruct(headerCopy);
|
||||
smh = &headerCopy;
|
||||
}
|
||||
|
||||
// Validate head and load commands fit in buffer.
|
||||
const uint32_t lcCount = smh->ncmds;
|
||||
const char *lcStart =
|
||||
start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header));
|
||||
StringRef lcRange(lcStart, smh->sizeofcmds);
|
||||
if (lcRange.end() > (start + objSize))
|
||||
return llvm::make_error<GenericError>("Load commands exceed file size");
|
||||
|
||||
// Get architecture from mach_header.
|
||||
f->arch = MachOLinkingContext::archFromCpuType(smh->cputype, smh->cpusubtype);
|
||||
if (f->arch != arch) {
|
||||
return llvm::make_error<GenericError>(
|
||||
Twine("file is wrong architecture. Expected "
|
||||
"(" + MachOLinkingContext::nameFromArch(arch)
|
||||
+ ") found ("
|
||||
+ MachOLinkingContext::nameFromArch(f->arch)
|
||||
+ ")" ));
|
||||
}
|
||||
// Copy file type and flags
|
||||
f->fileType = HeaderFileType(smh->filetype);
|
||||
f->flags = smh->flags;
|
||||
|
||||
|
||||
// Pre-scan load commands looking for indirect symbol table.
|
||||
uint32_t indirectSymbolTableOffset = 0;
|
||||
uint32_t indirectSymbolTableCount = 0;
|
||||
auto ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
|
||||
[&](uint32_t cmd, uint32_t size,
|
||||
const char *lc) -> bool {
|
||||
if (cmd == LC_DYSYMTAB) {
|
||||
const dysymtab_command *d = reinterpret_cast<const dysymtab_command*>(lc);
|
||||
indirectSymbolTableOffset = read32(&d->indirectsymoff, isBig);
|
||||
indirectSymbolTableCount = read32(&d->nindirectsyms, isBig);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (ec)
|
||||
return std::move(ec);
|
||||
|
||||
// Walk load commands looking for segments/sections and the symbol table.
|
||||
const data_in_code_entry *dataInCode = nullptr;
|
||||
const dyld_info_command *dyldInfo = nullptr;
|
||||
uint32_t dataInCodeSize = 0;
|
||||
ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
|
||||
[&] (uint32_t cmd, uint32_t size, const char* lc) -> bool {
|
||||
switch(cmd) {
|
||||
case LC_SEGMENT_64:
|
||||
if (is64) {
|
||||
const segment_command_64 *seg =
|
||||
reinterpret_cast<const segment_command_64*>(lc);
|
||||
const unsigned sectionCount = read32(&seg->nsects, isBig);
|
||||
const section_64 *sects = reinterpret_cast<const section_64*>
|
||||
(lc + sizeof(segment_command_64));
|
||||
const unsigned lcSize = sizeof(segment_command_64)
|
||||
+ sectionCount*sizeof(section_64);
|
||||
// Verify sections don't extend beyond end of segment load command.
|
||||
if (lcSize > size)
|
||||
return true;
|
||||
for (unsigned i=0; i < sectionCount; ++i) {
|
||||
const section_64 *sect = §s[i];
|
||||
Section section;
|
||||
section.segmentName = getString16(sect->segname);
|
||||
section.sectionName = getString16(sect->sectname);
|
||||
section.type = (SectionType)(read32(§->flags, isBig) &
|
||||
SECTION_TYPE);
|
||||
section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES;
|
||||
section.alignment = 1 << read32(§->align, isBig);
|
||||
section.address = read64(§->addr, isBig);
|
||||
const uint8_t *content =
|
||||
(const uint8_t *)start + read32(§->offset, isBig);
|
||||
size_t contentSize = read64(§->size, isBig);
|
||||
// Note: this assign() is copying the content bytes. Ideally,
|
||||
// we can use a custom allocator for vector to avoid the copy.
|
||||
section.content = llvm::makeArrayRef(content, contentSize);
|
||||
appendRelocations(section.relocations, mb->getBuffer(), isBig,
|
||||
read32(§->reloff, isBig),
|
||||
read32(§->nreloc, isBig));
|
||||
if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
|
||||
appendIndirectSymbols(section.indirectSymbols, mb->getBuffer(),
|
||||
isBig,
|
||||
indirectSymbolTableOffset,
|
||||
indirectSymbolTableCount,
|
||||
read32(§->reserved1, isBig),
|
||||
contentSize/4);
|
||||
}
|
||||
f->sections.push_back(section);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LC_SEGMENT:
|
||||
if (!is64) {
|
||||
const segment_command *seg =
|
||||
reinterpret_cast<const segment_command*>(lc);
|
||||
const unsigned sectionCount = read32(&seg->nsects, isBig);
|
||||
const section *sects = reinterpret_cast<const section*>
|
||||
(lc + sizeof(segment_command));
|
||||
const unsigned lcSize = sizeof(segment_command)
|
||||
+ sectionCount*sizeof(section);
|
||||
// Verify sections don't extend beyond end of segment load command.
|
||||
if (lcSize > size)
|
||||
return true;
|
||||
for (unsigned i=0; i < sectionCount; ++i) {
|
||||
const section *sect = §s[i];
|
||||
Section section;
|
||||
section.segmentName = getString16(sect->segname);
|
||||
section.sectionName = getString16(sect->sectname);
|
||||
section.type = (SectionType)(read32(§->flags, isBig) &
|
||||
SECTION_TYPE);
|
||||
section.attributes =
|
||||
read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES;
|
||||
section.alignment = 1 << read32(§->align, isBig);
|
||||
section.address = read32(§->addr, isBig);
|
||||
const uint8_t *content =
|
||||
(const uint8_t *)start + read32(§->offset, isBig);
|
||||
size_t contentSize = read32(§->size, isBig);
|
||||
// Note: this assign() is copying the content bytes. Ideally,
|
||||
// we can use a custom allocator for vector to avoid the copy.
|
||||
section.content = llvm::makeArrayRef(content, contentSize);
|
||||
appendRelocations(section.relocations, mb->getBuffer(), isBig,
|
||||
read32(§->reloff, isBig),
|
||||
read32(§->nreloc, isBig));
|
||||
if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
|
||||
appendIndirectSymbols(
|
||||
section.indirectSymbols, mb->getBuffer(), isBig,
|
||||
indirectSymbolTableOffset, indirectSymbolTableCount,
|
||||
read32(§->reserved1, isBig), contentSize / 4);
|
||||
}
|
||||
f->sections.push_back(section);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LC_SYMTAB: {
|
||||
const symtab_command *st = reinterpret_cast<const symtab_command*>(lc);
|
||||
const char *strings = start + read32(&st->stroff, isBig);
|
||||
const uint32_t strSize = read32(&st->strsize, isBig);
|
||||
// Validate string pool and symbol table all in buffer.
|
||||
if (read32((const uint8_t *)&st->stroff, isBig) +
|
||||
read32((const uint8_t *)&st->strsize, isBig) >
|
||||
objSize)
|
||||
return true;
|
||||
if (is64) {
|
||||
const uint32_t symOffset = read32(&st->symoff, isBig);
|
||||
const uint32_t symCount = read32(&st->nsyms, isBig);
|
||||
if ( symOffset+(symCount*sizeof(nlist_64)) > objSize)
|
||||
return true;
|
||||
const nlist_64 *symbols =
|
||||
reinterpret_cast<const nlist_64 *>(start + symOffset);
|
||||
// Convert each nlist_64 to a lld::mach_o::normalized::Symbol.
|
||||
for(uint32_t i=0; i < symCount; ++i) {
|
||||
nlist_64 tempSym;
|
||||
memcpy(&tempSym, &symbols[i], sizeof(nlist_64));
|
||||
const nlist_64 *sin = &tempSym;
|
||||
if (isBig != llvm::sys::IsBigEndianHost)
|
||||
swapStruct(tempSym);
|
||||
Symbol sout;
|
||||
if (sin->n_strx > strSize)
|
||||
return true;
|
||||
sout.name = &strings[sin->n_strx];
|
||||
sout.type = static_cast<NListType>(sin->n_type & (N_STAB|N_TYPE));
|
||||
sout.scope = (sin->n_type & (N_PEXT|N_EXT));
|
||||
sout.sect = sin->n_sect;
|
||||
sout.desc = sin->n_desc;
|
||||
sout.value = sin->n_value;
|
||||
if (sin->n_type & N_STAB)
|
||||
f->stabsSymbols.push_back(sout);
|
||||
else if (sout.type == N_UNDF)
|
||||
f->undefinedSymbols.push_back(sout);
|
||||
else if (sin->n_type & N_EXT)
|
||||
f->globalSymbols.push_back(sout);
|
||||
else
|
||||
f->localSymbols.push_back(sout);
|
||||
}
|
||||
} else {
|
||||
const uint32_t symOffset = read32(&st->symoff, isBig);
|
||||
const uint32_t symCount = read32(&st->nsyms, isBig);
|
||||
if ( symOffset+(symCount*sizeof(nlist)) > objSize)
|
||||
return true;
|
||||
const nlist *symbols =
|
||||
reinterpret_cast<const nlist *>(start + symOffset);
|
||||
// Convert each nlist to a lld::mach_o::normalized::Symbol.
|
||||
for(uint32_t i=0; i < symCount; ++i) {
|
||||
const nlist *sin = &symbols[i];
|
||||
nlist tempSym;
|
||||
if (isBig != llvm::sys::IsBigEndianHost) {
|
||||
tempSym = *sin; swapStruct(tempSym); sin = &tempSym;
|
||||
}
|
||||
Symbol sout;
|
||||
if (sin->n_strx > strSize)
|
||||
return true;
|
||||
sout.name = &strings[sin->n_strx];
|
||||
sout.type = (NListType)(sin->n_type & N_TYPE);
|
||||
sout.scope = (sin->n_type & (N_PEXT|N_EXT));
|
||||
sout.sect = sin->n_sect;
|
||||
sout.desc = sin->n_desc;
|
||||
sout.value = sin->n_value;
|
||||
if (sout.type == N_UNDF)
|
||||
f->undefinedSymbols.push_back(sout);
|
||||
else if (sout.scope == (SymbolScope)N_EXT)
|
||||
f->globalSymbols.push_back(sout);
|
||||
else if (sin->n_type & N_STAB)
|
||||
f->stabsSymbols.push_back(sout);
|
||||
else
|
||||
f->localSymbols.push_back(sout);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LC_ID_DYLIB: {
|
||||
const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
|
||||
f->installName = lc + read32(&dl->dylib.name, isBig);
|
||||
f->currentVersion = read32(&dl->dylib.current_version, isBig);
|
||||
f->compatVersion = read32(&dl->dylib.compatibility_version, isBig);
|
||||
}
|
||||
break;
|
||||
case LC_DATA_IN_CODE: {
|
||||
const linkedit_data_command *ldc =
|
||||
reinterpret_cast<const linkedit_data_command*>(lc);
|
||||
dataInCode = reinterpret_cast<const data_in_code_entry *>(
|
||||
start + read32(&ldc->dataoff, isBig));
|
||||
dataInCodeSize = read32(&ldc->datasize, isBig);
|
||||
}
|
||||
break;
|
||||
case LC_LOAD_DYLIB:
|
||||
case LC_LOAD_WEAK_DYLIB:
|
||||
case LC_REEXPORT_DYLIB:
|
||||
case LC_LOAD_UPWARD_DYLIB: {
|
||||
const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
|
||||
DependentDylib entry;
|
||||
entry.path = lc + read32(&dl->dylib.name, isBig);
|
||||
entry.kind = LoadCommandType(cmd);
|
||||
entry.compatVersion = read32(&dl->dylib.compatibility_version, isBig);
|
||||
entry.currentVersion = read32(&dl->dylib.current_version, isBig);
|
||||
f->dependentDylibs.push_back(entry);
|
||||
}
|
||||
break;
|
||||
case LC_RPATH: {
|
||||
const rpath_command *rpc = reinterpret_cast<const rpath_command *>(lc);
|
||||
f->rpaths.push_back(lc + read32(&rpc->path, isBig));
|
||||
}
|
||||
break;
|
||||
case LC_DYLD_INFO:
|
||||
case LC_DYLD_INFO_ONLY:
|
||||
dyldInfo = reinterpret_cast<const dyld_info_command*>(lc);
|
||||
break;
|
||||
case LC_VERSION_MIN_MACOSX:
|
||||
case LC_VERSION_MIN_IPHONEOS:
|
||||
case LC_VERSION_MIN_WATCHOS:
|
||||
case LC_VERSION_MIN_TVOS:
|
||||
// If we are emitting an object file, then we may take the load command
|
||||
// kind from these commands and pass it on to the output
|
||||
// file.
|
||||
f->minOSVersionKind = (LoadCommandType)cmd;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (ec)
|
||||
return std::move(ec);
|
||||
|
||||
if (dataInCode) {
|
||||
// Convert on-disk data_in_code_entry array to DataInCode vector.
|
||||
for (unsigned i=0; i < dataInCodeSize/sizeof(data_in_code_entry); ++i) {
|
||||
DataInCode entry;
|
||||
entry.offset = read32(&dataInCode[i].offset, isBig);
|
||||
entry.length = read16(&dataInCode[i].length, isBig);
|
||||
entry.kind =
|
||||
(DataRegionType)read16((const uint8_t *)&dataInCode[i].kind, isBig);
|
||||
f->dataInCode.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (dyldInfo) {
|
||||
// If any exports, extract and add to normalized exportInfo vector.
|
||||
if (dyldInfo->export_size) {
|
||||
const uint8_t *trieStart = reinterpret_cast<const uint8_t *>(
|
||||
start + read32(&dyldInfo->export_off, isBig));
|
||||
ArrayRef<uint8_t> trie(trieStart, read32(&dyldInfo->export_size, isBig));
|
||||
Error Err = Error::success();
|
||||
for (const ExportEntry &trieExport : MachOObjectFile::exports(Err, trie)) {
|
||||
Export normExport;
|
||||
normExport.name = trieExport.name().copy(f->ownedAllocations);
|
||||
normExport.offset = trieExport.address();
|
||||
normExport.kind = ExportSymbolKind(trieExport.flags() & EXPORT_SYMBOL_FLAGS_KIND_MASK);
|
||||
normExport.flags = trieExport.flags() & ~EXPORT_SYMBOL_FLAGS_KIND_MASK;
|
||||
normExport.otherOffset = trieExport.other();
|
||||
if (!trieExport.otherName().empty())
|
||||
normExport.otherName = trieExport.otherName().copy(f->ownedAllocations);
|
||||
f->exportInfo.push_back(normExport);
|
||||
}
|
||||
if (Err)
|
||||
return std::move(Err);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(f);
|
||||
}
|
||||
|
||||
class MachOObjectReader : public Reader {
|
||||
public:
|
||||
MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
|
||||
|
||||
bool canParse(file_magic magic, MemoryBufferRef mb) const override {
|
||||
return (magic == file_magic::macho_object && mb.getBufferSize() > 32);
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<File>>
|
||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®istry) const override {
|
||||
std::unique_ptr<File> ret =
|
||||
std::make_unique<MachOFile>(std::move(mb), &_ctx);
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
MachOLinkingContext &_ctx;
|
||||
};
|
||||
|
||||
class MachODylibReader : public Reader {
|
||||
public:
|
||||
MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
|
||||
|
||||
bool canParse(file_magic magic, MemoryBufferRef mb) const override {
|
||||
switch (magic) {
|
||||
case file_magic::macho_dynamically_linked_shared_lib:
|
||||
case file_magic::macho_dynamically_linked_shared_lib_stub:
|
||||
return mb.getBufferSize() > 32;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<File>>
|
||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®istry) const override {
|
||||
std::unique_ptr<File> ret =
|
||||
std::make_unique<MachODylibFile>(std::move(mb), &_ctx);
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
MachOLinkingContext &_ctx;
|
||||
};
|
||||
|
||||
class MachOTAPIReader : public Reader {
|
||||
public:
|
||||
MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
|
||||
|
||||
bool canParse(file_magic magic, MemoryBufferRef mb) const override {
|
||||
return magic == file_magic::tapi_file;
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<File>>
|
||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®istry) const override {
|
||||
std::unique_ptr<File> ret =
|
||||
std::make_unique<TAPIFile>(std::move(mb), &_ctx);
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
MachOLinkingContext &_ctx;
|
||||
};
|
||||
|
||||
} // namespace normalized
|
||||
} // namespace mach_o
|
||||
|
||||
void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) {
|
||||
MachOLinkingContext::Arch arch = ctx.arch();
|
||||
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
|
||||
add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
|
||||
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
|
||||
addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
|
||||
ctx.archHandler().kindStrings());
|
||||
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
|
||||
new mach_o::MachOYamlIOTaggedDocumentHandler(arch)));
|
||||
}
|
||||
|
||||
|
||||
} // namespace lld
|
|
@ -1,213 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.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_NORMALIZED_FILE_BINARY_UTILS_H
|
||||
#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/Error.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include <system_error>
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
namespace normalized {
|
||||
|
||||
class ByteBuffer {
|
||||
public:
|
||||
ByteBuffer() : _ostream(_bytes) { }
|
||||
|
||||
void append_byte(uint8_t b) {
|
||||
_ostream << b;
|
||||
}
|
||||
void append_uleb128(uint64_t value) {
|
||||
llvm::encodeULEB128(value, _ostream);
|
||||
}
|
||||
void append_uleb128Fixed(uint64_t value, unsigned byteCount) {
|
||||
unsigned min = llvm::getULEB128Size(value);
|
||||
assert(min <= byteCount);
|
||||
unsigned pad = byteCount - min;
|
||||
llvm::encodeULEB128(value, _ostream, pad);
|
||||
}
|
||||
void append_sleb128(int64_t value) {
|
||||
llvm::encodeSLEB128(value, _ostream);
|
||||
}
|
||||
void append_string(StringRef str) {
|
||||
_ostream << str;
|
||||
append_byte(0);
|
||||
}
|
||||
void align(unsigned alignment) {
|
||||
while ( (_ostream.tell() % alignment) != 0 )
|
||||
append_byte(0);
|
||||
}
|
||||
size_t size() {
|
||||
return _ostream.tell();
|
||||
}
|
||||
const uint8_t *bytes() {
|
||||
return reinterpret_cast<const uint8_t*>(_ostream.str().data());
|
||||
}
|
||||
|
||||
private:
|
||||
SmallVector<char, 128> _bytes;
|
||||
// Stream ivar must be after SmallVector ivar to construct properly.
|
||||
llvm::raw_svector_ostream _ostream;
|
||||
};
|
||||
|
||||
using namespace llvm::support::endian;
|
||||
using llvm::sys::getSwappedBytes;
|
||||
|
||||
template<typename T>
|
||||
static inline uint16_t read16(const T *loc, bool isBig) {
|
||||
assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
|
||||
return isBig ? read16be(loc) : read16le(loc);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline uint32_t read32(const T *loc, bool isBig) {
|
||||
assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
|
||||
return isBig ? read32be(loc) : read32le(loc);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline uint64_t read64(const T *loc, bool isBig) {
|
||||
assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
|
||||
return isBig ? read64be(loc) : read64le(loc);
|
||||
}
|
||||
|
||||
inline void write16(uint8_t *loc, uint16_t value, bool isBig) {
|
||||
if (isBig)
|
||||
write16be(loc, value);
|
||||
else
|
||||
write16le(loc, value);
|
||||
}
|
||||
|
||||
inline void write32(uint8_t *loc, uint32_t value, bool isBig) {
|
||||
if (isBig)
|
||||
write32be(loc, value);
|
||||
else
|
||||
write32le(loc, value);
|
||||
}
|
||||
|
||||
inline void write64(uint8_t *loc, uint64_t value, bool isBig) {
|
||||
if (isBig)
|
||||
write64be(loc, value);
|
||||
else
|
||||
write64le(loc, value);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit,
|
||||
uint8_t bitCount) {
|
||||
const uint32_t mask = ((1<<bitCount)-1);
|
||||
const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
|
||||
return (value >> shift) & mask;
|
||||
}
|
||||
|
||||
inline void
|
||||
bitFieldSet(uint32_t &bits, bool isBigEndianBigField, uint32_t newBits,
|
||||
uint8_t firstBit, uint8_t bitCount) {
|
||||
const uint32_t mask = ((1<<bitCount)-1);
|
||||
assert((newBits & mask) == newBits);
|
||||
const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
|
||||
bits &= ~(mask << shift);
|
||||
bits |= (newBits << shift);
|
||||
}
|
||||
|
||||
inline Relocation unpackRelocation(const llvm::MachO::any_relocation_info &r,
|
||||
bool isBigEndian) {
|
||||
uint32_t r0 = read32(&r.r_word0, isBigEndian);
|
||||
uint32_t r1 = read32(&r.r_word1, isBigEndian);
|
||||
|
||||
Relocation result;
|
||||
if (r0 & llvm::MachO::R_SCATTERED) {
|
||||
// scattered relocation record always laid out like big endian bit field
|
||||
result.offset = bitFieldExtract(r0, true, 8, 24);
|
||||
result.scattered = true;
|
||||
result.type = (RelocationInfoType)
|
||||
bitFieldExtract(r0, true, 4, 4);
|
||||
result.length = bitFieldExtract(r0, true, 2, 2);
|
||||
result.pcRel = bitFieldExtract(r0, true, 1, 1);
|
||||
result.isExtern = false;
|
||||
result.value = r1;
|
||||
result.symbol = 0;
|
||||
} else {
|
||||
result.offset = r0;
|
||||
result.scattered = false;
|
||||
result.type = (RelocationInfoType)
|
||||
bitFieldExtract(r1, isBigEndian, 28, 4);
|
||||
result.length = bitFieldExtract(r1, isBigEndian, 25, 2);
|
||||
result.pcRel = bitFieldExtract(r1, isBigEndian, 24, 1);
|
||||
result.isExtern = bitFieldExtract(r1, isBigEndian, 27, 1);
|
||||
result.value = 0;
|
||||
result.symbol = bitFieldExtract(r1, isBigEndian, 0, 24);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
inline llvm::MachO::any_relocation_info
|
||||
packRelocation(const Relocation &r, bool swap, bool isBigEndian) {
|
||||
uint32_t r0 = 0;
|
||||
uint32_t r1 = 0;
|
||||
|
||||
if (r.scattered) {
|
||||
r1 = r.value;
|
||||
bitFieldSet(r0, true, r.offset, 8, 24);
|
||||
bitFieldSet(r0, true, r.type, 4, 4);
|
||||
bitFieldSet(r0, true, r.length, 2, 2);
|
||||
bitFieldSet(r0, true, r.pcRel, 1, 1);
|
||||
bitFieldSet(r0, true, r.scattered, 0, 1); // R_SCATTERED
|
||||
} else {
|
||||
r0 = r.offset;
|
||||
bitFieldSet(r1, isBigEndian, r.type, 28, 4);
|
||||
bitFieldSet(r1, isBigEndian, r.isExtern, 27, 1);
|
||||
bitFieldSet(r1, isBigEndian, r.length, 25, 2);
|
||||
bitFieldSet(r1, isBigEndian, r.pcRel, 24, 1);
|
||||
bitFieldSet(r1, isBigEndian, r.symbol, 0, 24);
|
||||
}
|
||||
|
||||
llvm::MachO::any_relocation_info result;
|
||||
result.r_word0 = swap ? getSwappedBytes(r0) : r0;
|
||||
result.r_word1 = swap ? getSwappedBytes(r1) : r1;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline StringRef getString16(const char s[16]) {
|
||||
// The StringRef(const char *) constructor passes the const char * to
|
||||
// strlen(), so we can't use this constructor here, because if there is no
|
||||
// null terminator in s, then strlen() will read past the end of the array.
|
||||
return StringRef(s, strnlen(s, 16));
|
||||
}
|
||||
|
||||
inline void setString16(StringRef str, char s[16]) {
|
||||
memset(s, 0, 16);
|
||||
memcpy(s, str.begin(), (str.size() > 16) ? 16: str.size());
|
||||
}
|
||||
|
||||
// Implemented in normalizedToAtoms() and used by normalizedFromAtoms() so
|
||||
// that the same table can be used to map mach-o sections to and from
|
||||
// DefinedAtom::ContentType.
|
||||
void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
|
||||
StringRef &segmentName,
|
||||
StringRef §ionName,
|
||||
SectionType §ionType,
|
||||
SectionAttr §ionAttrs,
|
||||
bool &relocsToDefinedCanBeImplicit);
|
||||
|
||||
} // namespace normalized
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,840 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
///
|
||||
/// \file For mach-o object files, this implementation uses YAML I/O to
|
||||
/// provide the convert between YAML and the normalized mach-o (NM).
|
||||
///
|
||||
/// +------------+ +------+
|
||||
/// | normalized | <-> | yaml |
|
||||
/// +------------+ +------+
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/ReaderWriter/YamlContext.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <system_error>
|
||||
|
||||
using llvm::StringRef;
|
||||
using namespace llvm::yaml;
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
using lld::YamlContext;
|
||||
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(Segment)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(Export)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode)
|
||||
|
||||
|
||||
// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
|
||||
// A vector of Sections is a sequence.
|
||||
template<>
|
||||
struct SequenceTraits< std::vector<Section> > {
|
||||
static size_t size(IO &io, std::vector<Section> &seq) {
|
||||
return seq.size();
|
||||
}
|
||||
static Section& element(IO &io, std::vector<Section> &seq, size_t index) {
|
||||
if ( index >= seq.size() )
|
||||
seq.resize(index+1);
|
||||
return seq[index];
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SequenceTraits< std::vector<Symbol> > {
|
||||
static size_t size(IO &io, std::vector<Symbol> &seq) {
|
||||
return seq.size();
|
||||
}
|
||||
static Symbol& element(IO &io, std::vector<Symbol> &seq, size_t index) {
|
||||
if ( index >= seq.size() )
|
||||
seq.resize(index+1);
|
||||
return seq[index];
|
||||
}
|
||||
};
|
||||
|
||||
// A vector of Relocations is a sequence.
|
||||
template<>
|
||||
struct SequenceTraits< Relocations > {
|
||||
static size_t size(IO &io, Relocations &seq) {
|
||||
return seq.size();
|
||||
}
|
||||
static Relocation& element(IO &io, Relocations &seq, size_t index) {
|
||||
if ( index >= seq.size() )
|
||||
seq.resize(index+1);
|
||||
return seq[index];
|
||||
}
|
||||
};
|
||||
|
||||
// The content for a section is represented as a flow sequence of hex bytes.
|
||||
template<>
|
||||
struct SequenceTraits< ContentBytes > {
|
||||
static size_t size(IO &io, ContentBytes &seq) {
|
||||
return seq.size();
|
||||
}
|
||||
static Hex8& element(IO &io, ContentBytes &seq, size_t index) {
|
||||
if ( index >= seq.size() )
|
||||
seq.resize(index+1);
|
||||
return seq[index];
|
||||
}
|
||||
static const bool flow = true;
|
||||
};
|
||||
|
||||
// The indirect symbols for a section is represented as a flow sequence
|
||||
// of numbers (symbol table indexes).
|
||||
template<>
|
||||
struct SequenceTraits< IndirectSymbols > {
|
||||
static size_t size(IO &io, IndirectSymbols &seq) {
|
||||
return seq.size();
|
||||
}
|
||||
static uint32_t& element(IO &io, IndirectSymbols &seq, size_t index) {
|
||||
if ( index >= seq.size() )
|
||||
seq.resize(index+1);
|
||||
return seq[index];
|
||||
}
|
||||
static const bool flow = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> {
|
||||
static void enumeration(IO &io, lld::MachOLinkingContext::Arch &value) {
|
||||
io.enumCase(value, "unknown",lld::MachOLinkingContext::arch_unknown);
|
||||
io.enumCase(value, "ppc", lld::MachOLinkingContext::arch_ppc);
|
||||
io.enumCase(value, "x86", lld::MachOLinkingContext::arch_x86);
|
||||
io.enumCase(value, "x86_64", lld::MachOLinkingContext::arch_x86_64);
|
||||
io.enumCase(value, "armv6", lld::MachOLinkingContext::arch_armv6);
|
||||
io.enumCase(value, "armv7", lld::MachOLinkingContext::arch_armv7);
|
||||
io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s);
|
||||
io.enumCase(value, "arm64", lld::MachOLinkingContext::arch_arm64);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<lld::MachOLinkingContext::OS> {
|
||||
static void enumeration(IO &io, lld::MachOLinkingContext::OS &value) {
|
||||
io.enumCase(value, "unknown",
|
||||
lld::MachOLinkingContext::OS::unknown);
|
||||
io.enumCase(value, "Mac OS X",
|
||||
lld::MachOLinkingContext::OS::macOSX);
|
||||
io.enumCase(value, "iOS",
|
||||
lld::MachOLinkingContext::OS::iOS);
|
||||
io.enumCase(value, "iOS Simulator",
|
||||
lld::MachOLinkingContext::OS::iOS_simulator);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<HeaderFileType> {
|
||||
static void enumeration(IO &io, HeaderFileType &value) {
|
||||
io.enumCase(value, "MH_OBJECT", llvm::MachO::MH_OBJECT);
|
||||
io.enumCase(value, "MH_DYLIB", llvm::MachO::MH_DYLIB);
|
||||
io.enumCase(value, "MH_EXECUTE", llvm::MachO::MH_EXECUTE);
|
||||
io.enumCase(value, "MH_BUNDLE", llvm::MachO::MH_BUNDLE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<FileFlags> {
|
||||
static void bitset(IO &io, FileFlags &value) {
|
||||
io.bitSetCase(value, "MH_TWOLEVEL",
|
||||
llvm::MachO::MH_TWOLEVEL);
|
||||
io.bitSetCase(value, "MH_SUBSECTIONS_VIA_SYMBOLS",
|
||||
llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<SectionType> {
|
||||
static void enumeration(IO &io, SectionType &value) {
|
||||
io.enumCase(value, "S_REGULAR",
|
||||
llvm::MachO::S_REGULAR);
|
||||
io.enumCase(value, "S_ZEROFILL",
|
||||
llvm::MachO::S_ZEROFILL);
|
||||
io.enumCase(value, "S_CSTRING_LITERALS",
|
||||
llvm::MachO::S_CSTRING_LITERALS);
|
||||
io.enumCase(value, "S_4BYTE_LITERALS",
|
||||
llvm::MachO::S_4BYTE_LITERALS);
|
||||
io.enumCase(value, "S_8BYTE_LITERALS",
|
||||
llvm::MachO::S_8BYTE_LITERALS);
|
||||
io.enumCase(value, "S_LITERAL_POINTERS",
|
||||
llvm::MachO::S_LITERAL_POINTERS);
|
||||
io.enumCase(value, "S_NON_LAZY_SYMBOL_POINTERS",
|
||||
llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS);
|
||||
io.enumCase(value, "S_LAZY_SYMBOL_POINTERS",
|
||||
llvm::MachO::S_LAZY_SYMBOL_POINTERS);
|
||||
io.enumCase(value, "S_SYMBOL_STUBS",
|
||||
llvm::MachO::S_SYMBOL_STUBS);
|
||||
io.enumCase(value, "S_MOD_INIT_FUNC_POINTERS",
|
||||
llvm::MachO::S_MOD_INIT_FUNC_POINTERS);
|
||||
io.enumCase(value, "S_MOD_TERM_FUNC_POINTERS",
|
||||
llvm::MachO::S_MOD_TERM_FUNC_POINTERS);
|
||||
io.enumCase(value, "S_COALESCED",
|
||||
llvm::MachO::S_COALESCED);
|
||||
io.enumCase(value, "S_GB_ZEROFILL",
|
||||
llvm::MachO::S_GB_ZEROFILL);
|
||||
io.enumCase(value, "S_INTERPOSING",
|
||||
llvm::MachO::S_INTERPOSING);
|
||||
io.enumCase(value, "S_16BYTE_LITERALS",
|
||||
llvm::MachO::S_16BYTE_LITERALS);
|
||||
io.enumCase(value, "S_DTRACE_DOF",
|
||||
llvm::MachO::S_DTRACE_DOF);
|
||||
io.enumCase(value, "S_LAZY_DYLIB_SYMBOL_POINTERS",
|
||||
llvm::MachO::S_LAZY_DYLIB_SYMBOL_POINTERS);
|
||||
io.enumCase(value, "S_THREAD_LOCAL_REGULAR",
|
||||
llvm::MachO::S_THREAD_LOCAL_REGULAR);
|
||||
io.enumCase(value, "S_THREAD_LOCAL_ZEROFILL",
|
||||
llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
|
||||
io.enumCase(value, "S_THREAD_LOCAL_VARIABLES",
|
||||
llvm::MachO::S_THREAD_LOCAL_VARIABLES);
|
||||
io.enumCase(value, "S_THREAD_LOCAL_VARIABLE_POINTERS",
|
||||
llvm::MachO::S_THREAD_LOCAL_VARIABLE_POINTERS);
|
||||
io.enumCase(value, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS",
|
||||
llvm::MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<SectionAttr> {
|
||||
static void bitset(IO &io, SectionAttr &value) {
|
||||
io.bitSetCase(value, "S_ATTR_PURE_INSTRUCTIONS",
|
||||
llvm::MachO::S_ATTR_PURE_INSTRUCTIONS);
|
||||
io.bitSetCase(value, "S_ATTR_SOME_INSTRUCTIONS",
|
||||
llvm::MachO::S_ATTR_SOME_INSTRUCTIONS);
|
||||
io.bitSetCase(value, "S_ATTR_NO_DEAD_STRIP",
|
||||
llvm::MachO::S_ATTR_NO_DEAD_STRIP);
|
||||
io.bitSetCase(value, "S_ATTR_EXT_RELOC",
|
||||
llvm::MachO::S_ATTR_EXT_RELOC);
|
||||
io.bitSetCase(value, "S_ATTR_LOC_RELOC",
|
||||
llvm::MachO::S_ATTR_LOC_RELOC);
|
||||
io.bitSetCase(value, "S_ATTR_DEBUG",
|
||||
llvm::MachO::S_ATTR_DEBUG);
|
||||
}
|
||||
};
|
||||
|
||||
/// This is a custom formatter for SectionAlignment. Values are
|
||||
/// the power to raise by, ie, the n in 2^n.
|
||||
template <> struct ScalarTraits<SectionAlignment> {
|
||||
static void output(const SectionAlignment &value, void *ctxt,
|
||||
raw_ostream &out) {
|
||||
out << llvm::format("%d", (uint32_t)value);
|
||||
}
|
||||
|
||||
static StringRef input(StringRef scalar, void *ctxt,
|
||||
SectionAlignment &value) {
|
||||
uint32_t alignment;
|
||||
if (scalar.getAsInteger(0, alignment)) {
|
||||
return "malformed alignment value";
|
||||
}
|
||||
if (!llvm::isPowerOf2_32(alignment))
|
||||
return "alignment must be a power of 2";
|
||||
value = alignment;
|
||||
return StringRef(); // returning empty string means success
|
||||
}
|
||||
|
||||
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<NListType> {
|
||||
static void enumeration(IO &io, NListType &value) {
|
||||
io.enumCase(value, "N_UNDF", llvm::MachO::N_UNDF);
|
||||
io.enumCase(value, "N_ABS", llvm::MachO::N_ABS);
|
||||
io.enumCase(value, "N_SECT", llvm::MachO::N_SECT);
|
||||
io.enumCase(value, "N_PBUD", llvm::MachO::N_PBUD);
|
||||
io.enumCase(value, "N_INDR", llvm::MachO::N_INDR);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<SymbolScope> {
|
||||
static void bitset(IO &io, SymbolScope &value) {
|
||||
io.bitSetCase(value, "N_EXT", llvm::MachO::N_EXT);
|
||||
io.bitSetCase(value, "N_PEXT", llvm::MachO::N_PEXT);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<SymbolDesc> {
|
||||
static void bitset(IO &io, SymbolDesc &value) {
|
||||
io.bitSetCase(value, "N_NO_DEAD_STRIP", llvm::MachO::N_NO_DEAD_STRIP);
|
||||
io.bitSetCase(value, "N_WEAK_REF", llvm::MachO::N_WEAK_REF);
|
||||
io.bitSetCase(value, "N_WEAK_DEF", llvm::MachO::N_WEAK_DEF);
|
||||
io.bitSetCase(value, "N_ARM_THUMB_DEF", llvm::MachO::N_ARM_THUMB_DEF);
|
||||
io.bitSetCase(value, "N_SYMBOL_RESOLVER", llvm::MachO::N_SYMBOL_RESOLVER);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<Section> {
|
||||
struct NormalizedContentBytes;
|
||||
static void mapping(IO &io, Section §) {
|
||||
io.mapRequired("segment", sect.segmentName);
|
||||
io.mapRequired("section", sect.sectionName);
|
||||
io.mapRequired("type", sect.type);
|
||||
io.mapOptional("attributes", sect.attributes);
|
||||
io.mapOptional("alignment", sect.alignment, (SectionAlignment)1);
|
||||
io.mapRequired("address", sect.address);
|
||||
if (isZeroFillSection(sect.type)) {
|
||||
// S_ZEROFILL sections use "size:" instead of "content:"
|
||||
uint64_t size = sect.content.size();
|
||||
io.mapOptional("size", size);
|
||||
if (!io.outputting()) {
|
||||
uint8_t *bytes = nullptr;
|
||||
sect.content = makeArrayRef(bytes, size);
|
||||
}
|
||||
} else {
|
||||
MappingNormalization<NormalizedContent, ArrayRef<uint8_t>> content(
|
||||
io, sect.content);
|
||||
io.mapOptional("content", content->_normalizedContent);
|
||||
}
|
||||
io.mapOptional("relocations", sect.relocations);
|
||||
io.mapOptional("indirect-syms", sect.indirectSymbols);
|
||||
}
|
||||
|
||||
struct NormalizedContent {
|
||||
NormalizedContent(IO &io) : _io(io) {}
|
||||
NormalizedContent(IO &io, ArrayRef<uint8_t> content) : _io(io) {
|
||||
// When writing yaml, copy content byte array to Hex8 vector.
|
||||
for (auto &c : content) {
|
||||
_normalizedContent.push_back(c);
|
||||
}
|
||||
}
|
||||
ArrayRef<uint8_t> denormalize(IO &io) {
|
||||
// When reading yaml, allocate byte array owned by NormalizedFile and
|
||||
// copy Hex8 vector to byte array.
|
||||
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
|
||||
assert(info != nullptr);
|
||||
NormalizedFile *file = info->_normalizeMachOFile;
|
||||
assert(file != nullptr);
|
||||
size_t size = _normalizedContent.size();
|
||||
if (!size)
|
||||
return None;
|
||||
uint8_t *bytes = file->ownedAllocations.Allocate<uint8_t>(size);
|
||||
std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes);
|
||||
return makeArrayRef(bytes, size);
|
||||
}
|
||||
|
||||
IO &_io;
|
||||
ContentBytes _normalizedContent;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<Relocation> {
|
||||
static void mapping(IO &io, Relocation &reloc) {
|
||||
io.mapRequired("offset", reloc.offset);
|
||||
io.mapOptional("scattered", reloc.scattered, false);
|
||||
io.mapRequired("type", reloc.type);
|
||||
io.mapRequired("length", reloc.length);
|
||||
io.mapRequired("pc-rel", reloc.pcRel);
|
||||
if ( !reloc.scattered )
|
||||
io.mapRequired("extern", reloc.isExtern);
|
||||
if ( reloc.scattered )
|
||||
io.mapRequired("value", reloc.value);
|
||||
if ( !reloc.scattered )
|
||||
io.mapRequired("symbol", reloc.symbol);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<RelocationInfoType> {
|
||||
static void enumeration(IO &io, RelocationInfoType &value) {
|
||||
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
|
||||
assert(info != nullptr);
|
||||
NormalizedFile *file = info->_normalizeMachOFile;
|
||||
assert(file != nullptr);
|
||||
switch (file->arch) {
|
||||
case lld::MachOLinkingContext::arch_x86_64:
|
||||
io.enumCase(value, "X86_64_RELOC_UNSIGNED",
|
||||
llvm::MachO::X86_64_RELOC_UNSIGNED);
|
||||
io.enumCase(value, "X86_64_RELOC_SIGNED",
|
||||
llvm::MachO::X86_64_RELOC_SIGNED);
|
||||
io.enumCase(value, "X86_64_RELOC_BRANCH",
|
||||
llvm::MachO::X86_64_RELOC_BRANCH);
|
||||
io.enumCase(value, "X86_64_RELOC_GOT_LOAD",
|
||||
llvm::MachO::X86_64_RELOC_GOT_LOAD);
|
||||
io.enumCase(value, "X86_64_RELOC_GOT",
|
||||
llvm::MachO::X86_64_RELOC_GOT);
|
||||
io.enumCase(value, "X86_64_RELOC_SUBTRACTOR",
|
||||
llvm::MachO::X86_64_RELOC_SUBTRACTOR);
|
||||
io.enumCase(value, "X86_64_RELOC_SIGNED_1",
|
||||
llvm::MachO::X86_64_RELOC_SIGNED_1);
|
||||
io.enumCase(value, "X86_64_RELOC_SIGNED_2",
|
||||
llvm::MachO::X86_64_RELOC_SIGNED_2);
|
||||
io.enumCase(value, "X86_64_RELOC_SIGNED_4",
|
||||
llvm::MachO::X86_64_RELOC_SIGNED_4);
|
||||
io.enumCase(value, "X86_64_RELOC_TLV",
|
||||
llvm::MachO::X86_64_RELOC_TLV);
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_x86:
|
||||
io.enumCase(value, "GENERIC_RELOC_VANILLA",
|
||||
llvm::MachO::GENERIC_RELOC_VANILLA);
|
||||
io.enumCase(value, "GENERIC_RELOC_PAIR",
|
||||
llvm::MachO::GENERIC_RELOC_PAIR);
|
||||
io.enumCase(value, "GENERIC_RELOC_SECTDIFF",
|
||||
llvm::MachO::GENERIC_RELOC_SECTDIFF);
|
||||
io.enumCase(value, "GENERIC_RELOC_LOCAL_SECTDIFF",
|
||||
llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
|
||||
io.enumCase(value, "GENERIC_RELOC_TLV",
|
||||
llvm::MachO::GENERIC_RELOC_TLV);
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_armv6:
|
||||
case lld::MachOLinkingContext::arch_armv7:
|
||||
case lld::MachOLinkingContext::arch_armv7s:
|
||||
io.enumCase(value, "ARM_RELOC_VANILLA",
|
||||
llvm::MachO::ARM_RELOC_VANILLA);
|
||||
io.enumCase(value, "ARM_RELOC_PAIR",
|
||||
llvm::MachO::ARM_RELOC_PAIR);
|
||||
io.enumCase(value, "ARM_RELOC_SECTDIFF",
|
||||
llvm::MachO::ARM_RELOC_SECTDIFF);
|
||||
io.enumCase(value, "ARM_RELOC_LOCAL_SECTDIFF",
|
||||
llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF);
|
||||
io.enumCase(value, "ARM_RELOC_BR24",
|
||||
llvm::MachO::ARM_RELOC_BR24);
|
||||
io.enumCase(value, "ARM_THUMB_RELOC_BR22",
|
||||
llvm::MachO::ARM_THUMB_RELOC_BR22);
|
||||
io.enumCase(value, "ARM_RELOC_HALF",
|
||||
llvm::MachO::ARM_RELOC_HALF);
|
||||
io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF",
|
||||
llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_arm64:
|
||||
io.enumCase(value, "ARM64_RELOC_UNSIGNED",
|
||||
llvm::MachO::ARM64_RELOC_UNSIGNED);
|
||||
io.enumCase(value, "ARM64_RELOC_SUBTRACTOR",
|
||||
llvm::MachO::ARM64_RELOC_SUBTRACTOR);
|
||||
io.enumCase(value, "ARM64_RELOC_BRANCH26",
|
||||
llvm::MachO::ARM64_RELOC_BRANCH26);
|
||||
io.enumCase(value, "ARM64_RELOC_PAGE21",
|
||||
llvm::MachO::ARM64_RELOC_PAGE21);
|
||||
io.enumCase(value, "ARM64_RELOC_PAGEOFF12",
|
||||
llvm::MachO::ARM64_RELOC_PAGEOFF12);
|
||||
io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21",
|
||||
llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
|
||||
io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
|
||||
llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
|
||||
io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT",
|
||||
llvm::MachO::ARM64_RELOC_POINTER_TO_GOT);
|
||||
io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21",
|
||||
llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
|
||||
io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
|
||||
llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
|
||||
io.enumCase(value, "ARM64_RELOC_ADDEND",
|
||||
llvm::MachO::ARM64_RELOC_ADDEND);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown architecture");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<Symbol> {
|
||||
static void mapping(IO &io, Symbol& sym) {
|
||||
io.mapRequired("name", sym.name);
|
||||
io.mapRequired("type", sym.type);
|
||||
io.mapOptional("scope", sym.scope, SymbolScope(0));
|
||||
io.mapOptional("sect", sym.sect, (uint8_t)0);
|
||||
if (sym.type == llvm::MachO::N_UNDF) {
|
||||
// In undef symbols, desc field contains alignment/ordinal info
|
||||
// which is better represented as a hex vaule.
|
||||
uint16_t t1 = sym.desc;
|
||||
Hex16 t2 = t1;
|
||||
io.mapOptional("desc", t2, Hex16(0));
|
||||
sym.desc = t2;
|
||||
} else {
|
||||
// In defined symbols, desc fit is a set of option bits.
|
||||
io.mapOptional("desc", sym.desc, SymbolDesc(0));
|
||||
}
|
||||
io.mapRequired("value", sym.value);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom mapping for VMProtect (e.g. "r-x").
|
||||
template <>
|
||||
struct ScalarTraits<VMProtect> {
|
||||
static void output(const VMProtect &value, void*, raw_ostream &out) {
|
||||
out << ( (value & llvm::MachO::VM_PROT_READ) ? 'r' : '-');
|
||||
out << ( (value & llvm::MachO::VM_PROT_WRITE) ? 'w' : '-');
|
||||
out << ( (value & llvm::MachO::VM_PROT_EXECUTE) ? 'x' : '-');
|
||||
}
|
||||
static StringRef input(StringRef scalar, void*, VMProtect &value) {
|
||||
value = 0;
|
||||
if (scalar.size() != 3)
|
||||
return "segment access protection must be three chars (e.g. \"r-x\")";
|
||||
switch (scalar[0]) {
|
||||
case 'r':
|
||||
value = llvm::MachO::VM_PROT_READ;
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
return "segment access protection first char must be 'r' or '-'";
|
||||
}
|
||||
switch (scalar[1]) {
|
||||
case 'w':
|
||||
value = value | llvm::MachO::VM_PROT_WRITE;
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
return "segment access protection second char must be 'w' or '-'";
|
||||
}
|
||||
switch (scalar[2]) {
|
||||
case 'x':
|
||||
value = value | llvm::MachO::VM_PROT_EXECUTE;
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
return "segment access protection third char must be 'x' or '-'";
|
||||
}
|
||||
// Return the empty string on success,
|
||||
return StringRef();
|
||||
}
|
||||
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<Segment> {
|
||||
static void mapping(IO &io, Segment& seg) {
|
||||
io.mapRequired("name", seg.name);
|
||||
io.mapRequired("address", seg.address);
|
||||
io.mapRequired("size", seg.size);
|
||||
io.mapRequired("init-access", seg.init_access);
|
||||
io.mapRequired("max-access", seg.max_access);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<LoadCommandType> {
|
||||
static void enumeration(IO &io, LoadCommandType &value) {
|
||||
io.enumCase(value, "LC_LOAD_DYLIB",
|
||||
llvm::MachO::LC_LOAD_DYLIB);
|
||||
io.enumCase(value, "LC_LOAD_WEAK_DYLIB",
|
||||
llvm::MachO::LC_LOAD_WEAK_DYLIB);
|
||||
io.enumCase(value, "LC_REEXPORT_DYLIB",
|
||||
llvm::MachO::LC_REEXPORT_DYLIB);
|
||||
io.enumCase(value, "LC_LOAD_UPWARD_DYLIB",
|
||||
llvm::MachO::LC_LOAD_UPWARD_DYLIB);
|
||||
io.enumCase(value, "LC_LAZY_LOAD_DYLIB",
|
||||
llvm::MachO::LC_LAZY_LOAD_DYLIB);
|
||||
io.enumCase(value, "LC_VERSION_MIN_MACOSX",
|
||||
llvm::MachO::LC_VERSION_MIN_MACOSX);
|
||||
io.enumCase(value, "LC_VERSION_MIN_IPHONEOS",
|
||||
llvm::MachO::LC_VERSION_MIN_IPHONEOS);
|
||||
io.enumCase(value, "LC_VERSION_MIN_TVOS",
|
||||
llvm::MachO::LC_VERSION_MIN_TVOS);
|
||||
io.enumCase(value, "LC_VERSION_MIN_WATCHOS",
|
||||
llvm::MachO::LC_VERSION_MIN_WATCHOS);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingTraits<DependentDylib> {
|
||||
static void mapping(IO &io, DependentDylib& dylib) {
|
||||
io.mapRequired("path", dylib.path);
|
||||
io.mapOptional("kind", dylib.kind,
|
||||
llvm::MachO::LC_LOAD_DYLIB);
|
||||
io.mapOptional("compat-version", dylib.compatVersion,
|
||||
PackedVersion(0x10000));
|
||||
io.mapOptional("current-version", dylib.currentVersion,
|
||||
PackedVersion(0x10000));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<RebaseType> {
|
||||
static void enumeration(IO &io, RebaseType &value) {
|
||||
io.enumCase(value, "REBASE_TYPE_POINTER",
|
||||
llvm::MachO::REBASE_TYPE_POINTER);
|
||||
io.enumCase(value, "REBASE_TYPE_TEXT_PCREL32",
|
||||
llvm::MachO::REBASE_TYPE_TEXT_PCREL32);
|
||||
io.enumCase(value, "REBASE_TYPE_TEXT_ABSOLUTE32",
|
||||
llvm::MachO::REBASE_TYPE_TEXT_ABSOLUTE32);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<RebaseLocation> {
|
||||
static void mapping(IO &io, RebaseLocation& rebase) {
|
||||
io.mapRequired("segment-index", rebase.segIndex);
|
||||
io.mapRequired("segment-offset", rebase.segOffset);
|
||||
io.mapOptional("kind", rebase.kind,
|
||||
llvm::MachO::REBASE_TYPE_POINTER);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<BindType> {
|
||||
static void enumeration(IO &io, BindType &value) {
|
||||
io.enumCase(value, "BIND_TYPE_POINTER",
|
||||
llvm::MachO::BIND_TYPE_POINTER);
|
||||
io.enumCase(value, "BIND_TYPE_TEXT_ABSOLUTE32",
|
||||
llvm::MachO::BIND_TYPE_TEXT_ABSOLUTE32);
|
||||
io.enumCase(value, "BIND_TYPE_TEXT_PCREL32",
|
||||
llvm::MachO::BIND_TYPE_TEXT_PCREL32);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingTraits<BindLocation> {
|
||||
static void mapping(IO &io, BindLocation &bind) {
|
||||
io.mapRequired("segment-index", bind.segIndex);
|
||||
io.mapRequired("segment-offset", bind.segOffset);
|
||||
io.mapOptional("kind", bind.kind,
|
||||
llvm::MachO::BIND_TYPE_POINTER);
|
||||
io.mapOptional("can-be-null", bind.canBeNull, false);
|
||||
io.mapRequired("ordinal", bind.ordinal);
|
||||
io.mapRequired("symbol-name", bind.symbolName);
|
||||
io.mapOptional("addend", bind.addend, Hex64(0));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<ExportSymbolKind> {
|
||||
static void enumeration(IO &io, ExportSymbolKind &value) {
|
||||
io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_REGULAR",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
|
||||
io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
|
||||
io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<ExportFlags> {
|
||||
static void bitset(IO &io, ExportFlags &value) {
|
||||
io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
|
||||
io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_REEXPORT",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
|
||||
io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER",
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct MappingTraits<Export> {
|
||||
static void mapping(IO &io, Export &exp) {
|
||||
io.mapRequired("name", exp.name);
|
||||
io.mapOptional("offset", exp.offset);
|
||||
io.mapOptional("kind", exp.kind,
|
||||
llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
|
||||
if (!io.outputting() || exp.flags)
|
||||
io.mapOptional("flags", exp.flags);
|
||||
io.mapOptional("other", exp.otherOffset, Hex32(0));
|
||||
io.mapOptional("other-name", exp.otherName, StringRef());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarEnumerationTraits<DataRegionType> {
|
||||
static void enumeration(IO &io, DataRegionType &value) {
|
||||
io.enumCase(value, "DICE_KIND_DATA",
|
||||
llvm::MachO::DICE_KIND_DATA);
|
||||
io.enumCase(value, "DICE_KIND_JUMP_TABLE8",
|
||||
llvm::MachO::DICE_KIND_JUMP_TABLE8);
|
||||
io.enumCase(value, "DICE_KIND_JUMP_TABLE16",
|
||||
llvm::MachO::DICE_KIND_JUMP_TABLE16);
|
||||
io.enumCase(value, "DICE_KIND_JUMP_TABLE32",
|
||||
llvm::MachO::DICE_KIND_JUMP_TABLE32);
|
||||
io.enumCase(value, "DICE_KIND_ABS_JUMP_TABLE32",
|
||||
llvm::MachO::DICE_KIND_ABS_JUMP_TABLE32);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingTraits<DataInCode> {
|
||||
static void mapping(IO &io, DataInCode &entry) {
|
||||
io.mapRequired("offset", entry.offset);
|
||||
io.mapRequired("length", entry.length);
|
||||
io.mapRequired("kind", entry.kind);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ScalarTraits<PackedVersion> {
|
||||
static void output(const PackedVersion &value, void*, raw_ostream &out) {
|
||||
out << llvm::format("%d.%d", (value >> 16), (value >> 8) & 0xFF);
|
||||
if (value & 0xFF) {
|
||||
out << llvm::format(".%d", (value & 0xFF));
|
||||
}
|
||||
}
|
||||
static StringRef input(StringRef scalar, void*, PackedVersion &result) {
|
||||
uint32_t value;
|
||||
if (lld::MachOLinkingContext::parsePackedVersion(scalar, value))
|
||||
return "malformed version number";
|
||||
result = value;
|
||||
// Return the empty string on success,
|
||||
return StringRef();
|
||||
}
|
||||
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingTraits<NormalizedFile> {
|
||||
static void mapping(IO &io, NormalizedFile &file) {
|
||||
io.mapRequired("arch", file.arch);
|
||||
io.mapRequired("file-type", file.fileType);
|
||||
io.mapOptional("flags", file.flags);
|
||||
io.mapOptional("dependents", file.dependentDylibs);
|
||||
io.mapOptional("install-name", file.installName, StringRef());
|
||||
io.mapOptional("compat-version", file.compatVersion, PackedVersion(0x10000));
|
||||
io.mapOptional("current-version", file.currentVersion, PackedVersion(0x10000));
|
||||
io.mapOptional("has-UUID", file.hasUUID, true);
|
||||
io.mapOptional("rpaths", file.rpaths);
|
||||
io.mapOptional("entry-point", file.entryAddress, Hex64(0));
|
||||
io.mapOptional("stack-size", file.stackSize, Hex64(0));
|
||||
io.mapOptional("source-version", file.sourceVersion, Hex64(0));
|
||||
io.mapOptional("OS", file.os);
|
||||
io.mapOptional("min-os-version", file.minOSverson, PackedVersion(0));
|
||||
io.mapOptional("min-os-version-kind", file.minOSVersionKind, (LoadCommandType)0);
|
||||
io.mapOptional("sdk-version", file.sdkVersion, PackedVersion(0));
|
||||
io.mapOptional("segments", file.segments);
|
||||
io.mapOptional("sections", file.sections);
|
||||
io.mapOptional("local-symbols", file.localSymbols);
|
||||
io.mapOptional("global-symbols", file.globalSymbols);
|
||||
io.mapOptional("undefined-symbols",file.undefinedSymbols);
|
||||
io.mapOptional("page-size", file.pageSize, Hex32(4096));
|
||||
io.mapOptional("rebasings", file.rebasingInfo);
|
||||
io.mapOptional("bindings", file.bindingInfo);
|
||||
io.mapOptional("weak-bindings", file.weakBindingInfo);
|
||||
io.mapOptional("lazy-bindings", file.lazyBindingInfo);
|
||||
io.mapOptional("exports", file.exportInfo);
|
||||
io.mapOptional("dataInCode", file.dataInCode);
|
||||
}
|
||||
static std::string validate(IO &io, NormalizedFile &file) { return {}; }
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
} // namespace yaml
|
||||
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
/// Handles !mach-o tagged yaml documents.
|
||||
bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io,
|
||||
const lld::File *&file) const {
|
||||
if (!io.mapTag("!mach-o"))
|
||||
return false;
|
||||
// Step 1: parse yaml into normalized mach-o struct.
|
||||
NormalizedFile nf;
|
||||
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
|
||||
assert(info != nullptr);
|
||||
assert(info->_normalizeMachOFile == nullptr);
|
||||
info->_normalizeMachOFile = &nf;
|
||||
MappingTraits<NormalizedFile>::mapping(io, nf);
|
||||
// Step 2: parse normalized mach-o struct into atoms.
|
||||
auto fileOrError = normalizedToAtoms(nf, info->_path, true);
|
||||
|
||||
// Check that we parsed successfully.
|
||||
if (!fileOrError) {
|
||||
std::string buffer;
|
||||
llvm::raw_string_ostream stream(buffer);
|
||||
handleAllErrors(fileOrError.takeError(),
|
||||
[&](const llvm::ErrorInfoBase &EI) {
|
||||
EI.log(stream);
|
||||
stream << "\n";
|
||||
});
|
||||
io.setError(stream.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nf.arch != _arch) {
|
||||
io.setError(Twine("file is wrong architecture. Expected ("
|
||||
+ MachOLinkingContext::nameFromArch(_arch)
|
||||
+ ") found ("
|
||||
+ MachOLinkingContext::nameFromArch(nf.arch)
|
||||
+ ")"));
|
||||
return false;
|
||||
}
|
||||
info->_normalizeMachOFile = nullptr;
|
||||
file = fileOrError->release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace normalized {
|
||||
|
||||
/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>>
|
||||
readYaml(std::unique_ptr<MemoryBuffer> &mb) {
|
||||
// Make empty NormalizedFile.
|
||||
std::unique_ptr<NormalizedFile> f(new NormalizedFile());
|
||||
|
||||
// Create YAML Input parser.
|
||||
YamlContext yamlContext;
|
||||
yamlContext._normalizeMachOFile = f.get();
|
||||
llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
|
||||
|
||||
// Fill NormalizedFile by parsing yaml.
|
||||
yin >> *f;
|
||||
|
||||
// Return error if there were parsing problems.
|
||||
if (auto ec = yin.error())
|
||||
return llvm::make_error<GenericError>(Twine("YAML parsing error: ")
|
||||
+ ec.message());
|
||||
|
||||
// Hand ownership of instantiated NormalizedFile to caller.
|
||||
return std::move(f);
|
||||
}
|
||||
|
||||
|
||||
/// Writes a yaml encoded mach-o files from an in-memory normalized view.
|
||||
std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) {
|
||||
// YAML I/O is not const aware, so need to cast away ;-(
|
||||
NormalizedFile *f = const_cast<NormalizedFile*>(&file);
|
||||
|
||||
// Create yaml Output writer, using yaml options for context.
|
||||
YamlContext yamlContext;
|
||||
yamlContext._normalizeMachOFile = f;
|
||||
llvm::yaml::Output yout(out, &yamlContext);
|
||||
|
||||
// Stream out yaml.
|
||||
yout << *f;
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
} // namespace normalized
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -1,29 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/MachOPasses.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_PASSES_H
|
||||
#define LLD_READER_WRITER_MACHO_PASSES_H
|
||||
|
||||
#include "lld/Core/PassManager.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addShimPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_PASSES_H
|
|
@ -1,131 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/ObjCPass.cpp -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
///
|
||||
/// ObjC Image Info Atom created by the ObjC pass.
|
||||
///
|
||||
class ObjCImageInfoAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
ObjCImageInfoAtom(const File &file, bool isBig,
|
||||
MachOLinkingContext::ObjCConstraint objCConstraint,
|
||||
uint32_t swiftVersion)
|
||||
: SimpleDefinedAtom(file) {
|
||||
|
||||
Data.info.version = 0;
|
||||
|
||||
switch (objCConstraint) {
|
||||
case MachOLinkingContext::objc_unknown:
|
||||
llvm_unreachable("Shouldn't run the objc pass without a constraint");
|
||||
case MachOLinkingContext::objc_supports_gc:
|
||||
case MachOLinkingContext::objc_gc_only:
|
||||
llvm_unreachable("GC is not supported");
|
||||
case MachOLinkingContext::objc_retainReleaseForSimulator:
|
||||
// The retain/release for simulator flag is already the correct
|
||||
// encoded value for the data so just set it here.
|
||||
Data.info.flags = (uint32_t)objCConstraint;
|
||||
break;
|
||||
case MachOLinkingContext::objc_retainRelease:
|
||||
// We don't need to encode this flag, so just leave the flags as 0.
|
||||
Data.info.flags = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
Data.info.flags |= (swiftVersion << 8);
|
||||
|
||||
normalized::write32(Data.bytes + 4, Data.info.flags, isBig);
|
||||
}
|
||||
|
||||
~ObjCImageInfoAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeObjCImageInfo;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR__;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(Data.bytes, size());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct objc_image_info {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
union {
|
||||
objc_image_info info;
|
||||
uint8_t bytes[8];
|
||||
} Data;
|
||||
};
|
||||
|
||||
class ObjCPass : public Pass {
|
||||
public:
|
||||
ObjCPass(const MachOLinkingContext &context)
|
||||
: _ctx(context),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o objc pass>")) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
// Add the image info.
|
||||
mergedFile.addAtom(*getImageInfo());
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const DefinedAtom* getImageInfo() {
|
||||
bool IsBig = MachOLinkingContext::isBigEndian(_ctx.arch());
|
||||
return new (_file.allocator()) ObjCImageInfoAtom(_file, IsBig,
|
||||
_ctx.objcConstraint(),
|
||||
_ctx.swiftVersion());
|
||||
}
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
MachOFile &_file;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
pm.add(std::make_unique<ObjCPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,101 +0,0 @@
|
|||
//===---- lib/ReaderWriter/MachO/SectCreateFile.h ---------------*- c++ -*-===//
|
||||
//
|
||||
// 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_SECTCREATE_FILE_H
|
||||
#define LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// A FlateNamespaceFile instance may be added as a resolution source of last
|
||||
// resort, depending on how -flat_namespace and -undefined are set.
|
||||
//
|
||||
class SectCreateFile : public File {
|
||||
public:
|
||||
class SectCreateAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
SectCreateAtom(const File &file, StringRef segName, StringRef sectName,
|
||||
std::unique_ptr<MemoryBuffer> content)
|
||||
: SimpleDefinedAtom(file),
|
||||
_combinedName((segName + "/" + sectName).str()),
|
||||
_content(std::move(content)) {}
|
||||
|
||||
~SectCreateAtom() override = default;
|
||||
|
||||
uint64_t size() const override { return _content->getBufferSize(); }
|
||||
|
||||
Scope scope() const override { return scopeGlobal; }
|
||||
|
||||
ContentType contentType() const override { return typeSectCreate; }
|
||||
|
||||
SectionChoice sectionChoice() const override { return sectionCustomRequired; }
|
||||
|
||||
StringRef customSectionName() const override { return _combinedName; }
|
||||
|
||||
DeadStripKind deadStrip() const override { return deadStripNever; }
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
const uint8_t *data =
|
||||
reinterpret_cast<const uint8_t*>(_content->getBufferStart());
|
||||
return ArrayRef<uint8_t>(data, _content->getBufferSize());
|
||||
}
|
||||
|
||||
StringRef segmentName() const { return _segName; }
|
||||
StringRef sectionName() const { return _sectName; }
|
||||
|
||||
private:
|
||||
std::string _combinedName;
|
||||
StringRef _segName;
|
||||
StringRef _sectName;
|
||||
std::unique_ptr<MemoryBuffer> _content;
|
||||
};
|
||||
|
||||
SectCreateFile() : File("sectcreate", kindSectCreateObject) {}
|
||||
|
||||
void addSection(StringRef seg, StringRef sect,
|
||||
std::unique_ptr<MemoryBuffer> content) {
|
||||
_definedAtoms.push_back(
|
||||
new (allocator()) SectCreateAtom(*this, seg, sect, std::move(content)));
|
||||
}
|
||||
|
||||
const AtomRange<DefinedAtom> defined() const override {
|
||||
return _definedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<UndefinedAtom> undefined() const override {
|
||||
return _noUndefinedAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
|
||||
return _noSharedLibraryAtoms;
|
||||
}
|
||||
|
||||
const AtomRange<AbsoluteAtom> absolute() const override {
|
||||
return _noAbsoluteAtoms;
|
||||
}
|
||||
|
||||
void clearAtoms() override {
|
||||
_definedAtoms.clear();
|
||||
_noUndefinedAtoms.clear();
|
||||
_noSharedLibraryAtoms.clear();
|
||||
_noAbsoluteAtoms.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
AtomVector<DefinedAtom> _definedAtoms;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
|
|
@ -1,128 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This linker pass updates branch-sites whose target is a different mode
|
||||
// (thumb vs arm).
|
||||
//
|
||||
// Arm code has two instruction encodings thumb and arm. When branching from
|
||||
// one code encoding to another, you need to use an instruction that switches
|
||||
// the instruction mode. Usually the transition only happens at call sites, and
|
||||
// the linker can transform a BL instruction in BLX (or vice versa). But if the
|
||||
// compiler did a tail call optimization and a function ends with a branch (not
|
||||
// branch and link), there is no pc-rel BX instruction.
|
||||
//
|
||||
// The ShimPass looks for pc-rel B instructions that will need to switch mode.
|
||||
// For those cases it synthesizes a shim which does the transition, then
|
||||
// modifies the original atom with the B instruction to target to the shim atom.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class ShimPass : public Pass {
|
||||
public:
|
||||
ShimPass(const MachOLinkingContext &context)
|
||||
: _ctx(context), _archHandler(_ctx.archHandler()),
|
||||
_stubInfo(_archHandler.stubInfo()),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at non-call branches.
|
||||
if (!_archHandler.isNonCallBranch(*ref))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
|
||||
bool atomIsThumb = _archHandler.isThumbFunction(*atom);
|
||||
bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
|
||||
if (atomIsThumb != targetIsThumb)
|
||||
updateBranchToUseShim(atomIsThumb, *daTarget, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Exit early if no shims needed.
|
||||
if (_targetToShim.empty())
|
||||
return llvm::Error::success();
|
||||
|
||||
// Sort shim atoms so the layout order is stable.
|
||||
std::vector<const DefinedAtom *> shims;
|
||||
shims.reserve(_targetToShim.size());
|
||||
for (auto element : _targetToShim) {
|
||||
shims.push_back(element.second);
|
||||
}
|
||||
std::sort(shims.begin(), shims.end(),
|
||||
[](const DefinedAtom *l, const DefinedAtom *r) {
|
||||
return (l->name() < r->name());
|
||||
});
|
||||
|
||||
// Add all shims to merged file.
|
||||
for (const DefinedAtom *shim : shims)
|
||||
mergedFile.addAtom(*shim);
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
|
||||
const Reference *ref) {
|
||||
// Make file-format specific stub and other support atoms.
|
||||
const DefinedAtom *shim = this->getShim(thumbToArm, target);
|
||||
assert(shim != nullptr);
|
||||
// Switch branch site to target shim atom.
|
||||
const_cast<Reference *>(ref)->setTarget(shim);
|
||||
}
|
||||
|
||||
const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
|
||||
auto pos = _targetToShim.find(&target);
|
||||
if ( pos != _targetToShim.end() ) {
|
||||
// Reuse an existing shim.
|
||||
assert(pos->second != nullptr);
|
||||
return pos->second;
|
||||
} else {
|
||||
// There is no existing shim, so create a new one.
|
||||
const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
|
||||
target);
|
||||
_targetToShim[&target] = shim;
|
||||
return shim;
|
||||
}
|
||||
}
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
MachOFile &_file;
|
||||
llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
pm.add(std::make_unique<ShimPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,377 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This linker pass updates call-sites which have references to shared library
|
||||
// atoms to instead have a reference to a stub (PLT entry) for the specified
|
||||
// symbol. Each file format defines a subclass of StubsPass which implements
|
||||
// the abstract methods for creating the file format specific StubAtoms.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// Lazy Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class LazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
LazyPointerAtom(const File &file, bool is64)
|
||||
: SimpleDefinedAtom(file), _is64(is64) { }
|
||||
|
||||
~LazyPointerAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeLazyPointer;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
};
|
||||
|
||||
//
|
||||
// NonLazyPointer (GOT) Atom created by the stubs pass.
|
||||
//
|
||||
class NonLazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
NonLazyPointerAtom(const File &file, bool is64, ContentType contentType)
|
||||
: SimpleDefinedAtom(file), _is64(is64), _contentType(contentType) { }
|
||||
|
||||
~NonLazyPointerAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
const ContentType _contentType;
|
||||
};
|
||||
|
||||
//
|
||||
// Stub Atom created by the stubs pass.
|
||||
//
|
||||
class StubAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo){ }
|
||||
|
||||
~StubAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStub;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return 1 << _stubInfo.codeAlignment;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
//
|
||||
// Stub Helper Atom created by the stubs pass.
|
||||
//
|
||||
class StubHelperAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
|
||||
|
||||
~StubHelperAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return 1 << _stubInfo.codeAlignment;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubHelperSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubHelperBytes,
|
||||
_stubInfo.stubHelperSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
//
|
||||
// Stub Helper Common Atom created by the stubs pass.
|
||||
//
|
||||
class StubHelperCommonAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
|
||||
|
||||
~StubHelperCommonAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return 1 << _stubInfo.stubHelperCommonAlignment;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubHelperCommonSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes,
|
||||
_stubInfo.stubHelperCommonSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
class StubsPass : public Pass {
|
||||
public:
|
||||
StubsPass(const MachOLinkingContext &context)
|
||||
: _ctx(context), _archHandler(_ctx.archHandler()),
|
||||
_stubInfo(_archHandler.stubInfo()),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o Stubs pass>")) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
// Skip this pass if output format uses text relocations instead of stubs.
|
||||
if (!this->noTextRelocs())
|
||||
return llvm::Error::success();
|
||||
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at call-sites.
|
||||
if (!this->isCallSite(*ref))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
if (isa<SharedLibraryAtom>(target)) {
|
||||
// Calls to shared libraries go through stubs.
|
||||
_targetToUses[target].push_back(ref);
|
||||
continue;
|
||||
}
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){
|
||||
// Calls to interposable functions in same linkage unit must also go
|
||||
// through a stub.
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
_targetToUses[target].push_back(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit early if no stubs needed.
|
||||
if (_targetToUses.empty())
|
||||
return llvm::Error::success();
|
||||
|
||||
// First add help-common and GOT slots used by lazy binding.
|
||||
SimpleDefinedAtom *helperCommonAtom =
|
||||
new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo);
|
||||
SimpleDefinedAtom *helperCacheNLPAtom =
|
||||
new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
|
||||
_stubInfo.stubHelperImageCacheContentType);
|
||||
SimpleDefinedAtom *helperBinderNLPAtom =
|
||||
new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
|
||||
_stubInfo.stubHelperImageCacheContentType);
|
||||
addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
|
||||
helperCacheNLPAtom);
|
||||
addOptReference(
|
||||
helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
|
||||
_stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom);
|
||||
addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
|
||||
helperBinderNLPAtom);
|
||||
addOptReference(
|
||||
helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
|
||||
_stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom);
|
||||
mergedFile.addAtom(*helperCommonAtom);
|
||||
mergedFile.addAtom(*helperBinderNLPAtom);
|
||||
mergedFile.addAtom(*helperCacheNLPAtom);
|
||||
|
||||
// Add reference to dyld_stub_binder in libSystem.dylib
|
||||
auto I = llvm::find_if(
|
||||
mergedFile.sharedLibrary(), [&](const SharedLibraryAtom *atom) {
|
||||
return atom->name().equals(_stubInfo.binderSymbolName);
|
||||
});
|
||||
assert(I != mergedFile.sharedLibrary().end() &&
|
||||
"dyld_stub_binder not found");
|
||||
addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I);
|
||||
|
||||
// Sort targets by name, so stubs and lazy pointers are consistent
|
||||
std::vector<const Atom *> targetsNeedingStubs;
|
||||
for (auto it : _targetToUses)
|
||||
targetsNeedingStubs.push_back(it.first);
|
||||
std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(),
|
||||
[](const Atom * left, const Atom * right) {
|
||||
return (left->name().compare(right->name()) < 0);
|
||||
});
|
||||
|
||||
// Make and append stubs, lazy pointers, and helpers in alphabetical order.
|
||||
unsigned lazyOffset = 0;
|
||||
for (const Atom *target : targetsNeedingStubs) {
|
||||
auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo);
|
||||
auto *lp =
|
||||
new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit());
|
||||
auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo);
|
||||
|
||||
addReference(stub, _stubInfo.stubReferenceToLP, lp);
|
||||
addOptReference(stub, _stubInfo.stubReferenceToLP,
|
||||
_stubInfo.optStubReferenceToLP, lp);
|
||||
addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper);
|
||||
addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target);
|
||||
addReference(helper, _stubInfo.stubHelperReferenceToImm, helper);
|
||||
addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper,
|
||||
lazyOffset);
|
||||
addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon,
|
||||
helperCommonAtom);
|
||||
|
||||
mergedFile.addAtom(*stub);
|
||||
mergedFile.addAtom(*lp);
|
||||
mergedFile.addAtom(*helper);
|
||||
|
||||
// Update each reference to use stub.
|
||||
for (const Reference *ref : _targetToUses[target]) {
|
||||
assert(ref->target() == target);
|
||||
// Switch call site to reference stub atom instead.
|
||||
const_cast<Reference *>(ref)->setTarget(stub);
|
||||
}
|
||||
|
||||
// Calculate new offset
|
||||
lazyOffset += target->name().size() + 12;
|
||||
}
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
bool noTextRelocs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isCallSite(const Reference &ref) {
|
||||
return _archHandler.isCallSite(ref);
|
||||
}
|
||||
|
||||
void addReference(SimpleDefinedAtom* atom,
|
||||
const ArchHandler::ReferenceInfo &refInfo,
|
||||
const lld::Atom* target) {
|
||||
atom->addReference(Reference::KindNamespace::mach_o,
|
||||
refInfo.arch, refInfo.kind, refInfo.offset,
|
||||
target, refInfo.addend);
|
||||
}
|
||||
|
||||
void addReferenceAddend(SimpleDefinedAtom *atom,
|
||||
const ArchHandler::ReferenceInfo &refInfo,
|
||||
const lld::Atom *target, uint64_t addend) {
|
||||
atom->addReference(Reference::KindNamespace::mach_o, refInfo.arch,
|
||||
refInfo.kind, refInfo.offset, target, addend);
|
||||
}
|
||||
|
||||
void addOptReference(SimpleDefinedAtom* atom,
|
||||
const ArchHandler::ReferenceInfo &refInfo,
|
||||
const ArchHandler::OptionalRefInfo &optRef,
|
||||
const lld::Atom* target) {
|
||||
if (!optRef.used)
|
||||
return;
|
||||
atom->addReference(Reference::KindNamespace::mach_o,
|
||||
refInfo.arch, optRef.kind, optRef.offset,
|
||||
target, optRef.addend);
|
||||
}
|
||||
|
||||
typedef llvm::DenseMap<const Atom*,
|
||||
llvm::SmallVector<const Reference *, 8>> TargetToUses;
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
MachOFile &_file;
|
||||
TargetToUses _targetToUses;
|
||||
};
|
||||
|
||||
void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
pm.add(std::unique_ptr<Pass>(new StubsPass(ctx)));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,140 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This linker pass transforms all TLV references to real references.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// TLVP Entry Atom created by the TLV pass.
|
||||
//
|
||||
class TLVPEntryAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
TLVPEntryAtom(const File &file, bool is64, StringRef name)
|
||||
: SimpleDefinedAtom(file), _is64(is64), _name(name) {}
|
||||
|
||||
~TLVPEntryAtom() override = default;
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeTLVInitializerPtr;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
StringRef slotName() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
StringRef _name;
|
||||
};
|
||||
|
||||
class TLVPass : public Pass {
|
||||
public:
|
||||
TLVPass(const MachOLinkingContext &context)
|
||||
: _ctx(context), _archHandler(_ctx.archHandler()),
|
||||
_file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) {
|
||||
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::Error perform(SimpleFile &mergedFile) override {
|
||||
bool allowTLV = _ctx.minOS("10.7", "1.0");
|
||||
|
||||
for (const DefinedAtom *atom : mergedFile.defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
if (!_archHandler.isTLVAccess(*ref))
|
||||
continue;
|
||||
|
||||
if (!allowTLV)
|
||||
return llvm::make_error<GenericError>(
|
||||
"targeted OS version does not support use of thread local "
|
||||
"variables in " + atom->name() + " for architecture " +
|
||||
_ctx.archName());
|
||||
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
|
||||
const DefinedAtom *tlvpEntry = makeTLVPEntry(target);
|
||||
const_cast<Reference*>(ref)->setTarget(tlvpEntry);
|
||||
_archHandler.updateReferenceToTLV(ref);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const TLVPEntryAtom*> entries;
|
||||
entries.reserve(_targetToTLVP.size());
|
||||
for (auto &it : _targetToTLVP)
|
||||
entries.push_back(it.second);
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) {
|
||||
return (lhs->slotName().compare(rhs->slotName()) < 0);
|
||||
});
|
||||
|
||||
for (const TLVPEntryAtom *slot : entries)
|
||||
mergedFile.addAtom(*slot);
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
const DefinedAtom *makeTLVPEntry(const Atom *target) {
|
||||
auto pos = _targetToTLVP.find(target);
|
||||
|
||||
if (pos != _targetToTLVP.end())
|
||||
return pos->second;
|
||||
|
||||
auto *tlvpEntry = new (_file.allocator())
|
||||
TLVPEntryAtom(_file, _ctx.is64Bit(), target->name());
|
||||
_targetToTLVP[target] = tlvpEntry;
|
||||
const ArchHandler::ReferenceInfo &nlInfo =
|
||||
_archHandler.stubInfo().nonLazyPointerReferenceToBinder;
|
||||
tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
|
||||
nlInfo.kind, 0, target, 0);
|
||||
return tlvpEntry;
|
||||
}
|
||||
|
||||
const MachOLinkingContext &_ctx;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
MachOFile &_file;
|
||||
llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP;
|
||||
};
|
||||
|
||||
void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsTLVPass());
|
||||
pm.add(std::make_unique<TLVPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
|
@ -1,70 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ExecutableAtoms.h"
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Writer.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <system_error>
|
||||
|
||||
using lld::mach_o::normalized::NormalizedFile;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class MachOWriter : public Writer {
|
||||
public:
|
||||
MachOWriter(const MachOLinkingContext &ctxt) : _ctx(ctxt) {}
|
||||
|
||||
llvm::Error writeFile(const lld::File &file, StringRef path) override {
|
||||
// Construct empty normalized file from atoms.
|
||||
llvm::Expected<std::unique_ptr<NormalizedFile>> nFile =
|
||||
normalized::normalizedFromAtoms(file, _ctx);
|
||||
if (auto ec = nFile.takeError())
|
||||
return ec;
|
||||
|
||||
// For testing, write out yaml form of normalized file.
|
||||
if (_ctx.printAtoms()) {
|
||||
std::unique_ptr<Writer> yamlWriter = createWriterYAML(_ctx);
|
||||
if (auto ec = yamlWriter->writeFile(file, "-"))
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Write normalized file as mach-o binary.
|
||||
return writeBinary(*nFile->get(), path);
|
||||
}
|
||||
|
||||
void createImplicitFiles(std::vector<std::unique_ptr<File>> &r) override {
|
||||
// When building main executables, add _main as required entry point.
|
||||
if (_ctx.outputTypeHasEntry())
|
||||
r.emplace_back(new CEntryFile(_ctx));
|
||||
// If this can link with dylibs, need helper function (dyld_stub_binder).
|
||||
if (_ctx.needsStubsPass())
|
||||
r.emplace_back(new StubHelperFile(_ctx));
|
||||
// Final linked images can access a symbol for their mach_header.
|
||||
if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
|
||||
r.emplace_back(new MachHeaderAliasFile(_ctx));
|
||||
}
|
||||
private:
|
||||
const MachOLinkingContext &_ctx;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mach_o
|
||||
|
||||
std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &context) {
|
||||
return std::unique_ptr<Writer>(new lld::mach_o::MachOWriter(context));
|
||||
}
|
||||
|
||||
} // namespace lld
|
|
@ -1,9 +0,0 @@
|
|||
add_lld_library(lldYAML
|
||||
ReaderWriterYAML.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
lldCore
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -37,10 +37,6 @@ if (NOT LLD_BUILT_STANDALONE)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (LLVM_INCLUDE_TESTS)
|
||||
list(APPEND LLD_TEST_DEPS LLDUnitTests)
|
||||
endif()
|
||||
|
||||
add_lit_testsuite(check-lld "Running lld test suite"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${LLD_TEST_DEPS}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
|
||||
address: 0
|
||||
content: [ 0xC3 ]
|
||||
global-symbols:
|
||||
- name: _foo
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
desc: [ ]
|
||||
value: 0
|
|
@ -1,19 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
|
@ -1,11 +0,0 @@
|
|||
# RUN: ld64.lld.darwinold -arch x86_64 -lto_library %t -print-atoms -r %s 2>&1 | FileCheck %s
|
||||
#
|
||||
# Test that the -lto_library option does not result in an error.
|
||||
#
|
||||
|
||||
# CHECK-NOT: -lto_library
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _foo
|
||||
...
|
|
@ -1,15 +0,0 @@
|
|||
# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc %s 2>&1 | FileCheck %s
|
||||
#
|
||||
# Test that the -objc_gc is rejected.
|
||||
#
|
||||
|
||||
# CHECK: error: -objc_gc is not supported
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _main
|
||||
type: code
|
||||
scope: global
|
||||
content: [ 0x90 ]
|
||||
|
||||
...
|
|
@ -1,15 +0,0 @@
|
|||
# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc_compaction %s 2>&1 | FileCheck %s
|
||||
#
|
||||
# Test that the -objc_gc_compaction is rejected.
|
||||
#
|
||||
|
||||
# CHECK: error: -objc_gc_compaction is not supported
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _main
|
||||
type: code
|
||||
scope: global
|
||||
content: [ 0x90 ]
|
||||
|
||||
...
|
|
@ -1,15 +0,0 @@
|
|||
# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc_only %s 2>&1 | FileCheck %s
|
||||
#
|
||||
# Test that the -objc_gc_only is rejected.
|
||||
#
|
||||
|
||||
# CHECK: error: -objc_gc_only is not supported
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _main
|
||||
type: code
|
||||
scope: global
|
||||
content: [ 0x90 ]
|
||||
|
||||
...
|
|
@ -1,27 +0,0 @@
|
|||
# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
|
||||
# RUN: %p/Inputs/native-and-mach-o.objtxt \
|
||||
# RUN: %p/Inputs/native-and-mach-o2.objtxt -o %t && \
|
||||
# RUN: llvm-nm %t | FileCheck %s
|
||||
#
|
||||
# Test a mix of atoms and mach-o both encoded in yaml
|
||||
#
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _main
|
||||
type: code
|
||||
scope: global
|
||||
content: [ 55, 48, 89, E5, 30, C0, E8, 00,
|
||||
00, 00, 00, 31, C0, 5D, C3 ]
|
||||
references:
|
||||
- offset: 7
|
||||
kind: branch32
|
||||
target: _foo
|
||||
|
||||
undefined-atoms:
|
||||
- name: _foo
|
||||
|
||||
...
|
||||
|
||||
# CHECK: {{[0-9a-f]+}} T _foo
|
||||
# CHECK: {{[0-9a-f]+}} T _main
|
|
@ -1,30 +0,0 @@
|
|||
# -*- Python -*-
|
||||
|
||||
|
||||
#
|
||||
# Dump out Xcode binary dependency file.
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
f = open(sys.argv[1], "rb")
|
||||
byte = f.read(1)
|
||||
while byte != b'':
|
||||
if byte == b'\000':
|
||||
sys.stdout.write("linker-vers: ")
|
||||
elif byte == b'\020':
|
||||
sys.stdout.write("input-file: ")
|
||||
elif byte == b'\021':
|
||||
sys.stdout.write("not-found: ")
|
||||
elif byte == b'\100':
|
||||
sys.stdout.write("output-file: ")
|
||||
byte = f.read(1)
|
||||
while byte != b'\000':
|
||||
if byte != b'\012':
|
||||
sys.stdout.write(byte.decode("ascii"))
|
||||
byte = f.read(1)
|
||||
sys.stdout.write("\n")
|
||||
byte = f.read(1)
|
||||
|
||||
f.close()
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
--- !tapi-tbd-v3
|
||||
archs: [ x86_64 ]
|
||||
uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000000' ]
|
||||
platform: macosx
|
||||
install-name: '/usr/lib/libSystem.B.dylib'
|
||||
current-version: 0001.001.1
|
||||
exports:
|
||||
- archs: [ 'x86_64' ]
|
||||
re-exports: [ '/usr/lib/system/libdyld.dylib',
|
||||
'/usr/lib/system/libsystem_c.dylib',
|
||||
'/usr/lib/system/libsystem_m.dylib' ]
|
||||
--- !tapi-tbd-v3
|
||||
archs: [ x86_64 ]
|
||||
uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000001' ]
|
||||
platform: macosx
|
||||
install-name: '/usr/lib/system/libdyld.dylib'
|
||||
current-version: 0001.001.1
|
||||
parent-umbrella: System
|
||||
exports:
|
||||
- archs: [ 'x86_64' ]
|
||||
symbols: [ dyld_stub_binder ]
|
||||
--- !tapi-tbd-v3
|
||||
archs: [ x86_64 ]
|
||||
uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002' ]
|
||||
platform: macosx
|
||||
install-name: '/usr/lib/system/libsystem_c.dylib'
|
||||
current-version: 0001.001.1
|
||||
parent-umbrella: System
|
||||
exports:
|
||||
- archs: [ 'x86_64' ]
|
||||
symbols: [ ]
|
||||
--- !tapi-tbd-v3
|
||||
archs: [ x86_64 ]
|
||||
uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000003' ]
|
||||
platform: macosx
|
||||
install-name: '/usr/lib/system/libsystem_m.dylib'
|
||||
current-version: 0001.001.1
|
||||
parent-umbrella: System
|
||||
exports:
|
||||
- archs: [ 'x86_64' ]
|
||||
symbols: [ ___nan ]
|
||||
...
|
|
@ -1,6 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: dyld_stub_binder
|
|
@ -1,83 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: armv7
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
alignment: 2
|
||||
address: 0x0000000000000000
|
||||
content: [ 0xFE, 0xFF, 0xFF, 0xEB, 0x02, 0x00, 0x00, 0xFA,
|
||||
0xFC, 0xFF, 0xFF, 0xEB, 0xFB, 0xFF, 0xFF, 0xFA,
|
||||
0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1 ]
|
||||
relocations:
|
||||
- offset: 0x0000000C
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 4
|
||||
- offset: 0x00000008
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 3
|
||||
- offset: 0x00000004
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: false
|
||||
symbol: 1
|
||||
- offset: 0x00000000
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: false
|
||||
symbol: 1
|
||||
- segment: __DATA
|
||||
section: __data
|
||||
type: S_REGULAR
|
||||
attributes: [ ]
|
||||
address: 0x0000000000000018
|
||||
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
|
||||
relocations:
|
||||
- offset: 0x00000004
|
||||
type: ARM_RELOC_VANILLA
|
||||
length: 2
|
||||
pc-rel: false
|
||||
extern: false
|
||||
symbol: 1
|
||||
- offset: 0x00000000
|
||||
type: ARM_RELOC_VANILLA
|
||||
length: 2
|
||||
pc-rel: false
|
||||
extern: true
|
||||
symbol: 3
|
||||
local-symbols:
|
||||
- name: _d2
|
||||
type: N_SECT
|
||||
sect: 2
|
||||
value: 0x0000000000000018
|
||||
global-symbols:
|
||||
- name: _a1
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
- name: _a2
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000014
|
||||
undefined-symbols:
|
||||
- name: _t1
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
||||
- name: _t2
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
|
@ -1,60 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: armv7
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
alignment: 2
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7,
|
||||
0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3,
|
||||
0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ]
|
||||
relocations:
|
||||
- offset: 0x00000014
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 3
|
||||
- offset: 0x00000010
|
||||
type: ARM_RELOC_BR24
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 3
|
||||
- offset: 0x00000006
|
||||
type: ARM_THUMB_RELOC_BR22
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 2
|
||||
- offset: 0x00000002
|
||||
type: ARM_THUMB_RELOC_BR22
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 2
|
||||
global-symbols:
|
||||
- name: _a2
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x000000000000000C
|
||||
- name: _t2
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
desc: [ N_ARM_THUMB_DEF ]
|
||||
value: 0x0000000000000000
|
||||
undefined-symbols:
|
||||
- name: _a1
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
||||
- name: _t1
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
|
@ -1,13 +0,0 @@
|
|||
#
|
||||
# For use by test cases that create dynamic output types which may needs stubs
|
||||
# and therefore will need a dylib definition of dyld_stub_binder.
|
||||
#
|
||||
|
||||
--- !mach-o
|
||||
arch: arm64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: dyld_stub_binder
|
||||
|
||||
...
|
|
@ -1,13 +0,0 @@
|
|||
#
|
||||
# For use by test cases that create dynamic output types which may needs stubs
|
||||
# and therefore will need a dylib definition of dyld_stub_binder.
|
||||
#
|
||||
|
||||
--- !mach-o
|
||||
arch: armv7
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: dyld_stub_binder
|
||||
|
||||
...
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0xC3 ]
|
||||
global-symbols:
|
||||
- name: _bar
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
|
@ -1,25 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
has-UUID: false
|
||||
OS: unknown
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __objc_methname
|
||||
type: S_CSTRING_LITERALS
|
||||
attributes: [ ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x61, 0x62, 0x63, 0x00 ]
|
||||
- segment: __TEXT
|
||||
section: __objc_classname
|
||||
type: S_CSTRING_LITERALS
|
||||
attributes: [ ]
|
||||
address: 0x0000000000000006
|
||||
content: [ 0x61, 0x62, 0x63, 0x00 ]
|
||||
- segment: __TEXT
|
||||
section: __cstring
|
||||
type: S_CSTRING_LITERALS
|
||||
attributes: [ ]
|
||||
address: 0x000000000000000A
|
||||
content: [ 0x61, 0x62, 0x63, 0x00 ]
|
|
@ -1,6 +0,0 @@
|
|||
#
|
||||
# For use with exported_symbols_list.yaml
|
||||
#
|
||||
_foo
|
||||
_b
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
/foo/bar/a.o
|
||||
/foo/bar/b.o
|
||||
/foo/x.a
|
|
@ -1,53 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x0D, 0x00,
|
||||
0x00, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00,
|
||||
0x00, 0x00, 0x8B, 0x00, 0x03, 0x01, 0x48, 0x8B,
|
||||
0x0D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x5D,
|
||||
0xC3 ]
|
||||
relocations:
|
||||
- offset: 0x00000019
|
||||
type: X86_64_RELOC_GOT_LOAD
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 2
|
||||
- offset: 0x0000000E
|
||||
type: X86_64_RELOC_GOT_LOAD
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 1
|
||||
- offset: 0x00000007
|
||||
type: X86_64_RELOC_GOT_LOAD
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 3
|
||||
global-symbols:
|
||||
- name: _main
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
undefined-symbols:
|
||||
- name: _bar
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
||||
- name: _foo
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
||||
- name: _zazzle
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
|
@ -1,11 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libfoobar.dylib
|
||||
exports:
|
||||
- name: _bar
|
||||
- name: _zazzle
|
||||
- name: _foo
|
||||
- name: _aaa
|
||||
- name: _fff
|
||||
- name: _zzz
|
|
@ -1,8 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: arm64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _fprintf
|
||||
- name: ___stdoutp
|
||||
- name: dyld_stub_binder
|
|
@ -1,7 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: armv6
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _printf
|
||||
- name: dyld_stub_binder
|
|
@ -1,7 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: armv7
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _printf
|
||||
- name: dyld_stub_binder
|
|
@ -1,7 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _printf
|
||||
- name: dyld_stub_binder
|
|
@ -1,8 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _fprintf
|
||||
- name: dyld_stub_binder
|
||||
- name: ___stdoutp
|
|
@ -1 +0,0 @@
|
|||
hello
|
|
@ -1,6 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
exports:
|
||||
- name: _open
|
|
@ -1,8 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libfoo.dylib
|
||||
compat-version: 2.0
|
||||
current-version: 3.4
|
||||
exports:
|
||||
- name: _foo
|
|
@ -1,8 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libbaz.dylib
|
||||
compat-version: 3.0
|
||||
current-version: 4.5
|
||||
exports:
|
||||
- name: _baz
|
|
@ -1,8 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libbar.dylib
|
||||
compat-version: 1.0
|
||||
current-version: 2.3
|
||||
exports:
|
||||
- name: _bar
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,22 +0,0 @@
|
|||
|
||||
# This object file has no version min and so will prevent any -r link from emitting
|
||||
# a version min.
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x00, 0x00, 0x00, 0x00 ]
|
||||
global-symbols:
|
||||
- name: _main2
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
...
|
|
@ -1,11 +0,0 @@
|
|||
|
||||
# input file for order_file-basic.yaml
|
||||
|
||||
_func2
|
||||
libfoo.a(foo.o):_foo # tests file specific ordering within archive
|
||||
i386:_func3 # wrong arch, so ignored
|
||||
armv7:_func3 # wrong arch, so ignored
|
||||
_func1
|
||||
_notfound # unknown symbol silently ignored
|
||||
_data3 # data symbols should be orderable
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
bar/a.o
|
||||
bar/b.o
|
||||
x.a
|
|
@ -1,21 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ MH_TWOLEVEL ]
|
||||
install-name: /junk/libfoo.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000F9A
|
||||
content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ]
|
||||
global-symbols:
|
||||
- name: _foo
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000F9A
|
||||
dependents:
|
||||
- path: /junk/libbar.dylib
|
||||
kind: LC_REEXPORT_DYLIB
|
|
@ -1,18 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ MH_TWOLEVEL ]
|
||||
install-name: /junk/libbar.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000F9A
|
||||
content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ]
|
||||
global-symbols:
|
||||
- name: _bar
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000F9A
|
|
@ -1,19 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ MH_TWOLEVEL ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
|
@ -1,18 +0,0 @@
|
|||
# RUN: not ld64.lld.darwinold -arch x86_64 -r %s %p/Inputs/hello-world-x86_64.yaml 2>&1 | FileCheck %s
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
compat-version: 0.0
|
||||
current-version: 0.0
|
||||
has-UUID: false
|
||||
OS: unknown
|
||||
sections:
|
||||
- segment: __DATA
|
||||
section: __objc_imageinfo
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_NO_DEAD_STRIP ]
|
||||
address: 0x0000000000000100
|
||||
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 ]
|
||||
...
|
|
@ -1,13 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: arm64
|
||||
file-type: MH_DYLIB
|
||||
install-name: /usr/lib/libc++.dylib
|
||||
exports:
|
||||
- name: __Unwind_Resume
|
||||
- name: __ZTIl
|
||||
- name: __ZTIi
|
||||
- name: ___cxa_end_catch
|
||||
- name: ___cxa_begin_catch
|
||||
- name: ___cxa_allocate_exception
|
||||
- name: ___cxa_throw
|
||||
- name: ___gxx_personality_v0
|
|
@ -1,28 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ ]
|
||||
has-UUID: false
|
||||
OS: unknown
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
|
||||
0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00,
|
||||
0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00,
|
||||
0x00, 0x00, 0x00 ]
|
||||
global-symbols:
|
||||
- name: _foo
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
undefined-symbols:
|
||||
- name: _myGlobal
|
||||
type: N_UNDF
|
||||
scope: [ N_EXT ]
|
||||
value: 0x0000000000000000
|
|
@ -1,58 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
has-UUID: false
|
||||
OS: unknown
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
alignment: 4
|
||||
address: 0x0000000000000000
|
||||
content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90,
|
||||
0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3,
|
||||
0x31, 0xC0, 0xC3 ]
|
||||
local-symbols:
|
||||
- name: _myStatic
|
||||
type: N_SECT
|
||||
sect: 1
|
||||
value: 0x000000000000000B
|
||||
- name: _myVariablePreviouslyKnownAsPrivateExtern
|
||||
type: N_SECT
|
||||
scope: [ N_PEXT ]
|
||||
sect: 1
|
||||
desc: [ N_SYMBOL_RESOLVER ]
|
||||
value: 0x0000000000000011
|
||||
global-symbols:
|
||||
- name: _myGlobal
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000001
|
||||
- name: _myGlobalWeak
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
desc: [ N_WEAK_DEF ]
|
||||
value: 0x0000000000000002
|
||||
- name: _myHidden
|
||||
type: N_SECT
|
||||
scope: [ N_EXT, N_PEXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000004
|
||||
- name: _myHiddenWeak
|
||||
type: N_SECT
|
||||
scope: [ N_EXT, N_PEXT ]
|
||||
sect: 1
|
||||
desc: [ N_WEAK_DEF ]
|
||||
value: 0x0000000000000007
|
||||
- name: _myResolver
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
desc: [ N_SYMBOL_RESOLVER ]
|
||||
value: 0x0000000000000010
|
||||
|
||||
install-name: libspecial.dylib
|
|
@ -1,20 +0,0 @@
|
|||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue