Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,20 @@
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
)

View File

@@ -0,0 +1,228 @@
//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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 {
/// \brief The FileArchive class represents an Archive Library file
class FileArchive : public lld::ArchiveLibraryFile {
public:
FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
StringRef path, bool logLoading)
: ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
_registry(reg), _logLoading(logLoading) {}
/// \brief 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;
}
/// \brief 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 &reg) const override {
StringRef path = mb->getBufferIdentifier();
std::unique_ptr<File> ret =
llvm::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

View File

@@ -0,0 +1,172 @@
//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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

View File

@@ -0,0 +1,323 @@
//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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

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

View File

@@ -0,0 +1,181 @@
//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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(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

View File

@@ -0,0 +1,34 @@
add_lld_library(lldMachO
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
LINK_LIBS
lldCore
lldYAML
${LLVM_PTHREAD_LIB}
)
include_directories(.)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_MACHO_DEBUGINFO_H
#define LLD_READER_WRITER_MACHO_DEBUGINFO_H
#include "lld/Core/Atom.h"
#include <vector>
#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

View File

@@ -0,0 +1,155 @@
//===- lib/ReaderWriter/MachO/ExecutableAtoms.h ---------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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

View File

@@ -0,0 +1,400 @@
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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 <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 an the atom from 'section' that covers
/// 'offsetInSect'. Returns nullptr is no atom found.
MachODefinedAtom *findAtomCoveringAddress(const Section &section,
uint64_t offsetInSect,
uint32_t *foundOffsetAtom=nullptr) {
const auto &pos = _sectionAtoms.find(&section);
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 &sectAndAtoms : _sectionAtoms) {
for (auto &offAndAtom : sectAndAtoms.second) {
vistor(offAndAtom.atom);
}
}
}
typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
SectionAtomVisitor;
void eachAtomInSection(const Section &section, SectionAtomVisitor visitor) {
auto pos = _sectionAtoms.find(&section);
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) {
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();
}
private:
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) { }
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;
};
} // end namespace mach_o
} // end namespace lld
#endif // LLD_READER_WRITER_MACHO_FILE_H

View File

@@ -0,0 +1,63 @@
//===- lib/ReaderWriter/MachO/FlatNamespaceFile.h -------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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

View File

@@ -0,0 +1,184 @@
//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \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 accesss.
_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 master 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(llvm::make_unique<GOTPass>(ctx));
}
} // end namesapce mach_o
} // end namesapce lld

View File

@@ -0,0 +1,489 @@
//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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 &registry,
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 &registry,
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) {
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) {
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) {
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) {
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) {
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);
DEBUG({
StringRef comp = result ? "<" : ">=";
llvm::dbgs() << "Layout: '" << lc._atom.get()->name()
<< "' " << comp << " '"
<< rc._atom.get()->name() << "' (" << reason << ")\n";
});
return result;
}
LayoutPass::LayoutPass(const Registry &registry, 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) {
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.
DEBUG(checkFollowonChain(atomRange));
// Build override maps
buildOrdinalOverrideMap(atomRange);
DEBUG({
llvm::dbgs() << "unsorted atoms:\n";
printDefinedAtoms(atomRange);
});
std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
sort(llvm::parallel::par, vec.begin(), vec.end(),
[&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
return compareAtoms(l, r, _customSorter);
});
DEBUG(checkTransitivity(vec, _customSorter));
undecorate(atomRange, vec);
DEBUG({
llvm::dbgs() << "sorted atoms:\n";
printDefinedAtoms(atomRange);
});
DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n");
return llvm::Error::success();
}
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(llvm::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

View File

@@ -0,0 +1,119 @@
//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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 &registry, 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

View File

@@ -0,0 +1,336 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFile.h -----------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \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);
/// 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

Some files were not shown because too many files have changed in this diff Show More