diff options
Diffstat (limited to 'lld/lib/ReaderWriter')
32 files changed, 0 insertions, 17359 deletions
diff --git a/lld/lib/ReaderWriter/FileArchive.cpp b/lld/lib/ReaderWriter/FileArchive.cpp deleted file mode 100644 index 98f4d06ee210..000000000000 --- a/lld/lib/ReaderWriter/FileArchive.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler.cpp deleted file mode 100644 index c101f3b157bb..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler.cpp +++ /dev/null @@ -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 - - - diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler.h b/lld/lib/ReaderWriter/MachO/ArchHandler.h deleted file mode 100644 index 83646c09b1a8..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp deleted file mode 100644 index 06c98ac06fd1..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ /dev/null @@ -1,1522 +0,0 @@ -//===- lib/FileFormat/MachO/ArchHandler_arm.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::little32_t; - - -class ArchHandler_arm : public ArchHandler { -public: - ArchHandler_arm() = default; - ~ArchHandler_arm() override = default; - - const Registry::KindStrings *kindStrings() override { return _sKindStrings; } - - Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } - - const ArchHandler::StubInfo &stubInfo() override; - bool isCallSite(const Reference &) override; - bool isPointer(const Reference &) override; - bool isPairedReloc(const normalized::Relocation &) override; - bool isNonCallBranch(const Reference &) 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 invalid; - } - - Reference::KindValue unwindRefToFunctionKind() override { - return invalid; - } - - Reference::KindValue unwindRefToEhFrameKind() override { - return invalid; - } - - Reference::KindValue lazyImmediateLocationKind() override { - return lazyImmediateLocation; - } - - Reference::KindValue pointerKind() override { - return invalid; - } - - uint32_t dwarfCompactUnwindType() override { - // FIXME - return -1; - } - - 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, - FindSectionIndexForAtom, - FindAddressForAtom, - normalized::Relocations &) override; - - void addAdditionalReferences(MachODefinedAtom &atom) override; - - bool isDataInCodeTransition(Reference::KindValue refKind) override { - switch (refKind) { - case modeThumbCode: - case modeArmCode: - case modeData: - return true; - default: - return false; - break; - } - } - - Reference::KindValue dataInCodeTransitionStart( - const MachODefinedAtom &atom) override { - return modeData; - } - - Reference::KindValue dataInCodeTransitionEnd( - const MachODefinedAtom &atom) override { - return atom.isThumb() ? modeThumbCode : modeArmCode; - } - - bool isThumbFunction(const DefinedAtom &atom) override; - const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, - const DefinedAtom &) override; - -private: - friend class Thumb2ToArmShimAtom; - friend class ArmToThumbShimAtom; - - static const Registry::KindStrings _sKindStrings[]; - static const StubInfo _sStubInfoArmPIC; - - enum ArmKind : Reference::KindValue { - invalid, /// for error condition - - modeThumbCode, /// Content starting at this offset is thumb. - modeArmCode, /// Content starting at this offset is arm. - modeData, /// Content starting at this offset is data. - - // Kinds found in mach-o .o files: - thumb_bl22, /// ex: bl _foo - thumb_b22, /// ex: b _foo - thumb_movw, /// ex: movw r1, :lower16:_foo - thumb_movt, /// ex: movt r1, :lower16:_foo - thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) - thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) - arm_bl24, /// ex: bl _foo - arm_b24, /// ex: b _foo - arm_movw, /// ex: movw r1, :lower16:_foo - arm_movt, /// ex: movt r1, :lower16:_foo - arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) - arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) - pointer32, /// ex: .long _foo - delta32, /// ex: .long _foo - . - - // Kinds introduced by Passes: - lazyPointer, /// Location contains a lazy pointer. - lazyImmediateLocation, /// Location contains immediate value used in stub. - }; - - // Utility functions for inspecting/updating instructions. - static bool isThumbMovw(uint32_t instruction); - static bool isThumbMovt(uint32_t instruction); - static bool isArmMovw(uint32_t instruction); - static bool isArmMovt(uint32_t instruction); - static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); - static int32_t getDisplacementFromArmBranch(uint32_t instruction); - static uint16_t getWordFromThumbMov(uint32_t instruction); - static uint16_t getWordFromArmMov(uint32_t instruction); - static uint32_t clearThumbBit(uint32_t value, const Atom *target); - static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, - bool targetIsThumb); - static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, - int32_t disp, bool targetThumb); - static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); - static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); - - StringRef stubName(const DefinedAtom &); - bool useExternalRelocationTo(const Atom &target); - - void applyFixupFinal(const Reference &ref, uint8_t *location, - uint64_t fixupAddress, uint64_t targetAddress, - uint64_t inAtomAddress, bool &thumbMode, - bool targetIsThumb); - - void applyFixupRelocatable(const Reference &ref, uint8_t *location, - uint64_t fixupAddress, - uint64_t targetAddress, - uint64_t inAtomAddress, bool &thumbMode, - bool targetIsThumb); -}; - -//===----------------------------------------------------------------------===// -// ArchHandler_arm -//===----------------------------------------------------------------------===// - -const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { - LLD_KIND_STRING_ENTRY(invalid), - LLD_KIND_STRING_ENTRY(modeThumbCode), - LLD_KIND_STRING_ENTRY(modeArmCode), - LLD_KIND_STRING_ENTRY(modeData), - LLD_KIND_STRING_ENTRY(thumb_bl22), - LLD_KIND_STRING_ENTRY(thumb_b22), - LLD_KIND_STRING_ENTRY(thumb_movw), - LLD_KIND_STRING_ENTRY(thumb_movt), - LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), - LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), - LLD_KIND_STRING_ENTRY(arm_bl24), - LLD_KIND_STRING_ENTRY(arm_b24), - LLD_KIND_STRING_ENTRY(arm_movw), - LLD_KIND_STRING_ENTRY(arm_movt), - LLD_KIND_STRING_ENTRY(arm_movw_funcRel), - LLD_KIND_STRING_ENTRY(arm_movt_funcRel), - LLD_KIND_STRING_ENTRY(pointer32), - LLD_KIND_STRING_ENTRY(delta32), - LLD_KIND_STRING_ENTRY(lazyPointer), - LLD_KIND_STRING_ENTRY(lazyImmediateLocation), - LLD_KIND_STRING_END -}; - -const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { - "dyld_stub_binder", - - // References in lazy pointer - { Reference::KindArch::ARM, pointer32, 0, 0 }, - { Reference::KindArch::ARM, lazyPointer, 0, 0 }, - - // GOT pointer to dyld_stub_binder - { Reference::KindArch::ARM, pointer32, 0, 0 }, - - // arm code alignment 2^2 - 2, - - // Stub size and code - 16, - { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 - 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip - 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] - 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) - { Reference::KindArch::ARM, delta32, 12, 0 }, - { false, 0, 0, 0 }, - - // Stub Helper size and code - 12, - { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] - 0x00, 0x00, 0x00, 0xEA, // b _helperhelper - 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset - { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, - { Reference::KindArch::ARM, arm_b24, 4, 0 }, - - // Stub helper image cache content type - DefinedAtom::typeGOT, - - // Stub Helper-Common size and code - 36, - // Stub helper alignment - 2, - { // push lazy-info-offset - 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! - // push address of dyld_mageLoaderCache - 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 - 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip - 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! - // jump through dyld_stub_binder - 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 - 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip - 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] - 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) - 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) - { Reference::KindArch::ARM, delta32, 28, 0xC }, - { false, 0, 0, 0 }, - { Reference::KindArch::ARM, delta32, 32, 0x04 }, - { false, 0, 0, 0 } -}; - -const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { - // If multiple kinds of stubs are supported, select which StubInfo here. - return _sStubInfoArmPIC; -} - -bool ArchHandler_arm::isCallSite(const Reference &ref) { - switch (ref.kindValue()) { - case thumb_b22: - case thumb_bl22: - case arm_b24: - case arm_bl24: - return true; - default: - return false; - } -} - -bool ArchHandler_arm::isPointer(const Reference &ref) { - return (ref.kindValue() == pointer32); -} - -bool ArchHandler_arm::isNonCallBranch(const Reference &ref) { - switch (ref.kindValue()) { - case thumb_b22: - case arm_b24: - return true; - default: - return false; - } -} - -bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { - switch (reloc.type) { - case ARM_RELOC_SECTDIFF: - case ARM_RELOC_LOCAL_SECTDIFF: - case ARM_RELOC_HALF_SECTDIFF: - case ARM_RELOC_HALF: - return true; - default: - return false; - } -} - -/// Trace references from stub atom to lazy pointer to target and get its name. -StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { - assert(stubAtom.contentType() == DefinedAtom::typeStub); - for (const Reference *ref : stubAtom) { - if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) { - if (lp->contentType() != DefinedAtom::typeLazyPointer) - continue; - for (const Reference *ref2 : *lp) { - if (ref2->kindValue() != lazyPointer) - continue; - return ref2->target()->name(); - } - } - } - return "stub"; -} - -/// Extract displacement from an ARM b/bl/blx instruction. -int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { - // Sign-extend imm24 - int32_t displacement = (instruction & 0x00FFFFFF) << 2; - if ((displacement & 0x02000000) != 0) - displacement |= 0xFC000000; - // If this is BLX and H bit set, add 2. - if ((instruction & 0xFF000000) == 0xFB000000) - displacement += 2; - return displacement; -} - -/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. -uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, - int32_t displacement, - bool targetIsThumb) { - assert((displacement <= 33554428) && (displacement > (-33554432)) - && "arm branch out of range"); - bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); - uint32_t newInstruction = (instruction & 0xFF000000); - uint32_t h = 0; - if (targetIsThumb) { - // Force use of BLX. - newInstruction = 0xFA000000; - if (!is_blx) { - assert(((instruction & 0xF0000000) == 0xE0000000) - && "no conditional arm blx"); - assert(((instruction & 0xFF000000) == 0xEB000000) - && "no arm pc-rel BX instruction"); - } - if (displacement & 2) - h = 1; - } - else { - // Force use of B/BL. - if (is_blx) - newInstruction = 0xEB000000; - } - newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); - return newInstruction; -} - -/// Extract displacement from a thumb b/bl/blx instruction. -int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, - uint32_t instrAddr) { - bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); - uint32_t s = (instruction >> 10) & 0x1; - uint32_t j1 = (instruction >> 29) & 0x1; - uint32_t j2 = (instruction >> 27) & 0x1; - uint32_t imm10 = instruction & 0x3FF; - uint32_t imm11 = (instruction >> 16) & 0x7FF; - uint32_t i1 = (j1 == s); - uint32_t i2 = (j2 == s); - uint32_t dis = - (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); - int32_t sdis = dis; - int32_t result = s ? (sdis | 0xFE000000) : sdis; - if (is_blx && (instrAddr & 0x2)) { - // The thumb blx instruction always has low bit of imm11 as zero. The way - // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that - // the blx instruction always 4-byte aligns the pc before adding the - // displacement from the blx. We must emulate that when decoding this. - result -= 2; - } - return result; -} - -/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. -uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, - uint32_t instrAddr, - int32_t displacement, - bool targetIsThumb) { - assert((displacement <= 16777214) && (displacement > (-16777216)) - && "thumb branch out of range"); - bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); - bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); - bool is_b = ((instruction & 0xD000F800) == 0x9000F000); - uint32_t newInstruction = (instruction & 0xD000F800); - if (is_bl || is_blx) { - if (targetIsThumb) { - newInstruction = 0xD000F000; // Use bl - } else { - newInstruction = 0xC000F000; // Use blx - // See note in getDisplacementFromThumbBranch() about blx. - if (instrAddr & 0x2) - displacement += 2; - } - } else if (is_b) { - assert(targetIsThumb && "no pc-rel thumb branch instruction that " - "switches to arm mode"); - } - else { - llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); - } - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; - uint32_t firstDisp = (s << 10) | imm10; - newInstruction |= (nextDisp << 16) | firstDisp; - return newInstruction; -} - -bool ArchHandler_arm::isThumbMovw(uint32_t instruction) { - return (instruction & 0x8000FBF0) == 0x0000F240; -} - -bool ArchHandler_arm::isThumbMovt(uint32_t instruction) { - return (instruction & 0x8000FBF0) == 0x0000F2C0; -} - -bool ArchHandler_arm::isArmMovw(uint32_t instruction) { - return (instruction & 0x0FF00000) == 0x03000000; -} - -bool ArchHandler_arm::isArmMovt(uint32_t instruction) { - return (instruction & 0x0FF00000) == 0x03400000; -} - -uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { - assert(isThumbMovw(instruction) || isThumbMovt(instruction)); - uint32_t i = ((instruction & 0x00000400) >> 10); - uint32_t imm4 = (instruction & 0x0000000F); - uint32_t imm3 = ((instruction & 0x70000000) >> 28); - uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); - return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; -} - -uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { - assert(isArmMovw(instruction) || isArmMovt(instruction)); - uint32_t imm4 = ((instruction & 0x000F0000) >> 16); - uint32_t imm12 = (instruction & 0x00000FFF); - return (imm4 << 12) | imm12; -} - -uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { - assert(isThumbMovw(instr) || isThumbMovt(instr)); - uint32_t imm4 = (word & 0xF000) >> 12; - uint32_t i = (word & 0x0800) >> 11; - uint32_t imm3 = (word & 0x0700) >> 8; - uint32_t imm8 = word & 0x00FF; - return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); -} - -uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { - assert(isArmMovw(instr) || isArmMovt(instr)); - uint32_t imm4 = (word & 0xF000) >> 12; - uint32_t imm12 = word & 0x0FFF; - return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; -} - -uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { - // The assembler often adds one to the address of a thumb function. - // We need to undo that so it does not look like an addend. - if (value & 1) { - if (isa<DefinedAtom>(target)) { - const MachODefinedAtom *machoTarget = - reinterpret_cast<const MachODefinedAtom *>(target); - if (machoTarget->isThumb()) - value &= -2; // mask off thumb-bit - } - } - return value; -} - -llvm::Error ArchHandler_arm::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]; - uint64_t targetAddress; - uint32_t instruction = *(const ulittle32_t *)fixupContent; - int32_t displacement; - switch (relocPattern(reloc)) { - case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: - // ex: bl _foo (and _foo is undefined) - if ((instruction & 0xD000F800) == 0x9000F000) - *kind = thumb_b22; - else - *kind = thumb_bl22; - if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) - return ec; - // Instruction contains branch to addend. - displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); - *addend = fixupAddress + 4 + displacement; - return llvm::Error::success(); - case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: - // ex: bl _foo (and _foo is defined) - if ((instruction & 0xD000F800) == 0x9000F000) - *kind = thumb_b22; - else - *kind = thumb_bl22; - displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); - targetAddress = fixupAddress + 4 + displacement; - return atomFromAddress(reloc.symbol, targetAddress, target, addend); - case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: - // ex: bl _foo+4 (and _foo is defined) - if ((instruction & 0xD000F800) == 0x9000F000) - *kind = thumb_b22; - else - *kind = thumb_bl22; - displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); - targetAddress = fixupAddress + 4 + displacement; - if (auto ec = atomFromAddress(0, reloc.value, target, addend)) - return ec; - // reloc.value is target atom's address. Instruction contains branch - // to atom+addend. - *addend += (targetAddress - reloc.value); - return llvm::Error::success(); - case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: - // ex: bl _foo (and _foo is undefined) - if (((instruction & 0x0F000000) == 0x0A000000) - && ((instruction & 0xF0000000) != 0xF0000000)) - *kind = arm_b24; - else - *kind = arm_bl24; - if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) - return ec; - // Instruction contains branch to addend. - displacement = getDisplacementFromArmBranch(instruction); - *addend = fixupAddress + 8 + displacement; - return llvm::Error::success(); - case ARM_RELOC_BR24 | rPcRel | rLength4: - // ex: bl _foo (and _foo is defined) - if (((instruction & 0x0F000000) == 0x0A000000) - && ((instruction & 0xF0000000) != 0xF0000000)) - *kind = arm_b24; - else - *kind = arm_bl24; - displacement = getDisplacementFromArmBranch(instruction); - targetAddress = fixupAddress + 8 + displacement; - return atomFromAddress(reloc.symbol, targetAddress, target, addend); - case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: - // ex: bl _foo+4 (and _foo is defined) - if (((instruction & 0x0F000000) == 0x0A000000) - && ((instruction & 0xF0000000) != 0xF0000000)) - *kind = arm_b24; - else - *kind = arm_bl24; - displacement = getDisplacementFromArmBranch(instruction); - targetAddress = fixupAddress + 8 + displacement; - if (auto ec = atomFromAddress(0, reloc.value, target, addend)) - return ec; - // reloc.value is target atom's address. Instruction contains branch - // to atom+addend. - *addend += (targetAddress - reloc.value); - return llvm::Error::success(); - case ARM_RELOC_VANILLA | rExtern | rLength4: - // ex: .long _foo (and _foo is undefined) - *kind = pointer32; - if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) - return ec; - *addend = instruction; - return llvm::Error::success(); - case ARM_RELOC_VANILLA | rLength4: - // ex: .long _foo (and _foo is defined) - *kind = pointer32; - if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend)) - return ec; - *addend = clearThumbBit((uint32_t) * addend, *target); - return llvm::Error::success(); - case ARM_RELOC_VANILLA | rScattered | rLength4: - // ex: .long _foo+a (and _foo is defined) - *kind = pointer32; - if (auto ec = atomFromAddress(0, reloc.value, target, addend)) - return ec; - *addend += (clearThumbBit(instruction, *target) - reloc.value); - return llvm::Error::success(); - default: - return llvm::make_error<GenericError>("unsupported arm relocation type"); - } - return llvm::Error::success(); -} - -llvm::Error -ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, - const normalized::Relocation &reloc2, - const DefinedAtom *inAtom, - uint32_t offsetInAtom, - uint64_t fixupAddress, bool isBig, - bool scatterable, - FindAtomBySectionAndAddress atomFromAddr, - FindAtomBySymbolIndex atomFromSymbolIndex, - Reference::KindValue *kind, - const lld::Atom **target, - Reference::Addend *addend) { - bool pointerDiff = false; - bool funcRel; - bool top; - bool thumbReloc; - switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { - case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | - ARM_RELOC_PAIR | rScattered | rLenThmbLo): - // ex: movw r1, :lower16:(_x-L1) [thumb mode] - *kind = thumb_movw_funcRel; - funcRel = true; - top = false; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | - ARM_RELOC_PAIR | rScattered | rLenThmbHi): - // ex: movt r1, :upper16:(_x-L1) [thumb mode] - *kind = thumb_movt_funcRel; - funcRel = true; - top = true; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | - ARM_RELOC_PAIR | rScattered | rLenArmLo): - // ex: movw r1, :lower16:(_x-L1) [arm mode] - *kind = arm_movw_funcRel; - funcRel = true; - top = false; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | - ARM_RELOC_PAIR | rScattered | rLenArmHi): - // ex: movt r1, :upper16:(_x-L1) [arm mode] - *kind = arm_movt_funcRel; - funcRel = true; - top = true; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | - ARM_RELOC_PAIR | rLenThmbLo): - // ex: movw r1, :lower16:_x [thumb mode] - *kind = thumb_movw; - funcRel = false; - top = false; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | - ARM_RELOC_PAIR | rLenThmbHi): - // ex: movt r1, :upper16:_x [thumb mode] - *kind = thumb_movt; - funcRel = false; - top = true; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rLenArmLo) << 16 | - ARM_RELOC_PAIR | rLenArmLo): - // ex: movw r1, :lower16:_x [arm mode] - *kind = arm_movw; - funcRel = false; - top = false; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rLenArmHi) << 16 | - ARM_RELOC_PAIR | rLenArmHi): - // ex: movt r1, :upper16:_x [arm mode] - *kind = arm_movt; - funcRel = false; - top = true; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | - ARM_RELOC_PAIR | rLenThmbLo): - // ex: movw r1, :lower16:_x+a [thumb mode] - *kind = thumb_movw; - funcRel = false; - top = false; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | - ARM_RELOC_PAIR | rLenThmbHi): - // ex: movt r1, :upper16:_x+a [thumb mode] - *kind = thumb_movt; - funcRel = false; - top = true; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | - ARM_RELOC_PAIR | rLenArmLo): - // ex: movw r1, :lower16:_x+a [arm mode] - *kind = arm_movw; - funcRel = false; - top = false; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | - ARM_RELOC_PAIR | rLenArmHi): - // ex: movt r1, :upper16:_x+a [arm mode] - *kind = arm_movt; - funcRel = false; - top = true; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | - ARM_RELOC_PAIR | rLenThmbLo): - // ex: movw r1, :lower16:_undef [thumb mode] - *kind = thumb_movw; - funcRel = false; - top = false; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | - ARM_RELOC_PAIR | rLenThmbHi): - // ex: movt r1, :upper16:_undef [thumb mode] - *kind = thumb_movt; - funcRel = false; - top = true; - thumbReloc = true; - break; - case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | - ARM_RELOC_PAIR | rLenArmLo): - // ex: movw r1, :lower16:_undef [arm mode] - *kind = arm_movw; - funcRel = false; - top = false; - thumbReloc = false; - break; - case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | - ARM_RELOC_PAIR | rLenArmHi): - // ex: movt r1, :upper16:_undef [arm mode] - *kind = arm_movt; - funcRel = false; - top = true; - thumbReloc = false; - break; - case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | - ARM_RELOC_PAIR | rScattered | rLength4): - case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | - ARM_RELOC_PAIR | rScattered | rLength4): - // ex: .long _foo - . - pointerDiff = true; - break; - default: - return llvm::make_error<GenericError>("unsupported arm relocation pair"); - } - const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; - uint32_t instruction = *(const ulittle32_t *)fixupContent; - uint32_t value; - uint32_t fromAddress; - uint32_t toAddress; - uint16_t instruction16; - uint16_t other16; - const lld::Atom *fromTarget; - Reference::Addend offsetInTo; - Reference::Addend offsetInFrom; - if (pointerDiff) { - toAddress = reloc1.value; - fromAddress = reloc2.value; - if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) - return ec; - if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) - return ec; - if (scatterable && (fromTarget != inAtom)) - return llvm::make_error<GenericError>( - "SECTDIFF relocation where subtrahend label is not in atom"); - *kind = delta32; - value = clearThumbBit(instruction, *target); - *addend = (int32_t)(value - (toAddress - fixupAddress)); - } else if (funcRel) { - toAddress = reloc1.value; - fromAddress = reloc2.value; - if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) - return ec; - if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) - return ec; - if (fromTarget != inAtom) - return llvm::make_error<GenericError>("ARM_RELOC_HALF_SECTDIFF relocation" - " where subtrahend label is not in atom"); - other16 = (reloc2.offset & 0xFFFF); - if (thumbReloc) { - if (top) { - if (!isThumbMovt(instruction)) - return llvm::make_error<GenericError>("expected movt instruction"); - } - else { - if (!isThumbMovw(instruction)) - return llvm::make_error<GenericError>("expected movw instruction"); - } - instruction16 = getWordFromThumbMov(instruction); - } - else { - if (top) { - if (!isArmMovt(instruction)) - return llvm::make_error<GenericError>("expected movt instruction"); - } - else { - if (!isArmMovw(instruction)) - return llvm::make_error<GenericError>("expected movw instruction"); - } - instruction16 = getWordFromArmMov(instruction); - } - if (top) - value = (instruction16 << 16) | other16; - else - value = (other16 << 16) | instruction16; - value = clearThumbBit(value, *target); - int64_t ta = (int64_t) value - (toAddress - fromAddress); - *addend = ta - offsetInFrom; - return llvm::Error::success(); - } else { - uint32_t sectIndex; - if (thumbReloc) { - if (top) { - if (!isThumbMovt(instruction)) - return llvm::make_error<GenericError>("expected movt instruction"); - } - else { - if (!isThumbMovw(instruction)) - return llvm::make_error<GenericError>("expected movw instruction"); - } - instruction16 = getWordFromThumbMov(instruction); - } - else { - if (top) { - if (!isArmMovt(instruction)) - return llvm::make_error<GenericError>("expected movt instruction"); - } - else { - if (!isArmMovw(instruction)) - return llvm::make_error<GenericError>("expected movw instruction"); - } - instruction16 = getWordFromArmMov(instruction); - } - other16 = (reloc2.offset & 0xFFFF); - if (top) - value = (instruction16 << 16) | other16; - else - value = (other16 << 16) | instruction16; - if (reloc1.isExtern) { - if (auto ec = atomFromSymbolIndex(reloc1.symbol, target)) - return ec; - *addend = value; - } else { - if (reloc1.scattered) { - toAddress = reloc1.value; - sectIndex = 0; - } else { - toAddress = value; - sectIndex = reloc1.symbol; - } - if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo)) - return ec; - *addend = value - toAddress; - } - } - - return llvm::Error::success(); -} - -void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, - uint64_t fixupAddress, - uint64_t targetAddress, - uint64_t inAtomAddress, - bool &thumbMode, bool targetIsThumb) { - if (ref.kindNamespace() != Reference::KindNamespace::mach_o) - return; - assert(ref.kindArch() == Reference::KindArch::ARM); - ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); - int32_t displacement; - uint16_t value16; - uint32_t value32; - switch (static_cast<ArmKind>(ref.kindValue())) { - case modeThumbCode: - thumbMode = true; - break; - case modeArmCode: - thumbMode = false; - break; - case modeData: - break; - case thumb_b22: - case thumb_bl22: - assert(thumbMode); - displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); - value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, - displacement, targetIsThumb); - *loc32 = value32; - break; - case thumb_movw: - assert(thumbMode); - value16 = (targetAddress + ref.addend()) & 0xFFFF; - if (targetIsThumb) - value16 |= 1; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movt: - assert(thumbMode); - value16 = (targetAddress + ref.addend()) >> 16; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movw_funcRel: - assert(thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; - if (targetIsThumb) - value16 |= 1; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movt_funcRel: - assert(thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case arm_b24: - case arm_bl24: - assert(!thumbMode); - displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); - value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); - *loc32 = value32; - break; - case arm_movw: - assert(!thumbMode); - value16 = (targetAddress + ref.addend()) & 0xFFFF; - if (targetIsThumb) - value16 |= 1; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movt: - assert(!thumbMode); - value16 = (targetAddress + ref.addend()) >> 16; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movw_funcRel: - assert(!thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; - if (targetIsThumb) - value16 |= 1; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movt_funcRel: - assert(!thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case pointer32: - if (targetIsThumb) - *loc32 = targetAddress + ref.addend() + 1; - else - *loc32 = targetAddress + ref.addend(); - break; - case delta32: - if (targetIsThumb) - *loc32 = targetAddress - fixupAddress + ref.addend() + 1; - else - *loc32 = targetAddress - fixupAddress + ref.addend(); - break; - case lazyPointer: - // do nothing - break; - case lazyImmediateLocation: - *loc32 = ref.addend(); - break; - case invalid: - llvm_unreachable("invalid ARM Reference Kind"); - break; - } -} - -void ArchHandler_arm::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. - bool thumbMode = false; - for (const Reference *ref : atom) { - uint32_t offset = ref->offsetInAtom(); - const Atom *target = ref->target(); - uint64_t targetAddress = 0; - bool targetIsThumb = false; - if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) { - targetAddress = findAddress(*target); - targetIsThumb = isThumbFunction(*defTarg); - } - uint64_t atomAddress = findAddress(atom); - uint64_t fixupAddress = atomAddress + offset; - if (relocatable) { - applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, - targetAddress, atomAddress, thumbMode, - targetIsThumb); - } else { - applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, - targetAddress, atomAddress, thumbMode, targetIsThumb); - } - } -} - -bool ArchHandler_arm::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_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc, - uint64_t fixupAddress, - uint64_t targetAddress, - uint64_t inAtomAddress, - bool &thumbMode, - bool targetIsThumb) { - if (ref.kindNamespace() != Reference::KindNamespace::mach_o) - return; - assert(ref.kindArch() == Reference::KindArch::ARM); - bool useExternalReloc = useExternalRelocationTo(*ref.target()); - ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); - int32_t displacement; - uint16_t value16; - uint32_t value32; - bool targetIsUndef = isa<UndefinedAtom>(ref.target()); - switch (static_cast<ArmKind>(ref.kindValue())) { - case modeThumbCode: - thumbMode = true; - break; - case modeArmCode: - thumbMode = false; - break; - case modeData: - break; - case thumb_b22: - case thumb_bl22: - assert(thumbMode); - if (useExternalReloc) - displacement = (ref.addend() - (fixupAddress + 4)); - else - displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); - value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, - displacement, - targetIsUndef || targetIsThumb); - *loc32 = value32; - break; - case thumb_movw: - assert(thumbMode); - if (useExternalReloc) - value16 = ref.addend() & 0xFFFF; - else - value16 = (targetAddress + ref.addend()) & 0xFFFF; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movt: - assert(thumbMode); - if (useExternalReloc) - value16 = ref.addend() >> 16; - else - value16 = (targetAddress + ref.addend()) >> 16; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movw_funcRel: - assert(thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case thumb_movt_funcRel: - assert(thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; - *loc32 = setWordFromThumbMov(*loc32, value16); - break; - case arm_b24: - case arm_bl24: - assert(!thumbMode); - if (useExternalReloc) - displacement = (ref.addend() - (fixupAddress + 8)); - else - displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); - value32 = setDisplacementInArmBranch(*loc32, displacement, - targetIsThumb); - *loc32 = value32; - break; - case arm_movw: - assert(!thumbMode); - if (useExternalReloc) - value16 = ref.addend() & 0xFFFF; - else - value16 = (targetAddress + ref.addend()) & 0xFFFF; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movt: - assert(!thumbMode); - if (useExternalReloc) - value16 = ref.addend() >> 16; - else - value16 = (targetAddress + ref.addend()) >> 16; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movw_funcRel: - assert(!thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case arm_movt_funcRel: - assert(!thumbMode); - value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; - *loc32 = setWordFromArmMov(*loc32, value16); - break; - case pointer32: - *loc32 = targetAddress + ref.addend(); - break; - case delta32: - *loc32 = targetAddress - fixupAddress + ref.addend(); - break; - case lazyPointer: - case lazyImmediateLocation: - // do nothing - break; - case invalid: - llvm_unreachable("invalid ARM Reference Kind"); - break; - } -} - -void ArchHandler_arm::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::ARM); - uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); - bool useExternalReloc = useExternalRelocationTo(*ref.target()); - uint32_t targetAtomAddress; - uint32_t fromAtomAddress; - uint16_t other16; - switch (static_cast<ArmKind>(ref.kindValue())) { - case modeThumbCode: - case modeArmCode: - case modeData: - // Do nothing. - break; - case thumb_b22: - case thumb_bl22: - if (useExternalReloc) { - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4); - } else { - if (ref.addend() != 0) - appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), - ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4); - else - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_THUMB_RELOC_BR22 | rPcRel | rLength4); - } - break; - case thumb_movw: - if (useExternalReloc) { - other16 = ref.addend() >> 16; - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_HALF | rExtern | rLenThmbLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbLo); - } else { - targetAtomAddress = addressForAtom(*ref.target()); - if (ref.addend() != 0) { - other16 = (targetAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF | rScattered | rLenThmbLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbLo); - } else { - other16 = (targetAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_HALF | rLenThmbLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbLo); - } - } - break; - case thumb_movt: - if (useExternalReloc) { - other16 = ref.addend() & 0xFFFF; - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_HALF | rExtern | rLenThmbHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbHi); - } else { - targetAtomAddress = addressForAtom(*ref.target()); - if (ref.addend() != 0) { - other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF | rScattered | rLenThmbHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbHi); - } else { - other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_HALF | rLenThmbHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenThmbHi); - } - } - break; - case thumb_movw_funcRel: - fromAtomAddress = addressForAtom(atom); - targetAtomAddress = addressForAtom(*ref.target()); - other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo); - appendReloc(relocs, other16, 0, fromAtomAddress, - ARM_RELOC_PAIR | rScattered | rLenThmbLo); - break; - case thumb_movt_funcRel: - fromAtomAddress = addressForAtom(atom); - targetAtomAddress = addressForAtom(*ref.target()); - other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi); - appendReloc(relocs, other16, 0, fromAtomAddress, - ARM_RELOC_PAIR | rScattered | rLenThmbHi); - break; - case arm_b24: - case arm_bl24: - if (useExternalReloc) { - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_BR24 | rExtern | rPcRel | rLength4); - } else { - if (ref.addend() != 0) - appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), - ARM_RELOC_BR24 | rScattered | rPcRel | rLength4); - else - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_BR24 | rPcRel | rLength4); - } - break; - case arm_movw: - if (useExternalReloc) { - other16 = ref.addend() >> 16; - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_HALF | rExtern | rLenArmLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmLo); - } else { - targetAtomAddress = addressForAtom(*ref.target()); - if (ref.addend() != 0) { - other16 = (targetAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF | rScattered | rLenArmLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmLo); - } else { - other16 = (targetAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_HALF | rLenArmLo); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmLo); - } - } - break; - case arm_movt: - if (useExternalReloc) { - other16 = ref.addend() & 0xFFFF; - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_HALF | rExtern | rLenArmHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmHi); - } else { - targetAtomAddress = addressForAtom(*ref.target()); - if (ref.addend() != 0) { - other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF | rScattered | rLenArmHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmHi); - } else { - other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_HALF | rLenArmHi); - appendReloc(relocs, other16, 0, 0, - ARM_RELOC_PAIR | rLenArmHi); - } - } - break; - case arm_movw_funcRel: - fromAtomAddress = addressForAtom(atom); - targetAtomAddress = addressForAtom(*ref.target()); - other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo); - appendReloc(relocs, other16, 0, fromAtomAddress, - ARM_RELOC_PAIR | rScattered | rLenArmLo); - break; - case arm_movt_funcRel: - fromAtomAddress = addressForAtom(atom); - targetAtomAddress = addressForAtom(*ref.target()); - other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; - appendReloc(relocs, sectionOffset, 0, targetAtomAddress, - ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi); - appendReloc(relocs, other16, 0, fromAtomAddress, - ARM_RELOC_PAIR | rScattered | rLenArmHi); - break; - case pointer32: - if (useExternalReloc) { - appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, - ARM_RELOC_VANILLA | rExtern | rLength4); - } - else { - if (ref.addend() != 0) - appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), - ARM_RELOC_VANILLA | rScattered | rLength4); - else - appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, - ARM_RELOC_VANILLA | rLength4); - } - break; - case delta32: - appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), - ARM_RELOC_SECTDIFF | rScattered | rLength4); - appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + - ref.offsetInAtom(), - ARM_RELOC_PAIR | rScattered | rLength4); - break; - case lazyPointer: - case lazyImmediateLocation: - // do nothing - break; - case invalid: - llvm_unreachable("invalid ARM Reference Kind"); - break; - } -} - -void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { - if (atom.isThumb()) { - atom.addReference(Reference::KindNamespace::mach_o, - Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0); - } -} - -bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { - for (const Reference *ref : atom) { - if (ref->offsetInAtom() != 0) - return false; - if (ref->kindNamespace() != Reference::KindNamespace::mach_o) - continue; - assert(ref->kindArch() == Reference::KindArch::ARM); - if (ref->kindValue() == modeThumbCode) - return true; - } - return false; -} - -class Thumb2ToArmShimAtom : public SimpleDefinedAtom { -public: - Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, - const DefinedAtom &target) - : SimpleDefinedAtom(file) { - addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, - ArchHandler_arm::modeThumbCode, 0, this, 0); - addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, - ArchHandler_arm::delta32, 8, &target, 0); - std::string name = std::string(targetName) + "$shim"; - StringRef tmp(name); - _name = tmp.copy(file.allocator()); - } - - ~Thumb2ToArmShimAtom() override = default; - - StringRef name() const override { - return _name; - } - - ContentType contentType() const override { - return DefinedAtom::typeCode; - } - - Alignment alignment() const override { return 4; } - - uint64_t size() const override { - return 12; - } - - ContentPermissions permissions() const override { - return DefinedAtom::permR_X; - } - - ArrayRef<uint8_t> rawContent() const override { - static const uint8_t bytes[] = - { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4 - 0xFF, 0x44, // add ip, pc, ip - 0x60, 0x47, // ldr pc, [ip] - 0x00, 0x00, 0x00, 0x00 }; // .long target - this - assert(sizeof(bytes) == size()); - return llvm::makeArrayRef(bytes, sizeof(bytes)); - } -private: - StringRef _name; -}; - -class ArmToThumbShimAtom : public SimpleDefinedAtom { -public: - ArmToThumbShimAtom(MachOFile &file, StringRef targetName, - const DefinedAtom &target) - : SimpleDefinedAtom(file) { - addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, - ArchHandler_arm::delta32, 12, &target, 0); - std::string name = std::string(targetName) + "$shim"; - StringRef tmp(name); - _name = tmp.copy(file.allocator()); - } - - ~ArmToThumbShimAtom() override = default; - - StringRef name() const override { - return _name; - } - - ContentType contentType() const override { - return DefinedAtom::typeCode; - } - - Alignment alignment() const override { return 4; } - - uint64_t size() const override { - return 16; - } - - ContentPermissions permissions() const override { - return DefinedAtom::permR_X; - } - - ArrayRef<uint8_t> rawContent() const override { - static const uint8_t bytes[] = - { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 - 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip - 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] - 0x00, 0x00, 0x00, 0x00 }; // .long target - this - assert(sizeof(bytes) == size()); - return llvm::makeArrayRef(bytes, sizeof(bytes)); - } -private: - StringRef _name; -}; - -const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, - bool thumbToArm, - const DefinedAtom &target) { - bool isStub = (target.contentType() == DefinedAtom::typeStub); - StringRef targetName = isStub ? stubName(target) : target.name(); - if (thumbToArm) - return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); - else - return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); -} - -std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() { - return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm()); -} - -} // namespace mach_o -} // namespace lld diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp deleted file mode 100644 index bee081aec067..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp deleted file mode 100644 index 6ea8e8c42e80..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp deleted file mode 100644 index 687407049d4b..000000000000 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/Atoms.h b/lld/lib/ReaderWriter/MachO/Atoms.h deleted file mode 100644 index c61aaa88e8df..000000000000 --- a/lld/lib/ReaderWriter/MachO/Atoms.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp deleted file mode 100644 index f3636feb217b..000000000000 --- a/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/DebugInfo.h b/lld/lib/ReaderWriter/MachO/DebugInfo.h deleted file mode 100644 index 591dd1ebad86..000000000000 --- a/lld/lib/ReaderWriter/MachO/DebugInfo.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ExecutableAtoms.h b/lld/lib/ReaderWriter/MachO/ExecutableAtoms.h deleted file mode 100644 index ce94be457026..000000000000 --- a/lld/lib/ReaderWriter/MachO/ExecutableAtoms.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/File.h b/lld/lib/ReaderWriter/MachO/File.h deleted file mode 100644 index 77832969c6b3..000000000000 --- a/lld/lib/ReaderWriter/MachO/File.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/FlatNamespaceFile.h b/lld/lib/ReaderWriter/MachO/FlatNamespaceFile.h deleted file mode 100644 index 1885effef49f..000000000000 --- a/lld/lib/ReaderWriter/MachO/FlatNamespaceFile.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/GOTPass.cpp b/lld/lib/ReaderWriter/MachO/GOTPass.cpp deleted file mode 100644 index 9cb5ab5eab12..000000000000 --- a/lld/lib/ReaderWriter/MachO/GOTPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/LayoutPass.cpp b/lld/lib/ReaderWriter/MachO/LayoutPass.cpp deleted file mode 100644 index e92fdf1b4913..000000000000 --- a/lld/lib/ReaderWriter/MachO/LayoutPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/LayoutPass.h b/lld/lib/ReaderWriter/MachO/LayoutPass.h deleted file mode 100644 index 904e16b7fb0e..000000000000 --- a/lld/lib/ReaderWriter/MachO/LayoutPass.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp deleted file mode 100644 index acd919e4d411..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ /dev/null @@ -1,1104 +0,0 @@ -//===- lib/ReaderWriter/MachO/MachOLinkingContext.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/ErrorHandler.h" -#include "lld/ReaderWriter/MachOLinkingContext.h" -#include "ArchHandler.h" -#include "File.h" -#include "FlatNamespaceFile.h" -#include "MachONormalizedFile.h" -#include "MachOPasses.h" -#include "SectCreateFile.h" -#include "lld/Common/Driver.h" -#include "lld/Core/ArchiveLibraryFile.h" -#include "lld/Core/PassManager.h" -#include "lld/Core/Reader.h" -#include "lld/Core/Writer.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/Triple.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Demangle/Demangle.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/Path.h" -#include <algorithm> - -using lld::mach_o::ArchHandler; -using lld::mach_o::MachOFile; -using lld::mach_o::MachODylibFile; -using namespace llvm::MachO; - -namespace lld { - -bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) { - result = 0; - - if (str.empty()) - return false; - - SmallVector<StringRef, 3> parts; - llvm::SplitString(str, parts, "."); - - unsigned long long num; - if (llvm::getAsUnsignedInteger(parts[0], 10, num)) - return true; - if (num > 65535) - return true; - result = num << 16; - - if (parts.size() > 1) { - if (llvm::getAsUnsignedInteger(parts[1], 10, num)) - return true; - if (num > 255) - return true; - result |= (num << 8); - } - - if (parts.size() > 2) { - if (llvm::getAsUnsignedInteger(parts[2], 10, num)) - return true; - if (num > 255) - return true; - result |= num; - } - - return false; -} - -bool MachOLinkingContext::parsePackedVersion(StringRef str, uint64_t &result) { - result = 0; - - if (str.empty()) - return false; - - SmallVector<StringRef, 5> parts; - llvm::SplitString(str, parts, "."); - - unsigned long long num; - if (llvm::getAsUnsignedInteger(parts[0], 10, num)) - return true; - if (num > 0xFFFFFF) - return true; - result = num << 40; - - unsigned Shift = 30; - for (StringRef str : llvm::makeArrayRef(parts).slice(1)) { - if (llvm::getAsUnsignedInteger(str, 10, num)) - return true; - if (num > 0x3FF) - return true; - result |= (num << Shift); - Shift -= 10; - } - - return false; -} - -MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = { - { "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL }, - { "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL }, - { "ppc", arch_ppc, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL }, - { "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 }, - { "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }, - { "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S }, - { "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL }, - { "", arch_unknown,false, 0, 0 } -}; - -MachOLinkingContext::Arch -MachOLinkingContext::archFromCpuType(uint32_t cputype, uint32_t cpusubtype) { - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if ((info->cputype == cputype) && (info->cpusubtype == cpusubtype)) - return info->arch; - } - return arch_unknown; -} - -MachOLinkingContext::Arch -MachOLinkingContext::archFromName(StringRef archName) { - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->archName.equals(archName)) - return info->arch; - } - return arch_unknown; -} - -StringRef MachOLinkingContext::nameFromArch(Arch arch) { - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) - return info->archName; - } - return "<unknown>"; -} - -uint32_t MachOLinkingContext::cpuTypeFromArch(Arch arch) { - assert(arch != arch_unknown); - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) - return info->cputype; - } - llvm_unreachable("Unknown arch type"); -} - -uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) { - assert(arch != arch_unknown); - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) - return info->cpusubtype; - } - llvm_unreachable("Unknown arch type"); -} - -bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) { - return mach_o::normalized::isThinObjectFile(path, arch); -} - -bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, - uint32_t &size) { - return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size); -} - -MachOLinkingContext::MachOLinkingContext() {} - -MachOLinkingContext::~MachOLinkingContext() { - // Atoms are allocated on BumpPtrAllocator's on File's. - // As we transfer atoms from one file to another, we need to clear all of the - // atoms before we remove any of the BumpPtrAllocator's. - auto &nodes = getNodes(); - for (unsigned i = 0, e = nodes.size(); i != e; ++i) { - FileNode *node = dyn_cast<FileNode>(nodes[i].get()); - if (!node) - continue; - File *file = node->getFile(); - file->clearAtoms(); - } -} - -void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os, - uint32_t minOSVersion, - bool exportDynamicSymbols) { - _outputMachOType = type; - _arch = arch; - _os = os; - _osMinVersion = minOSVersion; - - // If min OS not specified on command line, use reasonable defaults. - // Note that we only do sensible defaults when emitting something other than - // object and preload. - if (_outputMachOType != llvm::MachO::MH_OBJECT && - _outputMachOType != llvm::MachO::MH_PRELOAD) { - if (minOSVersion == 0) { - switch (_arch) { - case arch_x86_64: - case arch_x86: - parsePackedVersion("10.8", _osMinVersion); - _os = MachOLinkingContext::OS::macOSX; - break; - case arch_armv6: - case arch_armv7: - case arch_armv7s: - case arch_arm64: - parsePackedVersion("7.0", _osMinVersion); - _os = MachOLinkingContext::OS::iOS; - break; - default: - break; - } - } - } - - switch (_outputMachOType) { - case llvm::MachO::MH_EXECUTE: - // If targeting newer OS, use _main - if (minOS("10.8", "6.0")) { - _entrySymbolName = "_main"; - } else { - // If targeting older OS, use start (in crt1.o) - _entrySymbolName = "start"; - } - - // __PAGEZERO defaults to 4GB on 64-bit (except for PP64 which lld does not - // support) and 4KB on 32-bit. - if (is64Bit(_arch)) { - _pageZeroSize = 0x100000000; - } else { - _pageZeroSize = 0x1000; - } - - // Initial base address is __PAGEZERO size. - _baseAddress = _pageZeroSize; - - // Make PIE by default when targetting newer OSs. - switch (os) { - case OS::macOSX: - if (minOSVersion >= 0x000A0700) // MacOSX 10.7 - _pie = true; - break; - case OS::iOS: - if (minOSVersion >= 0x00040300) // iOS 4.3 - _pie = true; - break; - case OS::iOS_simulator: - _pie = true; - break; - case OS::unknown: - break; - } - setGlobalsAreDeadStripRoots(exportDynamicSymbols); - break; - case llvm::MachO::MH_DYLIB: - setGlobalsAreDeadStripRoots(exportDynamicSymbols); - break; - case llvm::MachO::MH_BUNDLE: - break; - case llvm::MachO::MH_OBJECT: - _printRemainingUndefines = false; - _allowRemainingUndefines = true; - break; - default: - break; - } - - // Set default segment page sizes based on arch. - if (arch == arch_arm64) - _pageSize = 4*4096; -} - -uint32_t MachOLinkingContext::getCPUType() const { - return cpuTypeFromArch(_arch); -} - -uint32_t MachOLinkingContext::getCPUSubType() const { - return cpuSubtypeFromArch(_arch); -} - -bool MachOLinkingContext::is64Bit(Arch arch) { - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) { - return (info->cputype & CPU_ARCH_ABI64); - } - } - // unknown archs are not 64-bit. - return false; -} - -bool MachOLinkingContext::isHostEndian(Arch arch) { - assert(arch != arch_unknown); - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) { - return (info->littleEndian == llvm::sys::IsLittleEndianHost); - } - } - llvm_unreachable("Unknown arch type"); -} - -bool MachOLinkingContext::isBigEndian(Arch arch) { - assert(arch != arch_unknown); - for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { - if (info->arch == arch) { - return ! info->littleEndian; - } - } - llvm_unreachable("Unknown arch type"); -} - -bool MachOLinkingContext::is64Bit() const { - return is64Bit(_arch); -} - -bool MachOLinkingContext::outputTypeHasEntry() const { - switch (_outputMachOType) { - case MH_EXECUTE: - case MH_DYLINKER: - case MH_PRELOAD: - return true; - default: - return false; - } -} - -bool MachOLinkingContext::needsStubsPass() const { - switch (_outputMachOType) { - case MH_EXECUTE: - return !_outputMachOTypeStatic; - case MH_DYLIB: - case MH_BUNDLE: - return true; - default: - return false; - } -} - -bool MachOLinkingContext::needsGOTPass() const { - // GOT pass not used in -r mode. - if (_outputMachOType == MH_OBJECT) - return false; - // Only some arches use GOT pass. - switch (_arch) { - case arch_x86_64: - case arch_arm64: - return true; - default: - return false; - } -} - -bool MachOLinkingContext::needsCompactUnwindPass() const { - switch (_outputMachOType) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - return archHandler().needsCompactUnwind(); - default: - return false; - } -} - -bool MachOLinkingContext::needsObjCPass() const { - // ObjC pass is only needed if any of the inputs were ObjC. - return _objcConstraint != objc_unknown; -} - -bool MachOLinkingContext::needsShimPass() const { - // Shim pass only used in final executables. - if (_outputMachOType == MH_OBJECT) - return false; - // Only 32-bit arm arches use Shim pass. - switch (_arch) { - case arch_armv6: - case arch_armv7: - case arch_armv7s: - return true; - default: - return false; - } -} - -bool MachOLinkingContext::needsTLVPass() const { - switch (_outputMachOType) { - case MH_BUNDLE: - case MH_EXECUTE: - case MH_DYLIB: - return true; - default: - return false; - } -} - -StringRef MachOLinkingContext::binderSymbolName() const { - return archHandler().stubInfo().binderSymbolName; -} - -bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const { - uint32_t parsedVersion; - switch (_os) { - case OS::macOSX: - if (parsePackedVersion(mac, parsedVersion)) - return false; - return _osMinVersion >= parsedVersion; - case OS::iOS: - case OS::iOS_simulator: - if (parsePackedVersion(iOS, parsedVersion)) - return false; - return _osMinVersion >= parsedVersion; - case OS::unknown: - // If we don't know the target, then assume that we don't meet the min OS. - // This matches the ld64 behaviour - return false; - } - llvm_unreachable("invalid OS enum"); -} - -bool MachOLinkingContext::addEntryPointLoadCommand() const { - if ((_outputMachOType == MH_EXECUTE) && !_outputMachOTypeStatic) { - return minOS("10.8", "6.0"); - } - return false; -} - -bool MachOLinkingContext::addUnixThreadLoadCommand() const { - switch (_outputMachOType) { - case MH_EXECUTE: - if (_outputMachOTypeStatic) - return true; - else - return !minOS("10.8", "6.0"); - break; - case MH_DYLINKER: - case MH_PRELOAD: - return true; - default: - return false; - } -} - -bool MachOLinkingContext::pathExists(StringRef path) const { - if (!_testingFileUsage) - return llvm::sys::fs::exists(path.str()); - - // Otherwise, we're in test mode: only files explicitly provided on the - // command-line exist. - std::string key = path.str(); - std::replace(key.begin(), key.end(), '\\', '/'); - return _existingPaths.find(key) != _existingPaths.end(); -} - -bool MachOLinkingContext::fileExists(StringRef path) const { - bool found = pathExists(path); - // Log search misses. - if (!found) - addInputFileNotFound(path); - - // When testing, file is never opened, so logging is done here. - if (_testingFileUsage && found) - addInputFileDependency(path); - - return found; -} - -void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) { - _syslibRoots = paths; -} - -void MachOLinkingContext::addRpath(StringRef rpath) { - _rpaths.push_back(rpath); -} - -void MachOLinkingContext::addModifiedSearchDir(StringRef libPath, - bool isSystemPath) { - bool addedModifiedPath = false; - - // -syslibroot only applies to absolute paths. - if (libPath.startswith("/")) { - for (auto syslibRoot : _syslibRoots) { - SmallString<256> path(syslibRoot); - llvm::sys::path::append(path, libPath); - if (pathExists(path)) { - _searchDirs.push_back(path.str().copy(_allocator)); - addedModifiedPath = true; - } - } - } - - if (addedModifiedPath) - return; - - // Finally, if only one -syslibroot is given, system paths which aren't in it - // get suppressed. - if (_syslibRoots.size() != 1 || !isSystemPath) { - if (pathExists(libPath)) { - _searchDirs.push_back(libPath); - } - } -} - -void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath, - bool isSystemPath) { - bool pathAdded = false; - - // -syslibroot only used with to absolute framework search paths. - if (fwPath.startswith("/")) { - for (auto syslibRoot : _syslibRoots) { - SmallString<256> path(syslibRoot); - llvm::sys::path::append(path, fwPath); - if (pathExists(path)) { - _frameworkDirs.push_back(path.str().copy(_allocator)); - pathAdded = true; - } - } - } - // If fwPath found in any -syslibroot, then done. - if (pathAdded) - return; - - // If only one -syslibroot, system paths not in that SDK are suppressed. - if (isSystemPath && (_syslibRoots.size() == 1)) - return; - - // Only use raw fwPath if that directory exists. - if (pathExists(fwPath)) - _frameworkDirs.push_back(fwPath); -} - -llvm::Optional<StringRef> -MachOLinkingContext::searchDirForLibrary(StringRef path, - StringRef libName) const { - SmallString<256> fullPath; - if (libName.endswith(".o")) { - // A request ending in .o is special: just search for the file directly. - fullPath.assign(path); - llvm::sys::path::append(fullPath, libName); - if (fileExists(fullPath)) - return fullPath.str().copy(_allocator); - return llvm::None; - } - - // Search for stub library - fullPath.assign(path); - llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd"); - if (fileExists(fullPath)) - return fullPath.str().copy(_allocator); - - // Search for dynamic library - fullPath.assign(path); - llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib"); - if (fileExists(fullPath)) - return fullPath.str().copy(_allocator); - - // If not, try for a static library - fullPath.assign(path); - llvm::sys::path::append(fullPath, Twine("lib") + libName + ".a"); - if (fileExists(fullPath)) - return fullPath.str().copy(_allocator); - - return llvm::None; -} - -llvm::Optional<StringRef> -MachOLinkingContext::searchLibrary(StringRef libName) const { - SmallString<256> path; - for (StringRef dir : searchDirs()) { - llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName); - if (searchDir) - return searchDir; - } - - return llvm::None; -} - -llvm::Optional<StringRef> -MachOLinkingContext::findPathForFramework(StringRef fwName) const{ - SmallString<256> fullPath; - for (StringRef dir : frameworkDirs()) { - fullPath.assign(dir); - llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName); - if (fileExists(fullPath)) - return fullPath.str().copy(_allocator); - } - - return llvm::None; -} - -bool MachOLinkingContext::validateImpl() { - // TODO: if -arch not specified, look at arch of first .o file. - - if (_currentVersion && _outputMachOType != MH_DYLIB) { - error("-current_version can only be used with dylibs"); - return false; - } - - if (_compatibilityVersion && _outputMachOType != MH_DYLIB) { - error("-compatibility_version can only be used with dylibs"); - return false; - } - - if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) { - error("-mark_dead_strippable_dylib can only be used with dylibs"); - return false; - } - - if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) { - error("-bundle_loader can only be used with Mach-O bundles"); - return false; - } - - // If -exported_symbols_list used, all exported symbols must be defined. - if (_exportMode == ExportMode::exported) { - for (const auto &symbol : _exportedSymbols) - addInitialUndefinedSymbol(symbol.getKey()); - } - - // If -dead_strip, set up initial live symbols. - if (deadStrip()) { - // Entry point is live. - if (outputTypeHasEntry()) - addDeadStripRoot(entrySymbolName()); - // Lazy binding helper is live. - if (needsStubsPass()) - addDeadStripRoot(binderSymbolName()); - // If using -exported_symbols_list, make all exported symbols live. - if (_exportMode == ExportMode::exported) { - setGlobalsAreDeadStripRoots(false); - for (const auto &symbol : _exportedSymbols) - addDeadStripRoot(symbol.getKey()); - } - } - - addOutputFileDependency(outputPath()); - - return true; -} - -void MachOLinkingContext::addPasses(PassManager &pm) { - // objc pass should be before layout pass. Otherwise test cases may contain - // no atoms which confuses the layout pass. - if (needsObjCPass()) - mach_o::addObjCPass(pm, *this); - mach_o::addLayoutPass(pm, *this); - if (needsStubsPass()) - mach_o::addStubsPass(pm, *this); - if (needsCompactUnwindPass()) - mach_o::addCompactUnwindPass(pm, *this); - if (needsGOTPass()) - mach_o::addGOTPass(pm, *this); - if (needsTLVPass()) - mach_o::addTLVPass(pm, *this); - if (needsShimPass()) - mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass. -} - -Writer &MachOLinkingContext::writer() const { - if (!_writer) - _writer = createWriterMachO(*this); - return *_writer; -} - -ErrorOr<std::unique_ptr<MemoryBuffer>> -MachOLinkingContext::getMemoryBuffer(StringRef path) { - addInputFileDependency(path); - - ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = - MemoryBuffer::getFileOrSTDIN(path); - if (std::error_code ec = mbOrErr.getError()) - return ec; - std::unique_ptr<MemoryBuffer> mb = std::move(mbOrErr.get()); - - // If buffer contains a fat file, find required arch in fat buffer - // and switch buffer to point to just that required slice. - uint32_t offset; - uint32_t size; - if (sliceFromFatFile(mb->getMemBufferRef(), offset, size)) - return MemoryBuffer::getFileSlice(path, size, offset); - return std::move(mb); -} - -MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) { - ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = getMemoryBuffer(path); - if (mbOrErr.getError()) - return nullptr; - - ErrorOr<std::unique_ptr<File>> fileOrErr = - registry().loadFile(std::move(mbOrErr.get())); - if (!fileOrErr) - return nullptr; - std::unique_ptr<File> &file = fileOrErr.get(); - file->parse(); - MachODylibFile *result = reinterpret_cast<MachODylibFile *>(file.get()); - // Node object now owned by _indirectDylibs vector. - _indirectDylibs.push_back(std::move(file)); - return result; -} - -MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) { - // See if already loaded. - auto pos = _pathToDylibMap.find(path); - if (pos != _pathToDylibMap.end()) - return pos->second; - - // Search -L paths if of the form "libXXX.dylib" - std::pair<StringRef, StringRef> split = path.rsplit('/'); - StringRef leafName = split.second; - if (leafName.startswith("lib") && leafName.endswith(".dylib")) { - // FIXME: Need to enhance searchLibrary() to only look for .dylib - auto libPath = searchLibrary(leafName); - if (libPath) - return loadIndirectDylib(libPath.getValue()); - } - - // Try full path with sysroot. - for (StringRef sysPath : _syslibRoots) { - SmallString<256> fullPath; - fullPath.assign(sysPath); - llvm::sys::path::append(fullPath, path); - if (pathExists(fullPath)) - return loadIndirectDylib(fullPath); - } - - // Try full path. - if (pathExists(path)) { - return loadIndirectDylib(path); - } - - return nullptr; -} - -uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const { - auto pos = _pathToDylibMap.find(installName); - if (pos != _pathToDylibMap.end()) - return pos->second->currentVersion(); - else - return 0x10000; // 1.0 -} - -uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const { - auto pos = _pathToDylibMap.find(installName); - if (pos != _pathToDylibMap.end()) - return pos->second->compatVersion(); - else - return 0x10000; // 1.0 -} - -void MachOLinkingContext::createImplicitFiles( - std::vector<std::unique_ptr<File> > &result) { - // Add indirect dylibs by asking each linked dylib to add its indirects. - // Iterate until no more dylibs get loaded. - size_t dylibCount = 0; - while (dylibCount != _allDylibs.size()) { - dylibCount = _allDylibs.size(); - for (MachODylibFile *dylib : _allDylibs) { - dylib->loadReExportedDylibs([this] (StringRef path) -> MachODylibFile* { - return findIndirectDylib(path); }); - } - } - - // Let writer add output type specific extras. - writer().createImplicitFiles(result); - - // If undefinedMode is != error, add a FlatNamespaceFile instance. This will - // provide a SharedLibraryAtom for symbols that aren't defined elsewhere. - if (undefinedMode() != UndefinedMode::error) { - result.emplace_back(new mach_o::FlatNamespaceFile(*this)); - _flatNamespaceFile = result.back().get(); - } -} - -void MachOLinkingContext::registerDylib(MachODylibFile *dylib, - bool upward) const { - std::lock_guard<std::mutex> lock(_dylibsMutex); - - if (!llvm::count(_allDylibs, dylib)) - _allDylibs.push_back(dylib); - _pathToDylibMap[dylib->installName()] = dylib; - // If path is different than install name, register path too. - if (!dylib->path().equals(dylib->installName())) - _pathToDylibMap[dylib->path()] = dylib; - if (upward) - _upwardDylibs.insert(dylib); -} - -bool MachOLinkingContext::isUpwardDylib(StringRef installName) const { - for (MachODylibFile *dylib : _upwardDylibs) { - if (dylib->installName().equals(installName)) - return true; - } - return false; -} - -ArchHandler &MachOLinkingContext::archHandler() const { - if (!_archHandler) - _archHandler = ArchHandler::create(_arch); - return *_archHandler; -} - -void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect, - uint16_t align) { - SectionAlign entry = { seg, sect, align }; - _sectAligns.push_back(entry); -} - -void MachOLinkingContext::addSectCreateSection( - StringRef seg, StringRef sect, - std::unique_ptr<MemoryBuffer> content) { - - if (!_sectCreateFile) { - auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>(); - _sectCreateFile = sectCreateFile.get(); - getNodes().push_back(std::make_unique<FileNode>(std::move(sectCreateFile))); - } - - assert(_sectCreateFile && "sectcreate file does not exist."); - _sectCreateFile->addSection(seg, sect, std::move(content)); -} - -bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect, - uint16_t &align) const { - for (const SectionAlign &entry : _sectAligns) { - if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) { - align = entry.align; - return true; - } - } - return false; -} - -void MachOLinkingContext::addExportSymbol(StringRef sym) { - // Support old crufty export lists with bogus entries. - if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) { - llvm::errs() << "warning: ignoring " << sym << " in export list\n"; - return; - } - // Only i386 MacOSX uses old ABI, so don't change those. - if ((_os != OS::macOSX) || (_arch != arch_x86)) { - // ObjC has two different ABIs. Be nice and allow one export list work for - // both ABIs by renaming symbols. - if (sym.startswith(".objc_class_name_")) { - std::string abi2className("_OBJC_CLASS_$_"); - abi2className += sym.substr(17); - _exportedSymbols.insert(copy(abi2className)); - std::string abi2metaclassName("_OBJC_METACLASS_$_"); - abi2metaclassName += sym.substr(17); - _exportedSymbols.insert(copy(abi2metaclassName)); - return; - } - } - - // FIXME: Support wildcards. - _exportedSymbols.insert(sym); -} - -bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const { - switch (_exportMode) { - case ExportMode::globals: - llvm_unreachable("exportSymbolNamed() should not be called in this mode"); - break; - case ExportMode::exported: - return _exportedSymbols.count(sym); - case ExportMode::unexported: - return !_exportedSymbols.count(sym); - } - llvm_unreachable("_exportMode unknown enum value"); -} - -std::string MachOLinkingContext::demangle(StringRef symbolName) const { - // Only try to demangle symbols if -demangle on command line - if (!demangleSymbols()) - return std::string(symbolName); - - // Only try to demangle symbols that look like C++ symbols - if (!symbolName.startswith("__Z")) - return std::string(symbolName); - - SmallString<256> symBuff; - StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff); - // Mach-O has extra leading underscore that needs to be removed. - const char *cstr = nullTermSym.data() + 1; - int status; - char *demangled = llvm::itaniumDemangle(cstr, nullptr, nullptr, &status); - if (demangled) { - std::string result(demangled); - // __cxa_demangle() always uses a malloc'ed buffer to return the result. - free(demangled); - return result; - } - - return std::string(symbolName); -} - -static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo, - char Opcode, StringRef Path) { - if (!DepInfo) - return; - - *DepInfo << Opcode; - *DepInfo << Path; - *DepInfo << '\0'; -} - -std::error_code MachOLinkingContext::createDependencyFile(StringRef path) { - std::error_code ec; - _dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>( - new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None)); - if (ec) { - _dependencyInfo.reset(); - return ec; - } - - addDependencyInfoHelper(_dependencyInfo.get(), 0x00, "lld" /*FIXME*/); - return std::error_code(); -} - -void MachOLinkingContext::addInputFileDependency(StringRef path) const { - addDependencyInfoHelper(_dependencyInfo.get(), 0x10, path); -} - -void MachOLinkingContext::addInputFileNotFound(StringRef path) const { - addDependencyInfoHelper(_dependencyInfo.get(), 0x11, path); -} - -void MachOLinkingContext::addOutputFileDependency(StringRef path) const { - addDependencyInfoHelper(_dependencyInfo.get(), 0x40, path); -} - -void MachOLinkingContext::appendOrderedSymbol(StringRef symbol, - StringRef filename) { - // To support sorting static functions which may have the same name in - // multiple .o files, _orderFiles maps the symbol name to a vector - // of OrderFileNode each of which can specify a file prefix. - OrderFileNode info; - if (!filename.empty()) - info.fileFilter = copy(filename); - info.order = _orderFileEntries++; - _orderFiles[symbol].push_back(info); -} - -bool -MachOLinkingContext::findOrderOrdinal(const std::vector<OrderFileNode> &nodes, - const DefinedAtom *atom, - unsigned &ordinal) { - const File *objFile = &atom->file(); - assert(objFile); - StringRef objName = objFile->path(); - std::pair<StringRef, StringRef> dirAndLeaf = objName.rsplit('/'); - if (!dirAndLeaf.second.empty()) - objName = dirAndLeaf.second; - for (const OrderFileNode &info : nodes) { - if (info.fileFilter.empty()) { - // Have unprefixed symbol name in order file that matches this atom. - ordinal = info.order; - return true; - } - if (info.fileFilter.equals(objName)) { - // Have prefixed symbol name in order file that matches atom's path. - ordinal = info.order; - return true; - } - } - return false; -} - -bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left, - const DefinedAtom *right, - bool &leftBeforeRight) const { - // No custom sorting if no order file entries. - if (!_orderFileEntries) - return false; - - // Order files can only order named atoms. - StringRef leftName = left->name(); - StringRef rightName = right->name(); - if (leftName.empty() || rightName.empty()) - return false; - - // If neither is in order file list, no custom sorter. - auto leftPos = _orderFiles.find(leftName); - auto rightPos = _orderFiles.find(rightName); - bool leftIsOrdered = (leftPos != _orderFiles.end()); - bool rightIsOrdered = (rightPos != _orderFiles.end()); - if (!leftIsOrdered && !rightIsOrdered) - return false; - - // There could be multiple symbols with same name but different file prefixes. - unsigned leftOrder; - unsigned rightOrder; - bool foundLeft = - leftIsOrdered && findOrderOrdinal(leftPos->getValue(), left, leftOrder); - bool foundRight = rightIsOrdered && - findOrderOrdinal(rightPos->getValue(), right, rightOrder); - if (!foundLeft && !foundRight) - return false; - - // If only one is in order file list, ordered one goes first. - if (foundLeft != foundRight) - leftBeforeRight = foundLeft; - else - leftBeforeRight = (leftOrder < rightOrder); - - return true; -} - -static bool isLibrary(const std::unique_ptr<Node> &elem) { - if (FileNode *node = dyn_cast<FileNode>(const_cast<Node *>(elem.get()))) { - File *file = node->getFile(); - return isa<SharedLibraryFile>(file) || isa<ArchiveLibraryFile>(file); - } - return false; -} - -// The darwin linker processes input files in two phases. The first phase -// links in all object (.o) files in command line order. The second phase -// links in libraries in command line order. -// In this function we reorder the input files so that all the object files -// comes before any library file. We also make a group for the library files -// so that the Resolver will reiterate over the libraries as long as we find -// new undefines from libraries. -void MachOLinkingContext::finalizeInputFiles() { - std::vector<std::unique_ptr<Node>> &elements = getNodes(); - llvm::stable_sort(elements, [](const std::unique_ptr<Node> &a, - const std::unique_ptr<Node> &b) { - return !isLibrary(a) && isLibrary(b); - }); - size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary); - elements.push_back(std::make_unique<GroupEnd>(numLibs)); -} - -llvm::Error MachOLinkingContext::handleLoadedFile(File &file) { - auto *machoFile = dyn_cast<MachOFile>(&file); - if (!machoFile) - return llvm::Error::success(); - - // Check that the arch of the context matches that of the file. - // Also set the arch of the context if it didn't have one. - if (_arch == arch_unknown) { - _arch = machoFile->arch(); - } else if (machoFile->arch() != arch_unknown && machoFile->arch() != _arch) { - // Archs are different. - return llvm::make_error<GenericError>(file.path() + - Twine(" cannot be linked due to incompatible architecture")); - } - - // Check that the OS of the context matches that of the file. - // Also set the OS of the context if it didn't have one. - if (_os == OS::unknown) { - _os = machoFile->OS(); - } else if (machoFile->OS() != OS::unknown && machoFile->OS() != _os) { - // OSes are different. - return llvm::make_error<GenericError>(file.path() + - Twine(" cannot be linked due to incompatible operating systems")); - } - - // Check that if the objc info exists, that it is compatible with the target - // OS. - switch (machoFile->objcConstraint()) { - case objc_unknown: - // The file is not compiled with objc, so skip the checks. - break; - case objc_gc_only: - case objc_supports_gc: - llvm_unreachable("GC support should already have thrown an error"); - case objc_retainReleaseForSimulator: - // The file is built with simulator objc, so make sure that the context - // is also building with simulator support. - if (_os != OS::iOS_simulator) - return llvm::make_error<GenericError>(file.path() + - Twine(" cannot be linked. It contains ObjC built for the simulator" - " while we are linking a non-simulator target")); - assert((_objcConstraint == objc_unknown || - _objcConstraint == objc_retainReleaseForSimulator) && - "Must be linking with retain/release for the simulator"); - _objcConstraint = objc_retainReleaseForSimulator; - break; - case objc_retainRelease: - // The file is built without simulator objc, so make sure that the - // context is also building without simulator support. - if (_os == OS::iOS_simulator) - return llvm::make_error<GenericError>(file.path() + - Twine(" cannot be linked. It contains ObjC built for a non-simulator" - " target while we are linking a simulator target")); - assert((_objcConstraint == objc_unknown || - _objcConstraint == objc_retainRelease) && - "Must be linking with retain/release for a non-simulator target"); - _objcConstraint = objc_retainRelease; - break; - } - - // Check that the swift version of the context matches that of the file. - // Also set the swift version of the context if it didn't have one. - if (!_swiftVersion) { - _swiftVersion = machoFile->swiftVersion(); - } else if (machoFile->swiftVersion() && - machoFile->swiftVersion() != _swiftVersion) { - // Swift versions are different. - return llvm::make_error<GenericError>("different swift versions"); - } - - return llvm::Error::success(); -} - -} // end namespace lld diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h deleted file mode 100644 index 3ef2949addab..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp deleted file mode 100644 index 87601ca1be8b..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h deleted file mode 100644 index aeb04ef4508a..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp deleted file mode 100644 index 17b45b9ca827..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp +++ /dev/null @@ -1,1560 +0,0 @@ -//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.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 normalized -/// mach-o in memory to mach-o binary on disk. -/// -/// +---------------+ -/// | binary mach-o | -/// +---------------+ -/// ^ -/// | -/// | -/// +------------+ -/// | normalized | -/// +------------+ - -#include "MachONormalizedFile.h" -#include "MachONormalizedFileBinaryUtils.h" -#include "lld/Common/LLVM.h" -#include "lld/Core/Error.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/ilist.h" -#include "llvm/ADT/ilist_node.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include <functional> -#include <list> -#include <map> -#include <system_error> - -using namespace llvm::MachO; - -namespace lld { -namespace mach_o { -namespace normalized { - -struct TrieNode; // Forward declaration. - -struct TrieEdge : public llvm::ilist_node<TrieEdge> { - TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} - - StringRef _subString; - struct TrieNode *_child; -}; - -} // namespace normalized -} // namespace mach_o -} // namespace lld - - -namespace llvm { -using lld::mach_o::normalized::TrieEdge; -template <> -struct ilist_alloc_traits<TrieEdge> : ilist_noalloc_traits<TrieEdge> {}; -} // namespace llvm - - -namespace lld { -namespace mach_o { -namespace normalized { - -struct TrieNode { - typedef llvm::ilist<TrieEdge> TrieEdgeList; - - TrieNode(StringRef s) - : _cummulativeString(s), _address(0), _flags(0), _other(0), - _trieOffset(0), _hasExportInfo(false) {} - ~TrieNode() = default; - - void addSymbol(const Export &entry, BumpPtrAllocator &allocator, - std::vector<TrieNode *> &allNodes); - - void addOrderedNodes(const Export &entry, - std::vector<TrieNode *> &allNodes); - bool updateOffset(uint32_t &offset); - void appendToByteBuffer(ByteBuffer &out); - -private: - StringRef _cummulativeString; - TrieEdgeList _children; - uint64_t _address; - uint64_t _flags; - uint64_t _other; - StringRef _importedName; - uint32_t _trieOffset; - bool _hasExportInfo; - bool _ordered = false; -}; - -/// Utility class for writing a mach-o binary file given an in-memory -/// normalized file. -class MachOFileLayout { -public: - /// All layout computation is done in the constructor. - MachOFileLayout(const NormalizedFile &file, bool alwaysIncludeFunctionStarts); - - /// Returns the final file size as computed in the constructor. - size_t size() const; - - // Returns size of the mach_header and load commands. - size_t headerAndLoadCommandsSize() const; - - /// Writes the normalized file as a binary mach-o file to the specified - /// path. This does not have a stream interface because the generated - /// file may need the 'x' bit set. - llvm::Error writeBinary(StringRef path); - -private: - uint32_t loadCommandsSize(uint32_t &count, - bool alwaysIncludeFunctionStarts); - void buildFileOffsets(); - void writeMachHeader(); - llvm::Error writeLoadCommands(); - void writeSectionContent(); - void writeRelocations(); - void writeSymbolTable(); - void writeRebaseInfo(); - void writeBindingInfo(); - void writeLazyBindingInfo(); - void writeExportInfo(); - void writeFunctionStartsInfo(); - void writeDataInCodeInfo(); - void writeLinkEditContent(); - void buildLinkEditInfo(); - void buildRebaseInfo(); - void buildBindInfo(); - void buildLazyBindInfo(); - void buildExportTrie(); - void computeFunctionStartsSize(); - void computeDataInCodeSize(); - void computeSymbolTableSizes(); - void buildSectionRelocations(); - void appendSymbols(const std::vector<Symbol> &symbols, - uint32_t &symOffset, uint32_t &strOffset); - uint32_t indirectSymbolIndex(const Section §, uint32_t &index); - uint32_t indirectSymbolElementSize(const Section §); - - // For use as template parameter to load command methods. - struct MachO64Trait { - typedef llvm::MachO::segment_command_64 command; - typedef llvm::MachO::section_64 section; - enum { LC = llvm::MachO::LC_SEGMENT_64 }; - }; - - // For use as template parameter to load command methods. - struct MachO32Trait { - typedef llvm::MachO::segment_command command; - typedef llvm::MachO::section section; - enum { LC = llvm::MachO::LC_SEGMENT }; - }; - - template <typename T> - llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc); - template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc); - - uint32_t pointerAlign(uint32_t value); - static StringRef dyldPath(); - - struct SegExtraInfo { - uint32_t fileOffset; - uint32_t fileSize; - std::vector<const Section*> sections; - }; - typedef std::map<const Segment*, SegExtraInfo> SegMap; - struct SectionExtraInfo { - uint32_t fileOffset; - }; - typedef std::map<const Section*, SectionExtraInfo> SectionMap; - - const NormalizedFile &_file; - std::error_code _ec; - uint8_t *_buffer; - const bool _is64; - const bool _swap; - const bool _bigEndianArch; - uint64_t _seg1addr; - uint32_t _startOfLoadCommands; - uint32_t _countOfLoadCommands; - uint32_t _endOfLoadCommands; - uint32_t _startOfRelocations; - uint32_t _startOfFunctionStarts; - uint32_t _startOfDataInCode; - uint32_t _startOfSymbols; - uint32_t _startOfIndirectSymbols; - uint32_t _startOfSymbolStrings; - uint32_t _endOfSymbolStrings; - uint32_t _symbolTableLocalsStartIndex; - uint32_t _symbolTableGlobalsStartIndex; - uint32_t _symbolTableUndefinesStartIndex; - uint32_t _symbolStringPoolSize; - uint32_t _symbolTableSize; - uint32_t _functionStartsSize; - uint32_t _dataInCodeSize; - uint32_t _indirectSymbolTableCount; - // Used in object file creation only - uint32_t _startOfSectionsContent; - uint32_t _endOfSectionsContent; - // Used in final linked image only - uint32_t _startOfLinkEdit; - uint32_t _startOfRebaseInfo; - uint32_t _endOfRebaseInfo; - uint32_t _startOfBindingInfo; - uint32_t _endOfBindingInfo; - uint32_t _startOfLazyBindingInfo; - uint32_t _endOfLazyBindingInfo; - uint32_t _startOfExportTrie; - uint32_t _endOfExportTrie; - uint32_t _endOfLinkEdit; - uint64_t _addressOfLinkEdit; - SegMap _segInfo; - SectionMap _sectInfo; - ByteBuffer _rebaseInfo; - ByteBuffer _bindingInfo; - ByteBuffer _lazyBindingInfo; - ByteBuffer _weakBindingInfo; - ByteBuffer _exportTrie; -}; - -size_t headerAndLoadCommandsSize(const NormalizedFile &file, - bool includeFunctionStarts) { - MachOFileLayout layout(file, includeFunctionStarts); - return layout.headerAndLoadCommandsSize(); -} - -StringRef MachOFileLayout::dyldPath() { - return "/usr/lib/dyld"; -} - -uint32_t MachOFileLayout::pointerAlign(uint32_t value) { - return llvm::alignTo(value, _is64 ? 8 : 4); -} - - -size_t MachOFileLayout::headerAndLoadCommandsSize() const { - return _endOfLoadCommands; -} - -MachOFileLayout::MachOFileLayout(const NormalizedFile &file, - bool alwaysIncludeFunctionStarts) - : _file(file), - _is64(MachOLinkingContext::is64Bit(file.arch)), - _swap(!MachOLinkingContext::isHostEndian(file.arch)), - _bigEndianArch(MachOLinkingContext::isBigEndian(file.arch)), - _seg1addr(INT64_MAX) { - _startOfLoadCommands = _is64 ? sizeof(mach_header_64) : sizeof(mach_header); - const size_t segCommandBaseSize = - (_is64 ? sizeof(segment_command_64) : sizeof(segment_command)); - const size_t sectsSize = (_is64 ? sizeof(section_64) : sizeof(section)); - if (file.fileType == llvm::MachO::MH_OBJECT) { - // object files have just one segment load command containing all sections - _endOfLoadCommands = _startOfLoadCommands - + segCommandBaseSize - + file.sections.size() * sectsSize - + sizeof(symtab_command); - _countOfLoadCommands = 2; - if (file.hasMinVersionLoadCommand) { - _endOfLoadCommands += sizeof(version_min_command); - _countOfLoadCommands++; - } - if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) { - _endOfLoadCommands += sizeof(linkedit_data_command); - _countOfLoadCommands++; - } - if (_file.generateDataInCodeLoadCommand) { - _endOfLoadCommands += sizeof(linkedit_data_command); - _countOfLoadCommands++; - } - // Assign file offsets to each section. - _startOfSectionsContent = _endOfLoadCommands; - unsigned relocCount = 0; - uint64_t offset = _startOfSectionsContent; - for (const Section § : file.sections) { - if (isZeroFillSection(sect.type)) - _sectInfo[§].fileOffset = 0; - else { - offset = llvm::alignTo(offset, sect.alignment); - _sectInfo[§].fileOffset = offset; - offset += sect.content.size(); - } - relocCount += sect.relocations.size(); - } - _endOfSectionsContent = offset; - - computeSymbolTableSizes(); - computeFunctionStartsSize(); - computeDataInCodeSize(); - - // Align start of relocations. - _startOfRelocations = pointerAlign(_endOfSectionsContent); - _startOfFunctionStarts = _startOfRelocations + relocCount * 8; - _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; - _startOfSymbols = _startOfDataInCode + _dataInCodeSize; - // Add Indirect symbol table. - _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; - // Align start of symbol table and symbol strings. - _startOfSymbolStrings = _startOfIndirectSymbols - + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t)); - _endOfSymbolStrings = _startOfSymbolStrings - + pointerAlign(_symbolStringPoolSize); - _endOfLinkEdit = _endOfSymbolStrings; - DEBUG_WITH_TYPE("MachOFileLayout", - llvm::dbgs() << "MachOFileLayout()\n" - << " startOfLoadCommands=" << _startOfLoadCommands << "\n" - << " countOfLoadCommands=" << _countOfLoadCommands << "\n" - << " endOfLoadCommands=" << _endOfLoadCommands << "\n" - << " startOfRelocations=" << _startOfRelocations << "\n" - << " startOfSymbols=" << _startOfSymbols << "\n" - << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" - << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n" - << " startOfSectionsContent=" << _startOfSectionsContent << "\n" - << " endOfSectionsContent=" << _endOfSectionsContent << "\n"); - } else { - // Final linked images have one load command per segment. - _endOfLoadCommands = _startOfLoadCommands - + loadCommandsSize(_countOfLoadCommands, - alwaysIncludeFunctionStarts); - - // Assign section file offsets. - buildFileOffsets(); - buildLinkEditInfo(); - - // LINKEDIT of final linked images has in order: - // rebase info, binding info, lazy binding info, weak binding info, - // data-in-code, symbol table, indirect symbol table, symbol table strings. - _startOfRebaseInfo = _startOfLinkEdit; - _endOfRebaseInfo = _startOfRebaseInfo + _rebaseInfo.size(); - _startOfBindingInfo = _endOfRebaseInfo; - _endOfBindingInfo = _startOfBindingInfo + _bindingInfo.size(); - _startOfLazyBindingInfo = _endOfBindingInfo; - _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size(); - _startOfExportTrie = _endOfLazyBindingInfo; - _endOfExportTrie = _startOfExportTrie + _exportTrie.size(); - _startOfFunctionStarts = _endOfExportTrie; - _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; - _startOfSymbols = _startOfDataInCode + _dataInCodeSize; - _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; - _startOfSymbolStrings = _startOfIndirectSymbols - + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t)); - _endOfSymbolStrings = _startOfSymbolStrings - + pointerAlign(_symbolStringPoolSize); - _endOfLinkEdit = _endOfSymbolStrings; - DEBUG_WITH_TYPE("MachOFileLayout", - llvm::dbgs() << "MachOFileLayout()\n" - << " startOfLoadCommands=" << _startOfLoadCommands << "\n" - << " countOfLoadCommands=" << _countOfLoadCommands << "\n" - << " endOfLoadCommands=" << _endOfLoadCommands << "\n" - << " startOfLinkEdit=" << _startOfLinkEdit << "\n" - << " startOfRebaseInfo=" << _startOfRebaseInfo << "\n" - << " endOfRebaseInfo=" << _endOfRebaseInfo << "\n" - << " startOfBindingInfo=" << _startOfBindingInfo << "\n" - << " endOfBindingInfo=" << _endOfBindingInfo << "\n" - << " startOfLazyBindingInfo=" << _startOfLazyBindingInfo << "\n" - << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n" - << " startOfExportTrie=" << _startOfExportTrie << "\n" - << " endOfExportTrie=" << _endOfExportTrie << "\n" - << " startOfFunctionStarts=" << _startOfFunctionStarts << "\n" - << " startOfDataInCode=" << _startOfDataInCode << "\n" - << " startOfSymbols=" << _startOfSymbols << "\n" - << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" - << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n" - << " addressOfLinkEdit=" << _addressOfLinkEdit << "\n"); - } -} - -uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count, - bool alwaysIncludeFunctionStarts) { - uint32_t size = 0; - count = 0; - - const size_t segCommandSize = - (_is64 ? sizeof(segment_command_64) : sizeof(segment_command)); - const size_t sectionSize = (_is64 ? sizeof(section_64) : sizeof(section)); - - // Add LC_SEGMENT for each segment. - size += _file.segments.size() * segCommandSize; - count += _file.segments.size(); - // Add section record for each section. - size += _file.sections.size() * sectionSize; - - // If creating a dylib, add LC_ID_DYLIB. - if (_file.fileType == llvm::MachO::MH_DYLIB) { - size += sizeof(dylib_command) + pointerAlign(_file.installName.size() + 1); - ++count; - } - - // Add LC_DYLD_INFO - size += sizeof(dyld_info_command); - ++count; - - // Add LC_SYMTAB - size += sizeof(symtab_command); - ++count; - - // Add LC_DYSYMTAB - if (_file.fileType != llvm::MachO::MH_PRELOAD) { - size += sizeof(dysymtab_command); - ++count; - } - - // If main executable add LC_LOAD_DYLINKER - if (_file.fileType == llvm::MachO::MH_EXECUTE) { - size += pointerAlign(sizeof(dylinker_command) + dyldPath().size()+1); - ++count; - } - - // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, - // LC_VERSION_MIN_TVOS - if (_file.hasMinVersionLoadCommand) { - size += sizeof(version_min_command); - ++count; - } - - // Add LC_SOURCE_VERSION - size += sizeof(source_version_command); - ++count; - - // If main executable add LC_MAIN - if (_file.fileType == llvm::MachO::MH_EXECUTE) { - size += sizeof(entry_point_command); - ++count; - } - - // Add LC_LOAD_DYLIB for each dependent dylib. - for (const DependentDylib &dep : _file.dependentDylibs) { - size += sizeof(dylib_command) + pointerAlign(dep.path.size()+1); - ++count; - } - - // Add LC_RPATH - for (const StringRef &path : _file.rpaths) { - size += pointerAlign(sizeof(rpath_command) + path.size() + 1); - ++count; - } - - // Add LC_FUNCTION_STARTS if needed - if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) { - size += sizeof(linkedit_data_command); - ++count; - } - - // Add LC_DATA_IN_CODE if requested. Note, we do encode zero length entries. - // FIXME: Zero length entries is only to match ld64. Should we change this? - if (_file.generateDataInCodeLoadCommand) { - size += sizeof(linkedit_data_command); - ++count; - } - - return size; -} - -static bool overlaps(const Segment &s1, const Segment &s2) { - if (s2.address >= s1.address+s1.size) - return false; - if (s1.address >= s2.address+s2.size) - return false; - return true; -} - -static bool overlaps(const Section &s1, const Section &s2) { - if (s2.address >= s1.address+s1.content.size()) - return false; - if (s1.address >= s2.address+s2.content.size()) - return false; - return true; -} - -void MachOFileLayout::buildFileOffsets() { - // Verify no segments overlap - for (const Segment &sg1 : _file.segments) { - for (const Segment &sg2 : _file.segments) { - if (&sg1 == &sg2) - continue; - if (overlaps(sg1,sg2)) { - _ec = make_error_code(llvm::errc::executable_format_error); - return; - } - } - } - - // Verify no sections overlap - for (const Section &s1 : _file.sections) { - for (const Section &s2 : _file.sections) { - if (&s1 == &s2) - continue; - if (overlaps(s1,s2)) { - _ec = make_error_code(llvm::errc::executable_format_error); - return; - } - } - } - - // Build side table of extra info about segments and sections. - SegExtraInfo t; - t.fileOffset = 0; - for (const Segment &sg : _file.segments) { - _segInfo[&sg] = t; - } - SectionExtraInfo t2; - t2.fileOffset = 0; - // Assign sections to segments. - for (const Section &s : _file.sections) { - _sectInfo[&s] = t2; - bool foundSegment = false; - for (const Segment &sg : _file.segments) { - if (sg.name.equals(s.segmentName)) { - if ((s.address >= sg.address) - && (s.address+s.content.size() <= sg.address+sg.size)) { - _segInfo[&sg].sections.push_back(&s); - foundSegment = true; - break; - } - } - } - if (!foundSegment) { - _ec = make_error_code(llvm::errc::executable_format_error); - return; - } - } - - // Assign file offsets. - uint32_t fileOffset = 0; - DEBUG_WITH_TYPE("MachOFileLayout", - llvm::dbgs() << "buildFileOffsets()\n"); - for (const Segment &sg : _file.segments) { - _segInfo[&sg].fileOffset = fileOffset; - if ((_seg1addr == INT64_MAX) && sg.init_access) - _seg1addr = sg.address; - DEBUG_WITH_TYPE("MachOFileLayout", - llvm::dbgs() << " segment=" << sg.name - << ", fileOffset=" << _segInfo[&sg].fileOffset << "\n"); - - uint32_t segFileSize = 0; - // A segment that is not zero-fill must use a least one page of disk space. - if (sg.init_access) - segFileSize = _file.pageSize; - for (const Section *s : _segInfo[&sg].sections) { - uint32_t sectOffset = s->address - sg.address; - uint32_t sectFileSize = - isZeroFillSection(s->type) ? 0 : s->content.size(); - segFileSize = std::max(segFileSize, sectOffset + sectFileSize); - - _sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset; - DEBUG_WITH_TYPE("MachOFileLayout", - llvm::dbgs() << " section=" << s->sectionName - << ", fileOffset=" << fileOffset << "\n"); - } - - // round up all segments to page aligned, except __LINKEDIT - if (!sg.name.equals("__LINKEDIT")) { - _segInfo[&sg].fileSize = llvm::alignTo(segFileSize, _file.pageSize); - fileOffset = llvm::alignTo(fileOffset + segFileSize, _file.pageSize); - } - _addressOfLinkEdit = sg.address + sg.size; - } - _startOfLinkEdit = fileOffset; -} - -size_t MachOFileLayout::size() const { - return _endOfSymbolStrings; -} - -void MachOFileLayout::writeMachHeader() { - auto cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch); - // dynamic x86 executables on newer OS version should also set the - // CPU_SUBTYPE_LIB64 mask in the CPU subtype. - // FIXME: Check that this is a dynamic executable, not a static one. - if (_file.fileType == llvm::MachO::MH_EXECUTE && - cpusubtype == CPU_SUBTYPE_X86_64_ALL && - _file.os == MachOLinkingContext::OS::macOSX) { - uint32_t version; - bool failed = MachOLinkingContext::parsePackedVersion("10.5", version); - if (!failed && _file.minOSverson >= version) - cpusubtype |= CPU_SUBTYPE_LIB64; - } - - mach_header *mh = reinterpret_cast<mach_header*>(_buffer); - mh->magic = _is64 ? llvm::MachO::MH_MAGIC_64 : llvm::MachO::MH_MAGIC; - mh->cputype = MachOLinkingContext::cpuTypeFromArch(_file.arch); - mh->cpusubtype = cpusubtype; - mh->filetype = _file.fileType; - mh->ncmds = _countOfLoadCommands; - mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands; - mh->flags = _file.flags; - if (_swap) - swapStruct(*mh); -} - -uint32_t MachOFileLayout::indirectSymbolIndex(const Section §, - uint32_t &index) { - if (sect.indirectSymbols.empty()) - return 0; - uint32_t result = index; - index += sect.indirectSymbols.size(); - return result; -} - -uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) { - if (sect.indirectSymbols.empty()) - return 0; - if (sect.type != S_SYMBOL_STUBS) - return 0; - return sect.content.size() / sect.indirectSymbols.size(); -} - -template <typename T> -llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { - typename T::command* seg = reinterpret_cast<typename T::command*>(lc); - seg->cmd = T::LC; - seg->cmdsize = sizeof(typename T::command) - + _file.sections.size() * sizeof(typename T::section); - uint8_t *next = lc + seg->cmdsize; - memset(seg->segname, 0, 16); - seg->flags = 0; - seg->vmaddr = 0; - seg->fileoff = _endOfLoadCommands; - seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; - seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; - seg->nsects = _file.sections.size(); - if (seg->nsects) { - seg->vmsize = _file.sections.back().address - + _file.sections.back().content.size(); - seg->filesize = _sectInfo[&_file.sections.back()].fileOffset + - _file.sections.back().content.size() - - _sectInfo[&_file.sections.front()].fileOffset; - } - if (_swap) - swapStruct(*seg); - typename T::section *sout = reinterpret_cast<typename T::section*> - (lc+sizeof(typename T::command)); - uint32_t relOffset = _startOfRelocations; - uint32_t indirectSymRunningIndex = 0; - for (const Section &sin : _file.sections) { - setString16(sin.sectionName, sout->sectname); - setString16(sin.segmentName, sout->segname); - sout->addr = sin.address; - sout->size = sin.content.size(); - sout->offset = _sectInfo[&sin].fileOffset; - sout->align = llvm::Log2_32(sin.alignment); - sout->reloff = sin.relocations.empty() ? 0 : relOffset; - sout->nreloc = sin.relocations.size(); - sout->flags = sin.type | sin.attributes; - sout->reserved1 = indirectSymbolIndex(sin, indirectSymRunningIndex); - sout->reserved2 = indirectSymbolElementSize(sin); - relOffset += sin.relocations.size() * sizeof(any_relocation_info); - if (_swap) - swapStruct(*sout); - ++sout; - } - lc = next; - return llvm::Error::success(); -} - -template <typename T> -llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { - uint32_t indirectSymRunningIndex = 0; - for (const Segment &seg : _file.segments) { - // Link edit has no sections and a custom range of address, so handle it - // specially. - SegExtraInfo &segInfo = _segInfo[&seg]; - if (seg.name.equals("__LINKEDIT")) { - size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit; - typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); - cmd->cmd = T::LC; - cmd->cmdsize = sizeof(typename T::command); - uint8_t *next = lc + cmd->cmdsize; - setString16("__LINKEDIT", cmd->segname); - cmd->vmaddr = _addressOfLinkEdit; - cmd->vmsize = llvm::alignTo(linkeditSize, _file.pageSize); - cmd->fileoff = _startOfLinkEdit; - cmd->filesize = linkeditSize; - cmd->initprot = seg.init_access; - cmd->maxprot = seg.max_access; - cmd->nsects = 0; - cmd->flags = 0; - if (_swap) - swapStruct(*cmd); - lc = next; - continue; - } - // Write segment command with trailing sections. - typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); - cmd->cmd = T::LC; - cmd->cmdsize = sizeof(typename T::command) - + segInfo.sections.size() * sizeof(typename T::section); - uint8_t *next = lc + cmd->cmdsize; - setString16(seg.name, cmd->segname); - cmd->vmaddr = seg.address; - cmd->vmsize = seg.size; - cmd->fileoff = segInfo.fileOffset; - cmd->filesize = segInfo.fileSize; - cmd->initprot = seg.init_access; - cmd->maxprot = seg.max_access; - cmd->nsects = segInfo.sections.size(); - cmd->flags = 0; - if (_swap) - swapStruct(*cmd); - typename T::section *sect = reinterpret_cast<typename T::section*> - (lc+sizeof(typename T::command)); - for (const Section *section : segInfo.sections) { - setString16(section->sectionName, sect->sectname); - setString16(section->segmentName, sect->segname); - sect->addr = section->address; - sect->size = section->content.size(); - if (isZeroFillSection(section->type)) - sect->offset = 0; - else - sect->offset = section->address - seg.address + segInfo.fileOffset; - sect->align = llvm::Log2_32(section->alignment); - sect->reloff = 0; - sect->nreloc = 0; - sect->flags = section->type | section->attributes; - sect->reserved1 = indirectSymbolIndex(*section, indirectSymRunningIndex); - sect->reserved2 = indirectSymbolElementSize(*section); - if (_swap) - swapStruct(*sect); - ++sect; - } - lc = reinterpret_cast<uint8_t*>(next); - } - return llvm::Error::success(); -} - -static void writeVersionMinLoadCommand(const NormalizedFile &_file, - bool _swap, - uint8_t *&lc) { - if (!_file.hasMinVersionLoadCommand) - return; - version_min_command *vm = reinterpret_cast<version_min_command*>(lc); - switch (_file.os) { - case MachOLinkingContext::OS::unknown: - vm->cmd = _file.minOSVersionKind; - vm->cmdsize = sizeof(version_min_command); - vm->version = _file.minOSverson; - vm->sdk = 0; - break; - case MachOLinkingContext::OS::macOSX: - vm->cmd = LC_VERSION_MIN_MACOSX; - vm->cmdsize = sizeof(version_min_command); - vm->version = _file.minOSverson; - vm->sdk = _file.sdkVersion; - break; - case MachOLinkingContext::OS::iOS: - case MachOLinkingContext::OS::iOS_simulator: - vm->cmd = LC_VERSION_MIN_IPHONEOS; - vm->cmdsize = sizeof(version_min_command); - vm->version = _file.minOSverson; - vm->sdk = _file.sdkVersion; - break; - } - if (_swap) - swapStruct(*vm); - lc += sizeof(version_min_command); -} - -llvm::Error MachOFileLayout::writeLoadCommands() { - uint8_t *lc = &_buffer[_startOfLoadCommands]; - if (_file.fileType == llvm::MachO::MH_OBJECT) { - // Object files have one unnamed segment which holds all sections. - if (_is64) { - if (auto ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc)) - return ec; - } else { - if (auto ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc)) - return ec; - } - // Add LC_SYMTAB with symbol table info - symtab_command* st = reinterpret_cast<symtab_command*>(lc); - st->cmd = LC_SYMTAB; - st->cmdsize = sizeof(symtab_command); - st->symoff = _startOfSymbols; - st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() + - _file.globalSymbols.size() + _file.undefinedSymbols.size(); - st->stroff = _startOfSymbolStrings; - st->strsize = _endOfSymbolStrings - _startOfSymbolStrings; - if (_swap) - swapStruct(*st); - lc += sizeof(symtab_command); - - // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, - // LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS - writeVersionMinLoadCommand(_file, _swap, lc); - - // Add LC_FUNCTION_STARTS if needed. - if (_functionStartsSize != 0) { - linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); - dl->cmd = LC_FUNCTION_STARTS; - dl->cmdsize = sizeof(linkedit_data_command); - dl->dataoff = _startOfFunctionStarts; - dl->datasize = _functionStartsSize; - if (_swap) - swapStruct(*dl); - lc += sizeof(linkedit_data_command); - } - - // Add LC_DATA_IN_CODE if requested. - if (_file.generateDataInCodeLoadCommand) { - linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); - dl->cmd = LC_DATA_IN_CODE; - dl->cmdsize = sizeof(linkedit_data_command); - dl->dataoff = _startOfDataInCode; - dl->datasize = _dataInCodeSize; - if (_swap) - swapStruct(*dl); - lc += sizeof(linkedit_data_command); - } - } else { - // Final linked images have sections under segments. - if (_is64) { - if (auto ec = writeSegmentLoadCommands<MachO64Trait>(lc)) - return ec; - } else { - if (auto ec = writeSegmentLoadCommands<MachO32Trait>(lc)) - return ec; - } - - // Add LC_ID_DYLIB command for dynamic libraries. - if (_file.fileType == llvm::MachO::MH_DYLIB) { - dylib_command *dc = reinterpret_cast<dylib_command*>(lc); - StringRef path = _file.installName; - uint32_t size = sizeof(dylib_command) + pointerAlign(path.size() + 1); - dc->cmd = LC_ID_DYLIB; - dc->cmdsize = size; - dc->dylib.name = sizeof(dylib_command); // offset - // needs to be some constant value different than the one in LC_LOAD_DYLIB - dc->dylib.timestamp = 1; - dc->dylib.current_version = _file.currentVersion; - dc->dylib.compatibility_version = _file.compatVersion; - if (_swap) - swapStruct(*dc); - memcpy(lc + sizeof(dylib_command), path.begin(), path.size()); - lc[sizeof(dylib_command) + path.size()] = '\0'; - lc += size; - } - - // Add LC_DYLD_INFO_ONLY. - dyld_info_command* di = reinterpret_cast<dyld_info_command*>(lc); - di->cmd = LC_DYLD_INFO_ONLY; - di->cmdsize = sizeof(dyld_info_command); - di->rebase_off = _rebaseInfo.size() ? _startOfRebaseInfo : 0; - di->rebase_size = _rebaseInfo.size(); - di->bind_off = _bindingInfo.size() ? _startOfBindingInfo : 0; - di->bind_size = _bindingInfo.size(); - di->weak_bind_off = 0; - di->weak_bind_size = 0; - di->lazy_bind_off = _lazyBindingInfo.size() ? _startOfLazyBindingInfo : 0; - di->lazy_bind_size = _lazyBindingInfo.size(); - di->export_off = _exportTrie.size() ? _startOfExportTrie : 0; - di->export_size = _exportTrie.size(); - if (_swap) - swapStruct(*di); - lc += sizeof(dyld_info_command); - - // Add LC_SYMTAB with symbol table info. - symtab_command* st = reinterpret_cast<symtab_command*>(lc); - st->cmd = LC_SYMTAB; - st->cmdsize = sizeof(symtab_command); - st->symoff = _startOfSymbols; - st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() + - _file.globalSymbols.size() + _file.undefinedSymbols.size(); - st->stroff = _startOfSymbolStrings; - st->strsize = _endOfSymbolStrings - _startOfSymbolStrings; - if (_swap) - swapStruct(*st); - lc += sizeof(symtab_command); - - // Add LC_DYSYMTAB - if (_file.fileType != llvm::MachO::MH_PRELOAD) { - dysymtab_command* dst = reinterpret_cast<dysymtab_command*>(lc); - dst->cmd = LC_DYSYMTAB; - dst->cmdsize = sizeof(dysymtab_command); - dst->ilocalsym = _symbolTableLocalsStartIndex; - dst->nlocalsym = _file.stabsSymbols.size() + - _file.localSymbols.size(); - dst->iextdefsym = _symbolTableGlobalsStartIndex; - dst->nextdefsym = _file.globalSymbols.size(); - dst->iundefsym = _symbolTableUndefinesStartIndex; - dst->nundefsym = _file.undefinedSymbols.size(); - dst->tocoff = 0; - dst->ntoc = 0; - dst->modtaboff = 0; - dst->nmodtab = 0; - dst->extrefsymoff = 0; - dst->nextrefsyms = 0; - dst->indirectsymoff = _startOfIndirectSymbols; - dst->nindirectsyms = _indirectSymbolTableCount; - dst->extreloff = 0; - dst->nextrel = 0; - dst->locreloff = 0; - dst->nlocrel = 0; - if (_swap) - swapStruct(*dst); - lc += sizeof(dysymtab_command); - } - - // If main executable, add LC_LOAD_DYLINKER - if (_file.fileType == llvm::MachO::MH_EXECUTE) { - // Build LC_LOAD_DYLINKER load command. - uint32_t size=pointerAlign(sizeof(dylinker_command)+dyldPath().size()+1); - dylinker_command* dl = reinterpret_cast<dylinker_command*>(lc); - dl->cmd = LC_LOAD_DYLINKER; - dl->cmdsize = size; - dl->name = sizeof(dylinker_command); // offset - if (_swap) - swapStruct(*dl); - memcpy(lc+sizeof(dylinker_command), dyldPath().data(), dyldPath().size()); - lc[sizeof(dylinker_command)+dyldPath().size()] = '\0'; - lc += size; - } - - // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, - // LC_VERSION_MIN_TVOS - writeVersionMinLoadCommand(_file, _swap, lc); - - // Add LC_SOURCE_VERSION - { - // Note, using a temporary here to appease UB as we may not be aligned - // enough for a struct containing a uint64_t when emitting a 32-bit binary - source_version_command sv; - sv.cmd = LC_SOURCE_VERSION; - sv.cmdsize = sizeof(source_version_command); - sv.version = _file.sourceVersion; - if (_swap) - swapStruct(sv); - memcpy(lc, &sv, sizeof(source_version_command)); - lc += sizeof(source_version_command); - } - - // If main executable, add LC_MAIN. - if (_file.fileType == llvm::MachO::MH_EXECUTE) { - // Build LC_MAIN load command. - // Note, using a temporary here to appease UB as we may not be aligned - // enough for a struct containing a uint64_t when emitting a 32-bit binary - entry_point_command ep; - ep.cmd = LC_MAIN; - ep.cmdsize = sizeof(entry_point_command); - ep.entryoff = _file.entryAddress - _seg1addr; - ep.stacksize = _file.stackSize; - if (_swap) - swapStruct(ep); - memcpy(lc, &ep, sizeof(entry_point_command)); - lc += sizeof(entry_point_command); - } - - // Add LC_LOAD_DYLIB commands - for (const DependentDylib &dep : _file.dependentDylibs) { - dylib_command* dc = reinterpret_cast<dylib_command*>(lc); - uint32_t size = sizeof(dylib_command) + pointerAlign(dep.path.size()+1); - dc->cmd = dep.kind; - dc->cmdsize = size; - dc->dylib.name = sizeof(dylib_command); // offset - // needs to be some constant value different than the one in LC_ID_DYLIB - dc->dylib.timestamp = 2; - dc->dylib.current_version = dep.currentVersion; - dc->dylib.compatibility_version = dep.compatVersion; - if (_swap) - swapStruct(*dc); - memcpy(lc+sizeof(dylib_command), dep.path.begin(), dep.path.size()); - lc[sizeof(dylib_command)+dep.path.size()] = '\0'; - lc += size; - } - - // Add LC_RPATH - for (const StringRef &path : _file.rpaths) { - rpath_command *rpc = reinterpret_cast<rpath_command *>(lc); - uint32_t size = pointerAlign(sizeof(rpath_command) + path.size() + 1); - rpc->cmd = LC_RPATH; - rpc->cmdsize = size; - rpc->path = sizeof(rpath_command); // offset - if (_swap) - swapStruct(*rpc); - memcpy(lc+sizeof(rpath_command), path.begin(), path.size()); - lc[sizeof(rpath_command)+path.size()] = '\0'; - lc += size; - } - - // Add LC_FUNCTION_STARTS if needed. - if (_functionStartsSize != 0) { - linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); - dl->cmd = LC_FUNCTION_STARTS; - dl->cmdsize = sizeof(linkedit_data_command); - dl->dataoff = _startOfFunctionStarts; - dl->datasize = _functionStartsSize; - if (_swap) - swapStruct(*dl); - lc += sizeof(linkedit_data_command); - } - - // Add LC_DATA_IN_CODE if requested. - if (_file.generateDataInCodeLoadCommand) { - linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); - dl->cmd = LC_DATA_IN_CODE; - dl->cmdsize = sizeof(linkedit_data_command); - dl->dataoff = _startOfDataInCode; - dl->datasize = _dataInCodeSize; - if (_swap) - swapStruct(*dl); - lc += sizeof(linkedit_data_command); - } - } - assert(lc == &_buffer[_endOfLoadCommands]); - return llvm::Error::success(); -} - -void MachOFileLayout::writeSectionContent() { - for (const Section &s : _file.sections) { - // Copy all section content to output buffer. - if (isZeroFillSection(s.type)) - continue; - if (s.content.empty()) - continue; - uint32_t offset = _sectInfo[&s].fileOffset; - assert(offset >= _endOfLoadCommands); - uint8_t *p = &_buffer[offset]; - memcpy(p, &s.content[0], s.content.size()); - p += s.content.size(); - } -} - -void MachOFileLayout::writeRelocations() { - uint32_t relOffset = _startOfRelocations; - for (Section sect : _file.sections) { - for (Relocation r : sect.relocations) { - any_relocation_info* rb = reinterpret_cast<any_relocation_info*>( - &_buffer[relOffset]); - *rb = packRelocation(r, _swap, _bigEndianArch); - relOffset += sizeof(any_relocation_info); - } - } -} - -void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols, - uint32_t &symOffset, uint32_t &strOffset) { - for (const Symbol &sym : symbols) { - if (_is64) { - nlist_64* nb = reinterpret_cast<nlist_64*>(&_buffer[symOffset]); - nb->n_strx = strOffset - _startOfSymbolStrings; - nb->n_type = sym.type | sym.scope; - nb->n_sect = sym.sect; - nb->n_desc = sym.desc; - nb->n_value = sym.value; - if (_swap) - swapStruct(*nb); - symOffset += sizeof(nlist_64); - } else { - nlist* nb = reinterpret_cast<nlist*>(&_buffer[symOffset]); - nb->n_strx = strOffset - _startOfSymbolStrings; - nb->n_type = sym.type | sym.scope; - nb->n_sect = sym.sect; - nb->n_desc = sym.desc; - nb->n_value = sym.value; - if (_swap) - swapStruct(*nb); - symOffset += sizeof(nlist); - } - memcpy(&_buffer[strOffset], sym.name.begin(), sym.name.size()); - strOffset += sym.name.size(); - _buffer[strOffset++] ='\0'; // Strings in table have nul terminator. - } -} - -void MachOFileLayout::writeFunctionStartsInfo() { - if (!_functionStartsSize) - return; - memcpy(&_buffer[_startOfFunctionStarts], _file.functionStarts.data(), - _functionStartsSize); -} - -void MachOFileLayout::writeDataInCodeInfo() { - uint32_t offset = _startOfDataInCode; - for (const DataInCode &entry : _file.dataInCode) { - data_in_code_entry *dst = reinterpret_cast<data_in_code_entry*>( - &_buffer[offset]); - dst->offset = entry.offset; - dst->length = entry.length; - dst->kind = entry.kind; - if (_swap) - swapStruct(*dst); - offset += sizeof(data_in_code_entry); - } -} - -void MachOFileLayout::writeSymbolTable() { - // Write symbol table and symbol strings in parallel. - uint32_t symOffset = _startOfSymbols; - uint32_t strOffset = _startOfSymbolStrings; - // Reserve n_strx offset of zero to mean no name. - _buffer[strOffset++] = ' '; - _buffer[strOffset++] = '\0'; - appendSymbols(_file.stabsSymbols, symOffset, strOffset); - appendSymbols(_file.localSymbols, symOffset, strOffset); - appendSymbols(_file.globalSymbols, symOffset, strOffset); - appendSymbols(_file.undefinedSymbols, symOffset, strOffset); - // Write indirect symbol table array. - uint32_t *indirects = reinterpret_cast<uint32_t*> - (&_buffer[_startOfIndirectSymbols]); - if (_file.fileType == llvm::MachO::MH_OBJECT) { - // Object files have sections in same order as input normalized file. - for (const Section §ion : _file.sections) { - for (uint32_t index : section.indirectSymbols) { - if (_swap) - *indirects++ = llvm::sys::getSwappedBytes(index); - else - *indirects++ = index; - } - } - } else { - // Final linked images must sort sections from normalized file. - for (const Segment &seg : _file.segments) { - SegExtraInfo &segInfo = _segInfo[&seg]; - for (const Section *section : segInfo.sections) { - for (uint32_t index : section->indirectSymbols) { - if (_swap) - *indirects++ = llvm::sys::getSwappedBytes(index); - else - *indirects++ = index; - } - } - } - } -} - -void MachOFileLayout::writeRebaseInfo() { - memcpy(&_buffer[_startOfRebaseInfo], _rebaseInfo.bytes(), _rebaseInfo.size()); -} - -void MachOFileLayout::writeBindingInfo() { - memcpy(&_buffer[_startOfBindingInfo], - _bindingInfo.bytes(), _bindingInfo.size()); -} - -void MachOFileLayout::writeLazyBindingInfo() { - memcpy(&_buffer[_startOfLazyBindingInfo], - _lazyBindingInfo.bytes(), _lazyBindingInfo.size()); -} - -void MachOFileLayout::writeExportInfo() { - memcpy(&_buffer[_startOfExportTrie], _exportTrie.bytes(), _exportTrie.size()); -} - -void MachOFileLayout::buildLinkEditInfo() { - buildRebaseInfo(); - buildBindInfo(); - buildLazyBindInfo(); - buildExportTrie(); - computeSymbolTableSizes(); - computeFunctionStartsSize(); - computeDataInCodeSize(); -} - -void MachOFileLayout::buildSectionRelocations() { - -} - -void MachOFileLayout::buildRebaseInfo() { - // TODO: compress rebasing info. - for (const RebaseLocation& entry : _file.rebasingInfo) { - _rebaseInfo.append_byte(REBASE_OPCODE_SET_TYPE_IMM | entry.kind); - _rebaseInfo.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB - | entry.segIndex); - _rebaseInfo.append_uleb128(entry.segOffset); - _rebaseInfo.append_uleb128(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1); - } - _rebaseInfo.append_byte(REBASE_OPCODE_DONE); - _rebaseInfo.align(_is64 ? 8 : 4); -} - -void MachOFileLayout::buildBindInfo() { - // TODO: compress bind info. - uint64_t lastAddend = 0; - int lastOrdinal = 0x80000000; - StringRef lastSymbolName; - BindType lastType = (BindType)0; - Hex32 lastSegOffset = ~0U; - uint8_t lastSegIndex = (uint8_t)~0U; - for (const BindLocation& entry : _file.bindingInfo) { - if (entry.ordinal != lastOrdinal) { - if (entry.ordinal <= 0) - _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | - (entry.ordinal & BIND_IMMEDIATE_MASK)); - else if (entry.ordinal <= BIND_IMMEDIATE_MASK) - _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | - entry.ordinal); - else { - _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - _bindingInfo.append_uleb128(entry.ordinal); - } - lastOrdinal = entry.ordinal; - } - - if (lastSymbolName != entry.symbolName) { - _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); - _bindingInfo.append_string(entry.symbolName); - lastSymbolName = entry.symbolName; - } - - if (lastType != entry.kind) { - _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind); - lastType = entry.kind; - } - - if (lastSegIndex != entry.segIndex || lastSegOffset != entry.segOffset) { - _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB - | entry.segIndex); - _bindingInfo.append_uleb128(entry.segOffset); - lastSegIndex = entry.segIndex; - lastSegOffset = entry.segOffset; - } - if (entry.addend != lastAddend) { - _bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); - _bindingInfo.append_sleb128(entry.addend); - lastAddend = entry.addend; - } - _bindingInfo.append_byte(BIND_OPCODE_DO_BIND); - } - _bindingInfo.append_byte(BIND_OPCODE_DONE); - _bindingInfo.align(_is64 ? 8 : 4); -} - -void MachOFileLayout::buildLazyBindInfo() { - for (const BindLocation& entry : _file.lazyBindingInfo) { - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB - | entry.segIndex); - _lazyBindingInfo.append_uleb128(entry.segOffset); - if (entry.ordinal <= 0) - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | - (entry.ordinal & BIND_IMMEDIATE_MASK)); - else if (entry.ordinal <= BIND_IMMEDIATE_MASK) - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | - entry.ordinal); - else { - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - _lazyBindingInfo.append_uleb128(entry.ordinal); - } - // FIXME: We need to | the opcode here with flags. - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); - _lazyBindingInfo.append_string(entry.symbolName); - _lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND); - _lazyBindingInfo.append_byte(BIND_OPCODE_DONE); - } - _lazyBindingInfo.align(_is64 ? 8 : 4); -} - -void TrieNode::addSymbol(const Export& entry, - BumpPtrAllocator &allocator, - std::vector<TrieNode*> &allNodes) { - StringRef partialStr = entry.name.drop_front(_cummulativeString.size()); - for (TrieEdge &edge : _children) { - StringRef edgeStr = edge._subString; - if (partialStr.startswith(edgeStr)) { - // Already have matching edge, go down that path. - edge._child->addSymbol(entry, allocator, allNodes); - return; - } - // See if string has common prefix with existing edge. - for (int n=edgeStr.size()-1; n > 0; --n) { - if (partialStr.substr(0, n).equals(edgeStr.substr(0, n))) { - // Splice in new node: was A -> C, now A -> B -> C - StringRef bNodeStr = edge._child->_cummulativeString; - bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator); - auto *bNode = new (allocator) TrieNode(bNodeStr); - allNodes.push_back(bNode); - TrieNode* cNode = edge._child; - StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator); - StringRef bcEdgeStr = edgeStr.substr(n).copy(allocator); - DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() - << "splice in TrieNode('" << bNodeStr - << "') between edge '" - << abEdgeStr << "' and edge='" - << bcEdgeStr<< "'\n"); - TrieEdge& abEdge = edge; - abEdge._subString = abEdgeStr; - abEdge._child = bNode; - auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); - bNode->_children.insert(bNode->_children.end(), bcEdge); - bNode->addSymbol(entry, allocator, allNodes); - return; - } - } - } - if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { - assert(entry.otherOffset != 0); - } - if (entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { - assert(entry.otherOffset != 0); - } - // No commonality with any existing child, make a new edge. - auto *newNode = new (allocator) TrieNode(entry.name.copy(allocator)); - auto *newEdge = new (allocator) TrieEdge(partialStr, newNode); - _children.insert(_children.end(), newEdge); - DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() - << "new TrieNode('" << entry.name << "') with edge '" - << partialStr << "' from node='" - << _cummulativeString << "'\n"); - newNode->_address = entry.offset; - newNode->_flags = entry.flags | entry.kind; - newNode->_other = entry.otherOffset; - if ((entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && !entry.otherName.empty()) - newNode->_importedName = entry.otherName.copy(allocator); - newNode->_hasExportInfo = true; - allNodes.push_back(newNode); -} - -void TrieNode::addOrderedNodes(const Export& entry, - std::vector<TrieNode*> &orderedNodes) { - if (!_ordered) { - orderedNodes.push_back(this); - _ordered = true; - } - - StringRef partialStr = entry.name.drop_front(_cummulativeString.size()); - for (TrieEdge &edge : _children) { - StringRef edgeStr = edge._subString; - if (partialStr.startswith(edgeStr)) { - // Already have matching edge, go down that path. - edge._child->addOrderedNodes(entry, orderedNodes); - return; - } - } -} - -bool TrieNode::updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // Length when no export info - if (_hasExportInfo) { - if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { - nodeSize = llvm::getULEB128Size(_flags); - nodeSize += llvm::getULEB128Size(_other); // Other contains ordinal. - nodeSize += _importedName.size(); - ++nodeSize; // Trailing zero in imported name. - } else { - nodeSize = llvm::getULEB128Size(_flags) + llvm::getULEB128Size(_address); - if (_flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) - nodeSize += llvm::getULEB128Size(_other); - } - // Overall node size so far is uleb128 of export info + actual export info. - nodeSize += llvm::getULEB128Size(nodeSize); - } - // Compute size of all child edges. - ++nodeSize; // Byte for number of children. - for (TrieEdge &edge : _children) { - nodeSize += edge._subString.size() + 1 // String length. - + llvm::getULEB128Size(edge._child->_trieOffset); // Offset len. - } - // On input, 'offset' is new prefered location for this node. - bool result = (_trieOffset != offset); - // Store new location in node object for use by parents. - _trieOffset = offset; - // Update offset for next iteration. - offset += nodeSize; - // Return true if _trieOffset was changed. - return result; -} - -void TrieNode::appendToByteBuffer(ByteBuffer &out) { - if (_hasExportInfo) { - if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { - if (!_importedName.empty()) { - // nodes with re-export info: size, flags, ordinal, import-name - uint32_t nodeSize = llvm::getULEB128Size(_flags) - + llvm::getULEB128Size(_other) - + _importedName.size() + 1; - assert(nodeSize < 256); - out.append_byte(nodeSize); - out.append_uleb128(_flags); - out.append_uleb128(_other); - out.append_string(_importedName); - } else { - // nodes without re-export info: size, flags, ordinal, empty-string - uint32_t nodeSize = llvm::getULEB128Size(_flags) - + llvm::getULEB128Size(_other) + 1; - assert(nodeSize < 256); - out.append_byte(nodeSize); - out.append_uleb128(_flags); - out.append_uleb128(_other); - out.append_byte(0); - } - } else if ( _flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { - // Nodes with export info: size, flags, address, other - uint32_t nodeSize = llvm::getULEB128Size(_flags) - + llvm::getULEB128Size(_address) - + llvm::getULEB128Size(_other); - assert(nodeSize < 256); - out.append_byte(nodeSize); - out.append_uleb128(_flags); - out.append_uleb128(_address); - out.append_uleb128(_other); - } else { - // Nodes with export info: size, flags, address - uint32_t nodeSize = llvm::getULEB128Size(_flags) - + llvm::getULEB128Size(_address); - assert(nodeSize < 256); - out.append_byte(nodeSize); - out.append_uleb128(_flags); - out.append_uleb128(_address); - } - } else { - // Node with no export info. - uint32_t nodeSize = 0; - out.append_byte(nodeSize); - } - // Add number of children. - assert(_children.size() < 256); - out.append_byte(_children.size()); - // Append each child edge substring and node offset. - for (TrieEdge &edge : _children) { - out.append_string(edge._subString); - out.append_uleb128(edge._child->_trieOffset); - } -} - -void MachOFileLayout::buildExportTrie() { - if (_file.exportInfo.empty()) - return; - - // For all temporary strings and objects used building trie. - BumpPtrAllocator allocator; - - // Build trie of all exported symbols. - auto *rootNode = new (allocator) TrieNode(StringRef()); - std::vector<TrieNode*> allNodes; - allNodes.reserve(_file.exportInfo.size()*2); - allNodes.push_back(rootNode); - for (const Export& entry : _file.exportInfo) { - rootNode->addSymbol(entry, allocator, allNodes); - } - - std::vector<TrieNode*> orderedNodes; - orderedNodes.reserve(allNodes.size()); - - for (const Export& entry : _file.exportInfo) - rootNode->addOrderedNodes(entry, orderedNodes); - - // Assign each node in the vector an offset in the trie stream, iterating - // until all uleb128 sizes have stabilized. - bool more; - do { - uint32_t offset = 0; - more = false; - for (TrieNode* node : orderedNodes) { - if (node->updateOffset(offset)) - more = true; - } - } while (more); - - // Serialize trie to ByteBuffer. - for (TrieNode* node : orderedNodes) { - node->appendToByteBuffer(_exportTrie); - } - _exportTrie.align(_is64 ? 8 : 4); -} - -void MachOFileLayout::computeSymbolTableSizes() { - // MachO symbol tables have three ranges: locals, globals, and undefines - const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist)); - _symbolTableSize = nlistSize * (_file.stabsSymbols.size() - + _file.localSymbols.size() - + _file.globalSymbols.size() - + _file.undefinedSymbols.size()); - // Always reserve 1-byte for the empty string and 1-byte for its terminator. - _symbolStringPoolSize = 2; - for (const Symbol &sym : _file.stabsSymbols) { - _symbolStringPoolSize += (sym.name.size()+1); - } - for (const Symbol &sym : _file.localSymbols) { - _symbolStringPoolSize += (sym.name.size()+1); - } - for (const Symbol &sym : _file.globalSymbols) { - _symbolStringPoolSize += (sym.name.size()+1); - } - for (const Symbol &sym : _file.undefinedSymbols) { - _symbolStringPoolSize += (sym.name.size()+1); - } - _symbolTableLocalsStartIndex = 0; - _symbolTableGlobalsStartIndex = _file.stabsSymbols.size() + - _file.localSymbols.size(); - _symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex - + _file.globalSymbols.size(); - - _indirectSymbolTableCount = 0; - for (const Section § : _file.sections) { - _indirectSymbolTableCount += sect.indirectSymbols.size(); - } -} - -void MachOFileLayout::computeFunctionStartsSize() { - _functionStartsSize = _file.functionStarts.size(); -} - -void MachOFileLayout::computeDataInCodeSize() { - _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry); -} - -void MachOFileLayout::writeLinkEditContent() { - if (_file.fileType == llvm::MachO::MH_OBJECT) { - writeRelocations(); - writeFunctionStartsInfo(); - writeDataInCodeInfo(); - writeSymbolTable(); - } else { - writeRebaseInfo(); - writeBindingInfo(); - writeLazyBindingInfo(); - // TODO: add weak binding info - writeExportInfo(); - writeFunctionStartsInfo(); - writeDataInCodeInfo(); - writeSymbolTable(); - } -} - -llvm::Error MachOFileLayout::writeBinary(StringRef path) { - // Check for pending error from constructor. - if (_ec) - return llvm::errorCodeToError(_ec); - // Create FileOutputBuffer with calculated size. - unsigned flags = 0; - if (_file.fileType != llvm::MachO::MH_OBJECT) - flags = llvm::FileOutputBuffer::F_executable; - Expected<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr = - llvm::FileOutputBuffer::create(path, size(), flags); - if (Error E = fobOrErr.takeError()) - return E; - std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr; - // Write content. - _buffer = fob->getBufferStart(); - writeMachHeader(); - if (auto ec = writeLoadCommands()) - return ec; - writeSectionContent(); - writeLinkEditContent(); - if (Error E = fob->commit()) - return E; - - return llvm::Error::success(); -} - -/// Takes in-memory normalized view and writes a mach-o object file. -llvm::Error writeBinary(const NormalizedFile &file, StringRef path) { - MachOFileLayout layout(file, false); - return layout.writeBinary(path); -} - -} // namespace normalized -} // namespace mach_o -} // namespace lld diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp deleted file mode 100644 index ddfd1764f7e1..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ /dev/null @@ -1,1657 +0,0 @@ -//===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.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 Converts from in-memory Atoms to in-memory normalized mach-o. -/// -/// +------------+ -/// | normalized | -/// +------------+ -/// ^ -/// | -/// | -/// +-------+ -/// | Atoms | -/// +-------+ - -#include "ArchHandler.h" -#include "DebugInfo.h" -#include "MachONormalizedFile.h" -#include "MachONormalizedFileBinaryUtils.h" -#include "lld/Common/LLVM.h" -#include "lld/Core/Error.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Format.h" -#include <map> -#include <system_error> -#include <unordered_set> - -using llvm::StringRef; -using llvm::isa; -using namespace llvm::MachO; -using namespace lld::mach_o::normalized; -using namespace lld; - -namespace { - -struct AtomInfo { - const DefinedAtom *atom; - uint64_t offsetInSection; -}; - -struct SectionInfo { - SectionInfo(StringRef seg, StringRef sect, SectionType type, - const MachOLinkingContext &ctxt, uint32_t attr, - bool relocsToDefinedCanBeImplicit); - - StringRef segmentName; - StringRef sectionName; - SectionType type; - uint32_t attributes; - uint64_t address; - uint64_t size; - uint16_t alignment; - - /// If this is set, the any relocs in this section which point to defined - /// addresses can be implicitly generated. This is the case for the - /// __eh_frame section where references to the function can be implicit if the - /// function is defined. - bool relocsToDefinedCanBeImplicit; - - - std::vector<AtomInfo> atomsAndOffsets; - uint32_t normalizedSectionIndex; - uint32_t finalSectionIndex; -}; - -SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t, - const MachOLinkingContext &ctxt, uint32_t attrs, - bool relocsToDefinedCanBeImplicit) - : segmentName(sg), sectionName(sct), type(t), attributes(attrs), - address(0), size(0), alignment(1), - relocsToDefinedCanBeImplicit(relocsToDefinedCanBeImplicit), - normalizedSectionIndex(0), finalSectionIndex(0) { - uint16_t align = 1; - if (ctxt.sectionAligned(segmentName, sectionName, align)) { - alignment = align; - } -} - -struct SegmentInfo { - SegmentInfo(StringRef name); - - StringRef name; - uint64_t address; - uint64_t size; - uint32_t init_access; - uint32_t max_access; - std::vector<SectionInfo*> sections; - uint32_t normalizedSegmentIndex; -}; - -SegmentInfo::SegmentInfo(StringRef n) - : name(n), address(0), size(0), init_access(0), max_access(0), - normalizedSegmentIndex(0) { -} - -class Util { -public: - Util(const MachOLinkingContext &ctxt) - : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr), - _hasTLVDescriptors(false), _subsectionsViaSymbols(true) {} - ~Util(); - - void processDefinedAtoms(const lld::File &atomFile); - void processAtomAttributes(const DefinedAtom *atom); - void assignAtomToSection(const DefinedAtom *atom); - void organizeSections(); - void assignAddressesToSections(const NormalizedFile &file); - uint32_t fileFlags(); - void copySegmentInfo(NormalizedFile &file); - void copySectionInfo(NormalizedFile &file); - void updateSectionInfo(NormalizedFile &file); - void buildAtomToAddressMap(); - llvm::Error synthesizeDebugNotes(NormalizedFile &file); - llvm::Error addSymbols(const lld::File &atomFile, NormalizedFile &file); - void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file); - void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file); - void addExportInfo(const lld::File &, NormalizedFile &file); - void addSectionRelocs(const lld::File &, NormalizedFile &file); - void addFunctionStarts(const lld::File &, NormalizedFile &file); - void buildDataInCodeArray(const lld::File &, NormalizedFile &file); - void addDependentDylibs(const lld::File &, NormalizedFile &file); - void copyEntryPointAddress(NormalizedFile &file); - void copySectionContent(NormalizedFile &file); - - bool allSourceFilesHaveMinVersions() const { - return _allSourceFilesHaveMinVersions; - } - - uint32_t minVersion() const { - return _minVersion; - } - - LoadCommandType minVersionCommandType() const { - return _minVersionCommandType; - } - -private: - typedef std::map<DefinedAtom::ContentType, SectionInfo*> TypeToSection; - typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress; - - struct DylibInfo { int ordinal; bool hasWeak; bool hasNonWeak; }; - typedef llvm::StringMap<DylibInfo> DylibPathToInfo; - - SectionInfo *sectionForAtom(const DefinedAtom*); - SectionInfo *getRelocatableSection(DefinedAtom::ContentType type); - SectionInfo *getFinalSection(DefinedAtom::ContentType type); - void appendAtom(SectionInfo *sect, const DefinedAtom *atom); - SegmentInfo *segmentForName(StringRef segName); - void layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr); - void layoutSectionsInTextSegment(size_t, SegmentInfo *, uint64_t &); - void copySectionContent(SectionInfo *si, ContentBytes &content); - uint16_t descBits(const DefinedAtom* atom); - int dylibOrdinal(const SharedLibraryAtom *sa); - void segIndexForSection(const SectionInfo *sect, - uint8_t &segmentIndex, uint64_t &segmentStartAddr); - const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom); - const Atom *targetOfStub(const DefinedAtom *stubAtom); - llvm::Error getSymbolTableRegion(const DefinedAtom* atom, - bool &inGlobalsRegion, - SymbolScope &symbolScope); - void appendSection(SectionInfo *si, NormalizedFile &file); - uint32_t sectionIndexForAtom(const Atom *atom); - void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, - NormalizedFile &file); - - typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex; - struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; - struct AtomSorter { - bool operator()(const AtomAndIndex &left, const AtomAndIndex &right); - }; - struct SegmentSorter { - bool operator()(const SegmentInfo *left, const SegmentInfo *right); - static unsigned weight(const SegmentInfo *); - }; - struct TextSectionSorter { - bool operator()(const SectionInfo *left, const SectionInfo *right); - static unsigned weight(const SectionInfo *); - }; - - const MachOLinkingContext &_ctx; - mach_o::ArchHandler &_archHandler; - llvm::BumpPtrAllocator _allocator; - std::vector<SectionInfo*> _sectionInfos; - std::vector<SegmentInfo*> _segmentInfos; - TypeToSection _sectionMap; - std::vector<SectionInfo*> _customSections; - AtomToAddress _atomToAddress; - DylibPathToInfo _dylibInfo; - const DefinedAtom *_entryAtom; - AtomToIndex _atomToSymbolIndex; - std::vector<const Atom *> _machHeaderAliasAtoms; - bool _hasTLVDescriptors; - bool _subsectionsViaSymbols; - bool _allSourceFilesHaveMinVersions = true; - LoadCommandType _minVersionCommandType = (LoadCommandType)0; - uint32_t _minVersion = 0; - std::vector<lld::mach_o::Stab> _stabs; -}; - -Util::~Util() { - // The SectionInfo structs are BumpPtr allocated, but atomsAndOffsets needs - // to be deleted. - for (SectionInfo *si : _sectionInfos) { - // clear() destroys vector elements, but does not deallocate. - // Instead use swap() to deallocate vector buffer. - std::vector<AtomInfo> empty; - si->atomsAndOffsets.swap(empty); - } - // The SegmentInfo structs are BumpPtr allocated, but sections needs - // to be deleted. - for (SegmentInfo *sgi : _segmentInfos) { - std::vector<SectionInfo*> empty2; - sgi->sections.swap(empty2); - } -} - -SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) { - StringRef segmentName; - StringRef sectionName; - SectionType sectionType; - SectionAttr sectionAttrs; - bool relocsToDefinedCanBeImplicit; - - // Use same table used by when parsing .o files. - relocatableSectionInfoForContentType(type, segmentName, sectionName, - sectionType, sectionAttrs, - relocsToDefinedCanBeImplicit); - // If we already have a SectionInfo with this name, re-use it. - // This can happen if two ContentType map to the same mach-o section. - for (auto sect : _sectionMap) { - if (sect.second->sectionName.equals(sectionName) && - sect.second->segmentName.equals(segmentName)) { - return sect.second; - } - } - // Otherwise allocate new SectionInfo object. - auto *sect = new (_allocator) - SectionInfo(segmentName, sectionName, sectionType, _ctx, sectionAttrs, - relocsToDefinedCanBeImplicit); - _sectionInfos.push_back(sect); - _sectionMap[type] = sect; - return sect; -} - -#define ENTRY(seg, sect, type, atomType) \ - {seg, sect, type, DefinedAtom::atomType } - -struct MachOFinalSectionFromAtomType { - StringRef segmentName; - StringRef sectionName; - SectionType sectionType; - DefinedAtom::ContentType atomType; -}; - -const MachOFinalSectionFromAtomType sectsToAtomType[] = { - ENTRY("__TEXT", "__text", S_REGULAR, typeCode), - ENTRY("__TEXT", "__text", S_REGULAR, typeMachHeader), - ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString), - ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String), - ENTRY("__TEXT", "__const", S_REGULAR, typeConstant), - ENTRY("__TEXT", "__const", S_4BYTE_LITERALS, typeLiteral4), - ENTRY("__TEXT", "__const", S_8BYTE_LITERALS, typeLiteral8), - ENTRY("__TEXT", "__const", S_16BYTE_LITERALS, typeLiteral16), - ENTRY("__TEXT", "__stubs", S_SYMBOL_STUBS, typeStub), - ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper), - ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), - ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), - ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo), - ENTRY("__DATA", "__data", S_REGULAR, typeData), - ENTRY("__DATA", "__const", S_REGULAR, typeConstData), - ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), - ENTRY("__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS, - typeLazyPointer), - ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, - typeInitializerPtr), - ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS, - typeTerminatorPtr), - ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, - typeGOT), - ENTRY("__DATA", "__nl_symbol_ptr", S_NON_LAZY_SYMBOL_POINTERS, - typeNonLazyPointer), - ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, - typeThunkTLV), - ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, - typeTLVInitialData), - ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS, - typeTLVInitializerPtr), - ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, - typeTLVInitialZeroFill), - ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), - ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), -}; -#undef ENTRY - -SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { - for (auto &p : sectsToAtomType) { - if (p.atomType != atomType) - continue; - SectionAttr sectionAttrs = 0; - switch (atomType) { - case DefinedAtom::typeMachHeader: - case DefinedAtom::typeCode: - case DefinedAtom::typeStub: - case DefinedAtom::typeStubHelper: - sectionAttrs = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS; - break; - case DefinedAtom::typeThunkTLV: - _hasTLVDescriptors = true; - break; - default: - break; - } - // If we already have a SectionInfo with this name, re-use it. - // This can happen if two ContentType map to the same mach-o section. - for (auto sect : _sectionMap) { - if (sect.second->sectionName.equals(p.sectionName) && - sect.second->segmentName.equals(p.segmentName)) { - return sect.second; - } - } - // Otherwise allocate new SectionInfo object. - auto *sect = new (_allocator) SectionInfo( - p.segmentName, p.sectionName, p.sectionType, _ctx, sectionAttrs, - /* relocsToDefinedCanBeImplicit */ false); - _sectionInfos.push_back(sect); - _sectionMap[atomType] = sect; - return sect; - } - llvm_unreachable("content type not yet supported"); -} - -SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { - if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { - // Section for this atom is derived from content type. - DefinedAtom::ContentType type = atom->contentType(); - auto pos = _sectionMap.find(type); - if ( pos != _sectionMap.end() ) - return pos->second; - bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); - return rMode ? getRelocatableSection(type) : getFinalSection(type); - } else { - // This atom needs to be in a custom section. - StringRef customName = atom->customSectionName(); - // Look to see if we have already allocated the needed custom section. - for(SectionInfo *sect : _customSections) { - const DefinedAtom *firstAtom = sect->atomsAndOffsets.front().atom; - if (firstAtom->customSectionName().equals(customName)) { - return sect; - } - } - // Not found, so need to create a new custom section. - size_t seperatorIndex = customName.find('/'); - assert(seperatorIndex != StringRef::npos); - StringRef segName = customName.slice(0, seperatorIndex); - StringRef sectName = customName.drop_front(seperatorIndex + 1); - auto *sect = - new (_allocator) SectionInfo(segName, sectName, S_REGULAR, _ctx, - 0, /* relocsToDefinedCanBeImplicit */ false); - _customSections.push_back(sect); - _sectionInfos.push_back(sect); - return sect; - } -} - -void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) { - // Figure out offset for atom in this section given alignment constraints. - uint64_t offset = sect->size; - DefinedAtom::Alignment atomAlign = atom->alignment(); - uint64_t align = atomAlign.value; - uint64_t requiredModulus = atomAlign.modulus; - uint64_t currentModulus = (offset % align); - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += align+requiredModulus-currentModulus; - } - // Record max alignment of any atom in this section. - if (align > sect->alignment) - sect->alignment = atomAlign.value; - // Assign atom to this section with this offset. - AtomInfo ai = {atom, offset}; - sect->atomsAndOffsets.push_back(ai); - // Update section size to include this atom. - sect->size = offset + atom->size(); -} - -void Util::processDefinedAtoms(const lld::File &atomFile) { - for (const DefinedAtom *atom : atomFile.defined()) { - processAtomAttributes(atom); - assignAtomToSection(atom); - } -} - -void Util::processAtomAttributes(const DefinedAtom *atom) { - if (auto *machoFile = dyn_cast<mach_o::MachOFile>(&atom->file())) { - // If the file doesn't use subsections via symbols, then make sure we don't - // add that flag to the final output file if we have a relocatable file. - if (!machoFile->subsectionsViaSymbols()) - _subsectionsViaSymbols = false; - - // All the source files must have min versions for us to output an object - // file with a min version. - if (auto v = machoFile->minVersion()) - _minVersion = std::max(_minVersion, v); - else - _allSourceFilesHaveMinVersions = false; - - // If we don't have a platform load command, but one of the source files - // does, then take the one from the file. - if (!_minVersionCommandType) - if (auto v = machoFile->minVersionLoadCommandKind()) - _minVersionCommandType = v; - } -} - -void Util::assignAtomToSection(const DefinedAtom *atom) { - if (atom->contentType() == DefinedAtom::typeMachHeader) { - _machHeaderAliasAtoms.push_back(atom); - // Assign atom to this section with this offset. - AtomInfo ai = {atom, 0}; - sectionForAtom(atom)->atomsAndOffsets.push_back(ai); - } else if (atom->contentType() == DefinedAtom::typeDSOHandle) - _machHeaderAliasAtoms.push_back(atom); - else - appendAtom(sectionForAtom(atom), atom); -} - -SegmentInfo *Util::segmentForName(StringRef segName) { - for (SegmentInfo *si : _segmentInfos) { - if ( si->name.equals(segName) ) - return si; - } - auto *info = new (_allocator) SegmentInfo(segName); - - // Set the initial segment protection. - if (segName.equals("__TEXT")) - info->init_access = VM_PROT_READ | VM_PROT_EXECUTE; - else if (segName.equals("__PAGEZERO")) - info->init_access = 0; - else if (segName.equals("__LINKEDIT")) - info->init_access = VM_PROT_READ; - else { - // All others default to read-write - info->init_access = VM_PROT_READ | VM_PROT_WRITE; - } - - // Set max segment protection - // Note, its overkill to use a switch statement here, but makes it so much - // easier to use switch coverage to catch new cases. - switch (_ctx.os()) { - case lld::MachOLinkingContext::OS::unknown: - case lld::MachOLinkingContext::OS::macOSX: - case lld::MachOLinkingContext::OS::iOS_simulator: - if (segName.equals("__PAGEZERO")) { - info->max_access = 0; - break; - } - // All others default to all - info->max_access = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - break; - case lld::MachOLinkingContext::OS::iOS: - // iPhoneOS always uses same protection for max and initial - info->max_access = info->init_access; - break; - } - _segmentInfos.push_back(info); - return info; -} - -unsigned Util::SegmentSorter::weight(const SegmentInfo *seg) { - return llvm::StringSwitch<unsigned>(seg->name) - .Case("__PAGEZERO", 1) - .Case("__TEXT", 2) - .Case("__DATA", 3) - .Default(100); -} - -bool Util::SegmentSorter::operator()(const SegmentInfo *left, - const SegmentInfo *right) { - return (weight(left) < weight(right)); -} - -unsigned Util::TextSectionSorter::weight(const SectionInfo *sect) { - return llvm::StringSwitch<unsigned>(sect->sectionName) - .Case("__text", 1) - .Case("__stubs", 2) - .Case("__stub_helper", 3) - .Case("__const", 4) - .Case("__cstring", 5) - .Case("__unwind_info", 98) - .Case("__eh_frame", 99) - .Default(10); -} - -bool Util::TextSectionSorter::operator()(const SectionInfo *left, - const SectionInfo *right) { - return (weight(left) < weight(right)); -} - -void Util::organizeSections() { - // NOTE!: Keep this in sync with assignAddressesToSections. - switch (_ctx.outputMachOType()) { - case llvm::MachO::MH_EXECUTE: - // Main executables, need a zero-page segment - segmentForName("__PAGEZERO"); - // Fall into next case. - LLVM_FALLTHROUGH; - case llvm::MachO::MH_DYLIB: - case llvm::MachO::MH_BUNDLE: - // All dynamic code needs TEXT segment to hold the load commands. - segmentForName("__TEXT"); - break; - default: - break; - } - segmentForName("__LINKEDIT"); - - // Group sections into segments. - for (SectionInfo *si : _sectionInfos) { - SegmentInfo *seg = segmentForName(si->segmentName); - seg->sections.push_back(si); - } - // Sort segments. - std::sort(_segmentInfos.begin(), _segmentInfos.end(), SegmentSorter()); - - // Sort sections within segments. - for (SegmentInfo *seg : _segmentInfos) { - if (seg->name.equals("__TEXT")) { - std::sort(seg->sections.begin(), seg->sections.end(), - TextSectionSorter()); - } - } - - // Record final section indexes. - uint32_t segmentIndex = 0; - uint32_t sectionIndex = 1; - for (SegmentInfo *seg : _segmentInfos) { - seg->normalizedSegmentIndex = segmentIndex++; - for (SectionInfo *sect : seg->sections) - sect->finalSectionIndex = sectionIndex++; - } -} - -void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) { - seg->address = addr; - for (SectionInfo *sect : seg->sections) { - sect->address = llvm::alignTo(addr, sect->alignment); - addr = sect->address + sect->size; - } - seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); -} - -// __TEXT segment lays out backwards so padding is at front after load commands. -void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, - uint64_t &addr) { - seg->address = addr; - // Walks sections starting at end to calculate padding for start. - int64_t taddr = 0; - for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) { - SectionInfo *sect = *it; - taddr -= sect->size; - taddr = taddr & (0 - sect->alignment); - } - int64_t padding = taddr - hlcSize; - while (padding < 0) - padding += _ctx.pageSize(); - // Start assigning section address starting at padded offset. - addr += (padding + hlcSize); - for (SectionInfo *sect : seg->sections) { - sect->address = llvm::alignTo(addr, sect->alignment); - addr = sect->address + sect->size; - } - seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); -} - -void Util::assignAddressesToSections(const NormalizedFile &file) { - // NOTE!: Keep this in sync with organizeSections. - size_t hlcSize = headerAndLoadCommandsSize(file, - _ctx.generateFunctionStartsLoadCommand()); - uint64_t address = 0; - for (SegmentInfo *seg : _segmentInfos) { - if (seg->name.equals("__PAGEZERO")) { - seg->size = _ctx.pageZeroSize(); - address += seg->size; - } - else if (seg->name.equals("__TEXT")) { - // _ctx.baseAddress() == 0 implies it was either unspecified or - // pageZeroSize is also 0. In either case resetting address is safe. - address = _ctx.baseAddress() ? _ctx.baseAddress() : address; - layoutSectionsInTextSegment(hlcSize, seg, address); - } else - layoutSectionsInSegment(seg, address); - - address = llvm::alignTo(address, _ctx.pageSize()); - } - DEBUG_WITH_TYPE("WriterMachO-norm", - llvm::dbgs() << "assignAddressesToSections()\n"; - for (SegmentInfo *sgi : _segmentInfos) { - llvm::dbgs() << " address=" << llvm::format("0x%08llX", sgi->address) - << ", size=" << llvm::format("0x%08llX", sgi->size) - << ", segment-name='" << sgi->name - << "'\n"; - for (SectionInfo *si : sgi->sections) { - llvm::dbgs()<< " addr=" << llvm::format("0x%08llX", si->address) - << ", size=" << llvm::format("0x%08llX", si->size) - << ", section-name='" << si->sectionName - << "\n"; - } - } - ); -} - -void Util::copySegmentInfo(NormalizedFile &file) { - for (SegmentInfo *sgi : _segmentInfos) { - Segment seg; - seg.name = sgi->name; - seg.address = sgi->address; - seg.size = sgi->size; - seg.init_access = sgi->init_access; - seg.max_access = sgi->max_access; - file.segments.push_back(seg); - } -} - -void Util::appendSection(SectionInfo *si, NormalizedFile &file) { - // Add new empty section to end of file.sections. - Section temp; - file.sections.push_back(std::move(temp)); - Section* normSect = &file.sections.back(); - // Copy fields to normalized section. - normSect->segmentName = si->segmentName; - normSect->sectionName = si->sectionName; - normSect->type = si->type; - normSect->attributes = si->attributes; - normSect->address = si->address; - normSect->alignment = si->alignment; - // Record where normalized section is. - si->normalizedSectionIndex = file.sections.size()-1; -} - -void Util::copySectionContent(NormalizedFile &file) { - const bool r = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); - - // Utility function for ArchHandler to find address of atom in output file. - auto addrForAtom = [&] (const Atom &atom) -> uint64_t { - auto pos = _atomToAddress.find(&atom); - assert(pos != _atomToAddress.end()); - return pos->second; - }; - - auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t { - for (const SectionInfo *sectInfo : _sectionInfos) - for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) - if (atomInfo.atom == &atom) - return sectInfo->address; - llvm_unreachable("atom not assigned to section"); - }; - - for (SectionInfo *si : _sectionInfos) { - Section *normSect = &file.sections[si->normalizedSectionIndex]; - if (isZeroFillSection(si->type)) { - const uint8_t *empty = nullptr; - normSect->content = llvm::makeArrayRef(empty, si->size); - continue; - } - // Copy content from atoms to content buffer for section. - llvm::MutableArrayRef<uint8_t> sectionContent; - if (si->size) { - uint8_t *sectContent = file.ownedAllocations.Allocate<uint8_t>(si->size); - sectionContent = llvm::MutableArrayRef<uint8_t>(sectContent, si->size); - normSect->content = sectionContent; - } - for (AtomInfo &ai : si->atomsAndOffsets) { - if (!ai.atom->size()) { - assert(ai.atom->begin() == ai.atom->end() && - "Cannot have references without content"); - continue; - } - auto atomContent = sectionContent.slice(ai.offsetInSection, - ai.atom->size()); - _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, - sectionAddrForAtom, _ctx.baseAddress(), - atomContent); - } - } -} - -void Util::copySectionInfo(NormalizedFile &file) { - file.sections.reserve(_sectionInfos.size()); - // Write sections grouped by segment. - for (SegmentInfo *sgi : _segmentInfos) { - for (SectionInfo *si : sgi->sections) { - appendSection(si, file); - } - } -} - -void Util::updateSectionInfo(NormalizedFile &file) { - file.sections.reserve(_sectionInfos.size()); - // sections grouped by segment. - for (SegmentInfo *sgi : _segmentInfos) { - Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex]; - normSeg->address = sgi->address; - normSeg->size = sgi->size; - for (SectionInfo *si : sgi->sections) { - Section *normSect = &file.sections[si->normalizedSectionIndex]; - normSect->address = si->address; - } - } -} - -void Util::copyEntryPointAddress(NormalizedFile &nFile) { - if (!_entryAtom) { - nFile.entryAddress = 0; - return; - } - - if (_ctx.outputTypeHasEntry()) { - if (_archHandler.isThumbFunction(*_entryAtom)) - nFile.entryAddress = (_atomToAddress[_entryAtom] | 1); - else - nFile.entryAddress = _atomToAddress[_entryAtom]; - } -} - -void Util::buildAtomToAddressMap() { - DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() - << "assign atom addresses:\n"); - const bool lookForEntry = _ctx.outputTypeHasEntry(); - for (SectionInfo *sect : _sectionInfos) { - for (const AtomInfo &info : sect->atomsAndOffsets) { - _atomToAddress[info.atom] = sect->address + info.offsetInSection; - if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) && - (info.atom->size() != 0) && - info.atom->name() == _ctx.entrySymbolName()) { - _entryAtom = info.atom; - } - DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() - << " address=" - << llvm::format("0x%016X", _atomToAddress[info.atom]) - << llvm::format(" 0x%09lX", info.atom) - << ", file=#" - << info.atom->file().ordinal() - << ", atom=#" - << info.atom->ordinal() - << ", name=" - << info.atom->name() - << ", type=" - << info.atom->contentType() - << "\n"); - } - } - DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() - << "assign header alias atom addresses:\n"); - for (const Atom *atom : _machHeaderAliasAtoms) { - _atomToAddress[atom] = _ctx.baseAddress(); -#ifndef NDEBUG - if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) { - DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() - << " address=" - << llvm::format("0x%016X", _atomToAddress[atom]) - << llvm::format(" 0x%09lX", atom) - << ", file=#" - << definedAtom->file().ordinal() - << ", atom=#" - << definedAtom->ordinal() - << ", name=" - << definedAtom->name() - << ", type=" - << definedAtom->contentType() - << "\n"); - } else { - DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() - << " address=" - << llvm::format("0x%016X", _atomToAddress[atom]) - << " atom=" << atom - << " name=" << atom->name() << "\n"); - } -#endif - } -} - -llvm::Error Util::synthesizeDebugNotes(NormalizedFile &file) { - - // Bail out early if we don't need to generate a debug map. - if (_ctx.debugInfoMode() == MachOLinkingContext::DebugInfoMode::noDebugMap) - return llvm::Error::success(); - - std::vector<const DefinedAtom*> atomsNeedingDebugNotes; - std::set<const mach_o::MachOFile*> filesWithStabs; - bool objFileHasDwarf = false; - const File *objFile = nullptr; - - for (SectionInfo *sect : _sectionInfos) { - for (const AtomInfo &info : sect->atomsAndOffsets) { - if (const DefinedAtom *atom = dyn_cast<DefinedAtom>(info.atom)) { - - // FIXME: No stabs/debug-notes for symbols that wouldn't be in the - // symbol table. - // FIXME: No stabs/debug-notes for kernel dtrace probes. - - if (atom->contentType() == DefinedAtom::typeCFI || - atom->contentType() == DefinedAtom::typeCString) - continue; - - // Whenever we encounter a new file, update the 'objfileHasDwarf' flag. - if (&info.atom->file() != objFile) { - objFileHasDwarf = false; - if (const mach_o::MachOFile *atomFile = - dyn_cast<mach_o::MachOFile>(&info.atom->file())) { - if (atomFile->debugInfo()) { - if (isa<mach_o::DwarfDebugInfo>(atomFile->debugInfo())) - objFileHasDwarf = true; - else if (isa<mach_o::StabsDebugInfo>(atomFile->debugInfo())) - filesWithStabs.insert(atomFile); - } - } - } - - // If this atom is from a file that needs dwarf, add it to the list. - if (objFileHasDwarf) - atomsNeedingDebugNotes.push_back(info.atom); - } - } - } - - // Sort atoms needing debug notes by file ordinal, then atom ordinal. - std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), - [](const DefinedAtom *lhs, const DefinedAtom *rhs) { - if (lhs->file().ordinal() != rhs->file().ordinal()) - return (lhs->file().ordinal() < rhs->file().ordinal()); - return (lhs->ordinal() < rhs->ordinal()); - }); - - // FIXME: Handle <rdar://problem/17689030>: Add -add_ast_path option to \ - // linker which add N_AST stab entry to output - // See OutputFile::synthesizeDebugNotes in ObjectFile.cpp in ld64. - - StringRef oldFileName = ""; - StringRef oldDirPath = ""; - bool wroteStartSO = false; - std::unordered_set<std::string> seenFiles; - for (const DefinedAtom *atom : atomsNeedingDebugNotes) { - const auto &atomFile = cast<mach_o::MachOFile>(atom->file()); - assert(dyn_cast_or_null<lld::mach_o::DwarfDebugInfo>(atomFile.debugInfo()) - && "file for atom needing debug notes does not contain dwarf"); - auto &dwarf = cast<lld::mach_o::DwarfDebugInfo>(*atomFile.debugInfo()); - - auto &tu = dwarf.translationUnitSource(); - StringRef newFileName = tu.name; - StringRef newDirPath = tu.path; - - // Add an SO whenever the TU source file changes. - if (newFileName != oldFileName || newDirPath != oldDirPath) { - // Translation unit change, emit ending SO - if (oldFileName != "") - _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, "")); - - oldFileName = newFileName; - oldDirPath = newDirPath; - - // If newDirPath doesn't end with a '/' we need to add one: - if (newDirPath.back() != '/') { - char *p = - file.ownedAllocations.Allocate<char>(newDirPath.size() + 2); - memcpy(p, newDirPath.data(), newDirPath.size()); - p[newDirPath.size()] = '/'; - p[newDirPath.size() + 1] = '\0'; - newDirPath = p; - } - - // New translation unit, emit start SOs: - _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newDirPath)); - _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newFileName)); - - // Synthesize OSO for start of file. - char *fullPath = nullptr; - { - SmallString<1024> pathBuf(atomFile.path()); - if (auto EC = llvm::sys::fs::make_absolute(pathBuf)) - return llvm::errorCodeToError(EC); - fullPath = file.ownedAllocations.Allocate<char>(pathBuf.size() + 1); - memcpy(fullPath, pathBuf.c_str(), pathBuf.size() + 1); - } - - // Get mod time. - uint32_t modTime = 0; - llvm::sys::fs::file_status stat; - if (!llvm::sys::fs::status(fullPath, stat)) - if (llvm::sys::fs::exists(stat)) - modTime = llvm::sys::toTimeT(stat.getLastModificationTime()); - - _stabs.push_back(mach_o::Stab(nullptr, N_OSO, _ctx.getCPUSubType(), 1, - modTime, fullPath)); - // <rdar://problem/6337329> linker should put cpusubtype in n_sect field - // of nlist entry for N_OSO debug note entries. - wroteStartSO = true; - } - - if (atom->contentType() == DefinedAtom::typeCode) { - // Synthesize BNSYM and start FUN stabs. - _stabs.push_back(mach_o::Stab(atom, N_BNSYM, 1, 0, 0, "")); - _stabs.push_back(mach_o::Stab(atom, N_FUN, 1, 0, 0, atom->name())); - // Synthesize any SOL stabs needed - // FIXME: add SOL stabs. - _stabs.push_back(mach_o::Stab(nullptr, N_FUN, 0, 0, - atom->rawContent().size(), "")); - _stabs.push_back(mach_o::Stab(nullptr, N_ENSYM, 1, 0, - atom->rawContent().size(), "")); - } else { - if (atom->scope() == Atom::scopeTranslationUnit) - _stabs.push_back(mach_o::Stab(atom, N_STSYM, 1, 0, 0, atom->name())); - else - _stabs.push_back(mach_o::Stab(nullptr, N_GSYM, 1, 0, 0, atom->name())); - } - } - - // Emit ending SO if necessary. - if (wroteStartSO) - _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, "")); - - // Copy any stabs from .o file. - for (const auto *objFile : filesWithStabs) { - const auto &stabsList = - cast<mach_o::StabsDebugInfo>(objFile->debugInfo())->stabs(); - for (auto &stab : stabsList) { - // FIXME: Drop stabs whose atoms have been dead-stripped. - _stabs.push_back(stab); - } - } - - return llvm::Error::success(); -} - -uint16_t Util::descBits(const DefinedAtom* atom) { - uint16_t desc = 0; - switch (atom->merge()) { - case lld::DefinedAtom::mergeNo: - case lld::DefinedAtom::mergeAsTentative: - break; - case lld::DefinedAtom::mergeAsWeak: - case lld::DefinedAtom::mergeAsWeakAndAddressUsed: - desc |= N_WEAK_DEF; - break; - case lld::DefinedAtom::mergeSameNameAndSize: - case lld::DefinedAtom::mergeByLargestSection: - case lld::DefinedAtom::mergeByContent: - llvm_unreachable("Unsupported DefinedAtom::merge()"); - break; - } - if (atom->contentType() == lld::DefinedAtom::typeResolver) - desc |= N_SYMBOL_RESOLVER; - if (atom->contentType() == lld::DefinedAtom::typeMachHeader) - desc |= REFERENCED_DYNAMICALLY; - if (_archHandler.isThumbFunction(*atom)) - desc |= N_ARM_THUMB_DEF; - if (atom->deadStrip() == DefinedAtom::deadStripNever && - _ctx.outputMachOType() == llvm::MachO::MH_OBJECT) { - if ((atom->contentType() != DefinedAtom::typeInitializerPtr) - && (atom->contentType() != DefinedAtom::typeTerminatorPtr)) - desc |= N_NO_DEAD_STRIP; - } - return desc; -} - -bool Util::AtomSorter::operator()(const AtomAndIndex &left, - const AtomAndIndex &right) { - return (left.atom->name().compare(right.atom->name()) < 0); -} - -llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom, - bool &inGlobalsRegion, - SymbolScope &scope) { - bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); - switch (atom->scope()) { - case Atom::scopeTranslationUnit: - scope = 0; - inGlobalsRegion = false; - return llvm::Error::success(); - case Atom::scopeLinkageUnit: - if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::exported) && - _ctx.exportSymbolNamed(atom->name())) { - return llvm::make_error<GenericError>( - Twine("cannot export hidden symbol ") + atom->name()); - } - if (rMode) { - if (_ctx.keepPrivateExterns()) { - // -keep_private_externs means keep in globals region as N_PEXT. - scope = N_PEXT | N_EXT; - inGlobalsRegion = true; - return llvm::Error::success(); - } - } - // scopeLinkageUnit symbols are no longer global once linked. - scope = N_PEXT; - inGlobalsRegion = false; - return llvm::Error::success(); - case Atom::scopeGlobal: - if (_ctx.exportRestrictMode()) { - if (_ctx.exportSymbolNamed(atom->name())) { - scope = N_EXT; - inGlobalsRegion = true; - return llvm::Error::success(); - } else { - scope = N_PEXT; - inGlobalsRegion = false; - return llvm::Error::success(); - } - } else { - scope = N_EXT; - inGlobalsRegion = true; - return llvm::Error::success(); - } - break; - } - llvm_unreachable("atom->scope() unknown enum value"); -} - - - -llvm::Error Util::addSymbols(const lld::File &atomFile, - NormalizedFile &file) { - bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); - // Mach-O symbol table has four regions: stabs, locals, globals, undefs. - - // Add all stabs. - for (auto &stab : _stabs) { - lld::mach_o::normalized::Symbol sym; - sym.type = static_cast<NListType>(stab.type); - sym.scope = 0; - sym.sect = stab.other; - sym.desc = stab.desc; - if (stab.atom) - sym.value = _atomToAddress[stab.atom]; - else - sym.value = stab.value; - sym.name = stab.str; - file.stabsSymbols.push_back(sym); - } - - // Add all local (non-global) symbols in address order - std::vector<AtomAndIndex> globals; - globals.reserve(512); - for (SectionInfo *sect : _sectionInfos) { - for (const AtomInfo &info : sect->atomsAndOffsets) { - const DefinedAtom *atom = info.atom; - if (!atom->name().empty()) { - SymbolScope symbolScope; - bool inGlobalsRegion; - if (auto ec = getSymbolTableRegion(atom, inGlobalsRegion, symbolScope)){ - return ec; - } - if (inGlobalsRegion) { - AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope }; - globals.push_back(ai); - } else { - lld::mach_o::normalized::Symbol sym; - sym.name = atom->name(); - sym.type = N_SECT; - sym.scope = symbolScope; - sym.sect = sect->finalSectionIndex; - sym.desc = descBits(atom); - sym.value = _atomToAddress[atom]; - _atomToSymbolIndex[atom] = file.localSymbols.size(); - file.localSymbols.push_back(sym); - } - } else if (rMode && _archHandler.needsLocalSymbolInRelocatableFile(atom)){ - // Create 'Lxxx' labels for anonymous atoms if archHandler says so. - static unsigned tempNum = 1; - char tmpName[16]; - sprintf(tmpName, "L%04u", tempNum++); - StringRef tempRef(tmpName); - lld::mach_o::normalized::Symbol sym; - sym.name = tempRef.copy(file.ownedAllocations); - sym.type = N_SECT; - sym.scope = 0; - sym.sect = sect->finalSectionIndex; - sym.desc = 0; - sym.value = _atomToAddress[atom]; - _atomToSymbolIndex[atom] = file.localSymbols.size(); - file.localSymbols.push_back(sym); - } - } - } - - // Sort global symbol alphabetically, then add to symbol table. - std::sort(globals.begin(), globals.end(), AtomSorter()); - const uint32_t globalStartIndex = file.localSymbols.size(); - for (AtomAndIndex &ai : globals) { - lld::mach_o::normalized::Symbol sym; - sym.name = ai.atom->name(); - sym.type = N_SECT; - sym.scope = ai.scope; - sym.sect = ai.index; - sym.desc = descBits(static_cast<const DefinedAtom*>(ai.atom)); - sym.value = _atomToAddress[ai.atom]; - _atomToSymbolIndex[ai.atom] = globalStartIndex + file.globalSymbols.size(); - file.globalSymbols.push_back(sym); - } - - // Sort undefined symbol alphabetically, then add to symbol table. - std::vector<AtomAndIndex> undefs; - undefs.reserve(128); - for (const UndefinedAtom *atom : atomFile.undefined()) { - AtomAndIndex ai = { atom, 0, N_EXT }; - undefs.push_back(ai); - } - for (const SharedLibraryAtom *atom : atomFile.sharedLibrary()) { - AtomAndIndex ai = { atom, 0, N_EXT }; - undefs.push_back(ai); - } - std::sort(undefs.begin(), undefs.end(), AtomSorter()); - const uint32_t start = file.globalSymbols.size() + file.localSymbols.size(); - for (AtomAndIndex &ai : undefs) { - lld::mach_o::normalized::Symbol sym; - uint16_t desc = 0; - if (!rMode) { - uint8_t ordinal = 0; - if (!_ctx.useFlatNamespace()) - ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom)); - llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal); - } - sym.name = ai.atom->name(); - sym.type = N_UNDF; - sym.scope = ai.scope; - sym.sect = 0; - sym.desc = desc; - sym.value = 0; - _atomToSymbolIndex[ai.atom] = file.undefinedSymbols.size() + start; - file.undefinedSymbols.push_back(sym); - } - - return llvm::Error::success(); -} - -const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) { - for (const Reference *ref : *lpAtom) { - if (_archHandler.isLazyPointer(*ref)) { - return ref->target(); - } - } - return nullptr; -} - -const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) { - for (const Reference *ref : *stubAtom) { - if (const Atom *ta = ref->target()) { - if (const DefinedAtom *lpAtom = dyn_cast<DefinedAtom>(ta)) { - const Atom *target = targetOfLazyPointer(lpAtom); - if (target) - return target; - } - } - } - return nullptr; -} - -void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) { - for (SectionInfo *si : _sectionInfos) { - Section &normSect = file.sections[si->normalizedSectionIndex]; - switch (si->type) { - case llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS: - for (const AtomInfo &info : si->atomsAndOffsets) { - bool foundTarget = false; - for (const Reference *ref : *info.atom) { - const Atom *target = ref->target(); - if (target) { - if (isa<const SharedLibraryAtom>(target)) { - uint32_t index = _atomToSymbolIndex[target]; - normSect.indirectSymbols.push_back(index); - foundTarget = true; - } else { - normSect.indirectSymbols.push_back( - llvm::MachO::INDIRECT_SYMBOL_LOCAL); - } - } - } - if (!foundTarget) { - normSect.indirectSymbols.push_back( - llvm::MachO::INDIRECT_SYMBOL_ABS); - } - } - break; - case llvm::MachO::S_LAZY_SYMBOL_POINTERS: - for (const AtomInfo &info : si->atomsAndOffsets) { - const Atom *target = targetOfLazyPointer(info.atom); - if (target) { - uint32_t index = _atomToSymbolIndex[target]; - normSect.indirectSymbols.push_back(index); - } - } - break; - case llvm::MachO::S_SYMBOL_STUBS: - for (const AtomInfo &info : si->atomsAndOffsets) { - const Atom *target = targetOfStub(info.atom); - if (target) { - uint32_t index = _atomToSymbolIndex[target]; - normSect.indirectSymbols.push_back(index); - } - } - break; - default: - break; - } - } -} - -void Util::addDependentDylibs(const lld::File &atomFile, - NormalizedFile &nFile) { - // Scan all imported symbols and build up list of dylibs they are from. - int ordinal = 1; - for (const auto *dylib : _ctx.allDylibs()) { - DylibPathToInfo::iterator pos = _dylibInfo.find(dylib->installName()); - if (pos == _dylibInfo.end()) { - DylibInfo info; - bool flatNamespaceAtom = dylib == _ctx.flatNamespaceFile(); - - // If we're in -flat_namespace mode (or this atom came from the flat - // namespace file under -undefined dynamic_lookup) then use the flat - // lookup ordinal. - if (flatNamespaceAtom || _ctx.useFlatNamespace()) - info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; - else - info.ordinal = ordinal++; - info.hasWeak = false; - info.hasNonWeak = !info.hasWeak; - _dylibInfo[dylib->installName()] = info; - - // Unless this was a flat_namespace atom, record the source dylib. - if (!flatNamespaceAtom) { - DependentDylib depInfo; - depInfo.path = dylib->installName(); - depInfo.kind = llvm::MachO::LC_LOAD_DYLIB; - depInfo.currentVersion = _ctx.dylibCurrentVersion(dylib->path()); - depInfo.compatVersion = _ctx.dylibCompatVersion(dylib->path()); - nFile.dependentDylibs.push_back(depInfo); - } - } else { - pos->second.hasWeak = false; - pos->second.hasNonWeak = !pos->second.hasWeak; - } - } - // Automatically weak link dylib in which all symbols are weak (canBeNull). - for (DependentDylib &dep : nFile.dependentDylibs) { - DylibInfo &info = _dylibInfo[dep.path]; - if (info.hasWeak && !info.hasNonWeak) - dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB; - else if (_ctx.isUpwardDylib(dep.path)) - dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB; - } -} - -int Util::dylibOrdinal(const SharedLibraryAtom *sa) { - return _dylibInfo[sa->loadName()].ordinal; -} - -void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, - uint64_t &segmentStartAddr) { - segmentIndex = 0; - for (const SegmentInfo *seg : _segmentInfos) { - if ((seg->address <= sect->address) - && (seg->address+seg->size >= sect->address+sect->size)) { - segmentStartAddr = seg->address; - return; - } - ++segmentIndex; - } - llvm_unreachable("section not in any segment"); -} - -uint32_t Util::sectionIndexForAtom(const Atom *atom) { - uint64_t address = _atomToAddress[atom]; - for (const SectionInfo *si : _sectionInfos) { - if ((si->address <= address) && (address < si->address+si->size)) - return si->finalSectionIndex; - } - llvm_unreachable("atom not in any section"); -} - -void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { - if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) - return; - - // Utility function for ArchHandler to find symbol index for an atom. - auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t { - auto pos = _atomToSymbolIndex.find(&atom); - assert(pos != _atomToSymbolIndex.end()); - return pos->second; - }; - - // Utility function for ArchHandler to find section index for an atom. - auto sectIndexForAtom = [&] (const Atom &atom) -> uint32_t { - return sectionIndexForAtom(&atom); - }; - - // Utility function for ArchHandler to find address of atom in output file. - auto addressForAtom = [&] (const Atom &atom) -> uint64_t { - auto pos = _atomToAddress.find(&atom); - assert(pos != _atomToAddress.end()); - return pos->second; - }; - - for (SectionInfo *si : _sectionInfos) { - Section &normSect = file.sections[si->normalizedSectionIndex]; - for (const AtomInfo &info : si->atomsAndOffsets) { - const DefinedAtom *atom = info.atom; - for (const Reference *ref : *atom) { - // Skip emitting relocs for sections which are always able to be - // implicitly regenerated and where the relocation targets an address - // which is defined. - if (si->relocsToDefinedCanBeImplicit && isa<DefinedAtom>(ref->target())) - continue; - _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref, - symIndexForAtom, - sectIndexForAtom, - addressForAtom, - normSect.relocations); - } - } - } -} - -void Util::addFunctionStarts(const lld::File &, NormalizedFile &file) { - if (!_ctx.generateFunctionStartsLoadCommand()) - return; - file.functionStarts.reserve(8192); - // Delta compress function starts, starting with the mach header symbol. - const uint64_t badAddress = ~0ULL; - uint64_t addr = badAddress; - for (SectionInfo *si : _sectionInfos) { - for (const AtomInfo &info : si->atomsAndOffsets) { - auto type = info.atom->contentType(); - if (type == DefinedAtom::typeMachHeader) { - addr = _atomToAddress[info.atom]; - continue; - } - if (type != DefinedAtom::typeCode) - continue; - assert(addr != badAddress && "Missing mach header symbol"); - // Skip atoms which have 0 size. This is so that LC_FUNCTION_STARTS - // can't spill in to the next section. - if (!info.atom->size()) - continue; - uint64_t nextAddr = _atomToAddress[info.atom]; - if (_archHandler.isThumbFunction(*info.atom)) - nextAddr |= 1; - uint64_t delta = nextAddr - addr; - if (delta) { - ByteBuffer buffer; - buffer.append_uleb128(delta); - file.functionStarts.insert(file.functionStarts.end(), buffer.bytes(), - buffer.bytes() + buffer.size()); - } - addr = nextAddr; - } - } - - // Null terminate, and pad to pointer size for this arch. - file.functionStarts.push_back(0); - - auto size = file.functionStarts.size(); - for (unsigned i = size, e = llvm::alignTo(size, _ctx.is64Bit() ? 8 : 4); - i != e; ++i) - file.functionStarts.push_back(0); -} - -void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) { - if (!_ctx.generateDataInCodeLoadCommand()) - return; - for (SectionInfo *si : _sectionInfos) { - for (const AtomInfo &info : si->atomsAndOffsets) { - // Atoms that contain data-in-code have "transition" references - // which mark a point where the embedded data starts of ends. - // This needs to be converted to the mach-o format which is an array - // of data-in-code ranges. - uint32_t startOffset = 0; - DataRegionType mode = DataRegionType(0); - for (const Reference *ref : *info.atom) { - if (ref->kindNamespace() != Reference::KindNamespace::mach_o) - continue; - if (_archHandler.isDataInCodeTransition(ref->kindValue())) { - DataRegionType nextMode = (DataRegionType)ref->addend(); - if (mode != nextMode) { - if (mode != 0) { - // Found end data range, so make range entry. - DataInCode entry; - entry.offset = si->address + info.offsetInSection + startOffset; - entry.length = ref->offsetInAtom() - startOffset; - entry.kind = mode; - file.dataInCode.push_back(entry); - } - } - mode = nextMode; - startOffset = ref->offsetInAtom(); - } - } - if (mode != 0) { - // Function ends with data (no end transition). - DataInCode entry; - entry.offset = si->address + info.offsetInSection + startOffset; - entry.length = info.atom->size() - startOffset; - entry.kind = mode; - file.dataInCode.push_back(entry); - } - } - } -} - -void Util::addRebaseAndBindingInfo(const lld::File &atomFile, - NormalizedFile &nFile) { - if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) - return; - - uint8_t segmentIndex; - uint64_t segmentStartAddr; - uint32_t offsetInBindInfo = 0; - - for (SectionInfo *sect : _sectionInfos) { - segIndexForSection(sect, segmentIndex, segmentStartAddr); - for (const AtomInfo &info : sect->atomsAndOffsets) { - const DefinedAtom *atom = info.atom; - for (const Reference *ref : *atom) { - uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom() - - segmentStartAddr; - const Atom* targ = ref->target(); - if (_archHandler.isPointer(*ref)) { - // A pointer to a DefinedAtom requires rebasing. - if (isa<DefinedAtom>(targ)) { - RebaseLocation rebase; - rebase.segIndex = segmentIndex; - rebase.segOffset = segmentOffset; - rebase.kind = llvm::MachO::REBASE_TYPE_POINTER; - nFile.rebasingInfo.push_back(rebase); - } - // A pointer to an SharedLibraryAtom requires binding. - if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) { - BindLocation bind; - bind.segIndex = segmentIndex; - bind.segOffset = segmentOffset; - bind.kind = llvm::MachO::BIND_TYPE_POINTER; - bind.canBeNull = sa->canBeNullAtRuntime(); - bind.ordinal = dylibOrdinal(sa); - bind.symbolName = targ->name(); - bind.addend = ref->addend(); - nFile.bindingInfo.push_back(bind); - } - } - else if (_archHandler.isLazyPointer(*ref)) { - BindLocation bind; - if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) { - bind.ordinal = dylibOrdinal(sa); - } else { - bind.ordinal = llvm::MachO::BIND_SPECIAL_DYLIB_SELF; - } - bind.segIndex = segmentIndex; - bind.segOffset = segmentOffset; - bind.kind = llvm::MachO::BIND_TYPE_POINTER; - bind.canBeNull = false; //sa->canBeNullAtRuntime(); - bind.symbolName = targ->name(); - bind.addend = ref->addend(); - nFile.lazyBindingInfo.push_back(bind); - - // Now that we know the segmentOffset and the ordinal attribute, - // we can fix the helper's code - - fixLazyReferenceImm(atom, offsetInBindInfo, nFile); - - // 5 bytes for opcodes + variable sizes (target name + \0 and offset - // encode's size) - offsetInBindInfo += - 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset); - if (bind.ordinal > BIND_IMMEDIATE_MASK) - offsetInBindInfo += llvm::getULEB128Size(bind.ordinal); - } - } - } - } -} - -void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, - NormalizedFile &file) { - for (const Reference *ref : *atom) { - const DefinedAtom *da = dyn_cast<DefinedAtom>(ref->target()); - if (da == nullptr) - return; - - const Reference *helperRef = nullptr; - for (const Reference *hr : *da) { - if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) { - helperRef = hr; - break; - } - } - if (helperRef == nullptr) - continue; - - // TODO: maybe get the fixed atom content from _archHandler ? - for (SectionInfo *sectInfo : _sectionInfos) { - for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) { - if (atomInfo.atom == helperRef->target()) { - auto sectionContent = - file.sections[sectInfo->normalizedSectionIndex].content; - uint8_t *rawb = - file.ownedAllocations.Allocate<uint8_t>(sectionContent.size()); - llvm::MutableArrayRef<uint8_t> newContent{rawb, - sectionContent.size()}; - std::copy(sectionContent.begin(), sectionContent.end(), - newContent.begin()); - llvm::support::ulittle32_t *loc = - reinterpret_cast<llvm::support::ulittle32_t *>( - &newContent[atomInfo.offsetInSection + - helperRef->offsetInAtom()]); - *loc = offset; - file.sections[sectInfo->normalizedSectionIndex].content = newContent; - } - } - } - } -} - -void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { - if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) - return; - - for (SectionInfo *sect : _sectionInfos) { - for (const AtomInfo &info : sect->atomsAndOffsets) { - const DefinedAtom *atom = info.atom; - if (atom->scope() != Atom::scopeGlobal) - continue; - if (_ctx.exportRestrictMode()) { - if (!_ctx.exportSymbolNamed(atom->name())) - continue; - } - Export exprt; - exprt.name = atom->name(); - exprt.offset = _atomToAddress[atom] - _ctx.baseAddress(); - exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR; - if (atom->merge() == DefinedAtom::mergeAsWeak) - exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - else - exprt.flags = 0; - exprt.otherOffset = 0; - exprt.otherName = StringRef(); - nFile.exportInfo.push_back(exprt); - } - } -} - -uint32_t Util::fileFlags() { - // FIXME: these need to determined at runtime. - if (_ctx.outputMachOType() == MH_OBJECT) { - return _subsectionsViaSymbols ? (uint32_t)MH_SUBSECTIONS_VIA_SYMBOLS : 0; - } else { - uint32_t flags = MH_DYLDLINK; - if (!_ctx.useFlatNamespace()) - flags |= MH_TWOLEVEL | MH_NOUNDEFS; - if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE()) - flags |= MH_PIE; - if (_hasTLVDescriptors) - flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS); - return flags; - } -} - -} // end anonymous namespace - -namespace lld { -namespace mach_o { -namespace normalized { - -/// Convert a set of Atoms into a normalized mach-o file. -llvm::Expected<std::unique_ptr<NormalizedFile>> -normalizedFromAtoms(const lld::File &atomFile, - const MachOLinkingContext &context) { - // The util object buffers info until the normalized file can be made. - Util util(context); - util.processDefinedAtoms(atomFile); - util.organizeSections(); - - std::unique_ptr<NormalizedFile> f(new NormalizedFile()); - NormalizedFile &normFile = *f.get(); - normFile.arch = context.arch(); - normFile.fileType = context.outputMachOType(); - normFile.flags = util.fileFlags(); - normFile.stackSize = context.stackSize(); - normFile.installName = context.installName(); - normFile.currentVersion = context.currentVersion(); - normFile.compatVersion = context.compatibilityVersion(); - normFile.os = context.os(); - - // If we are emitting an object file, then the min version is the maximum - // of the min's of all the source files and the cmdline. - if (normFile.fileType == llvm::MachO::MH_OBJECT) - normFile.minOSverson = std::max(context.osMinVersion(), util.minVersion()); - else - normFile.minOSverson = context.osMinVersion(); - - normFile.minOSVersionKind = util.minVersionCommandType(); - - normFile.sdkVersion = context.sdkVersion(); - normFile.sourceVersion = context.sourceVersion(); - - if (context.generateVersionLoadCommand() && - context.os() != MachOLinkingContext::OS::unknown) - normFile.hasMinVersionLoadCommand = true; - else if (normFile.fileType == llvm::MachO::MH_OBJECT && - util.allSourceFilesHaveMinVersions() && - ((normFile.os != MachOLinkingContext::OS::unknown) || - util.minVersionCommandType())) { - // If we emit an object file, then it should contain a min version load - // command if all of the source files also contained min version commands. - // Also, we either need to have a platform, or found a platform from the - // source object files. - normFile.hasMinVersionLoadCommand = true; - } - normFile.generateDataInCodeLoadCommand = - context.generateDataInCodeLoadCommand(); - normFile.pageSize = context.pageSize(); - normFile.rpaths = context.rpaths(); - util.addDependentDylibs(atomFile, normFile); - util.copySegmentInfo(normFile); - util.copySectionInfo(normFile); - util.assignAddressesToSections(normFile); - util.buildAtomToAddressMap(); - if (auto err = util.synthesizeDebugNotes(normFile)) - return std::move(err); - util.updateSectionInfo(normFile); - util.copySectionContent(normFile); - if (auto ec = util.addSymbols(atomFile, normFile)) { - return std::move(ec); - } - util.addIndirectSymbols(atomFile, normFile); - util.addRebaseAndBindingInfo(atomFile, normFile); - util.addExportInfo(atomFile, normFile); - util.addSectionRelocs(atomFile, normFile); - util.addFunctionStarts(atomFile, normFile); - util.buildDataInCodeArray(atomFile, normFile); - util.copyEntryPointAddress(normFile); - - return std::move(f); -} - -} // namespace normalized -} // namespace mach_o -} // namespace lld diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp deleted file mode 100644 index 164a283b972b..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ /dev/null @@ -1,1635 +0,0 @@ -//===- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.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 Converts from in-memory normalized mach-o to in-memory Atoms. -/// -/// +------------+ -/// | normalized | -/// +------------+ -/// | -/// | -/// v -/// +-------+ -/// | Atoms | -/// +-------+ - -#include "ArchHandler.h" -#include "Atoms.h" -#include "File.h" -#include "MachONormalizedFile.h" -#include "MachONormalizedFileBinaryUtils.h" -#include "lld/Common/LLVM.h" -#include "lld/Core/Error.h" -#include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/Support/DataExtractor.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm::MachO; -using namespace lld::mach_o::normalized; - -#define DEBUG_TYPE "normalized-file-to-atoms" - -namespace lld { -namespace mach_o { - - -namespace { // anonymous - - -#define ENTRY(seg, sect, type, atomType) \ - {seg, sect, type, DefinedAtom::atomType } - -struct MachORelocatableSectionToAtomType { - StringRef segmentName; - StringRef sectionName; - SectionType sectionType; - DefinedAtom::ContentType atomType; -}; - -const MachORelocatableSectionToAtomType sectsToAtomType[] = { - ENTRY("__TEXT", "__text", S_REGULAR, typeCode), - ENTRY("__TEXT", "__text", S_REGULAR, typeResolver), - ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString), - ENTRY("", "", S_CSTRING_LITERALS, typeCString), - ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String), - ENTRY("__TEXT", "__const", S_REGULAR, typeConstant), - ENTRY("__TEXT", "__const_coal", S_COALESCED, typeConstant), - ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), - ENTRY("__TEXT", "__eh_frame", S_REGULAR, typeCFI), - ENTRY("__TEXT", "__literal4", S_4BYTE_LITERALS, typeLiteral4), - ENTRY("__TEXT", "__literal8", S_8BYTE_LITERALS, typeLiteral8), - ENTRY("__TEXT", "__literal16", S_16BYTE_LITERALS, typeLiteral16), - ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), - ENTRY("__DATA", "__data", S_REGULAR, typeData), - ENTRY("__DATA", "__datacoal_nt", S_COALESCED, typeData), - ENTRY("__DATA", "__const", S_REGULAR, typeConstData), - ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), - ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, - typeInitializerPtr), - ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS, - typeTerminatorPtr), - ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, - typeGOT), - ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), - ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS, - typeGOT), - ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), - ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, - typeThunkTLV), - ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData), - ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, - typeTLVInitialZeroFill), - ENTRY("__DATA", "__objc_imageinfo", S_REGULAR, typeObjCImageInfo), - ENTRY("__DATA", "__objc_catlist", S_REGULAR, typeObjC2CategoryList), - ENTRY("", "", S_INTERPOSING, typeInterposingTuples), - ENTRY("__LD", "__compact_unwind", S_REGULAR, - typeCompactUnwindInfo), - ENTRY("", "", S_REGULAR, typeUnknown) -}; -#undef ENTRY - - -/// Figures out ContentType of a mach-o section. -DefinedAtom::ContentType atomTypeFromSection(const Section §ion, - bool &customSectionName) { - // First look for match of name and type. Empty names in table are wildcards. - customSectionName = false; - for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ; - p->atomType != DefinedAtom::typeUnknown; ++p) { - if (p->sectionType != section.type) - continue; - if (!p->segmentName.equals(section.segmentName) && !p->segmentName.empty()) - continue; - if (!p->sectionName.equals(section.sectionName) && !p->sectionName.empty()) - continue; - customSectionName = p->segmentName.empty() && p->sectionName.empty(); - return p->atomType; - } - // Look for code denoted by section attributes - if (section.attributes & S_ATTR_PURE_INSTRUCTIONS) - return DefinedAtom::typeCode; - - return DefinedAtom::typeUnknown; -} - -enum AtomizeModel { - atomizeAtSymbols, - atomizeFixedSize, - atomizePointerSize, - atomizeUTF8, - atomizeUTF16, - atomizeCFI, - atomizeCU, - atomizeCFString -}; - -/// Returns info on how to atomize a section of the specified ContentType. -void sectionParseInfo(DefinedAtom::ContentType atomType, - unsigned int &sizeMultiple, - DefinedAtom::Scope &scope, - DefinedAtom::Merge &merge, - AtomizeModel &atomizeModel) { - struct ParseInfo { - DefinedAtom::ContentType atomType; - unsigned int sizeMultiple; - DefinedAtom::Scope scope; - DefinedAtom::Merge merge; - AtomizeModel atomizeModel; - }; - - #define ENTRY(type, size, scope, merge, model) \ - {DefinedAtom::type, size, DefinedAtom::scope, DefinedAtom::merge, model } - - static const ParseInfo parseInfo[] = { - ENTRY(typeCode, 1, scopeGlobal, mergeNo, - atomizeAtSymbols), - ENTRY(typeData, 1, scopeGlobal, mergeNo, - atomizeAtSymbols), - ENTRY(typeConstData, 1, scopeGlobal, mergeNo, - atomizeAtSymbols), - ENTRY(typeZeroFill, 1, scopeGlobal, mergeNo, - atomizeAtSymbols), - ENTRY(typeConstant, 1, scopeGlobal, mergeNo, - atomizeAtSymbols), - ENTRY(typeCString, 1, scopeLinkageUnit, mergeByContent, - atomizeUTF8), - ENTRY(typeUTF16String, 1, scopeLinkageUnit, mergeByContent, - atomizeUTF16), - ENTRY(typeCFI, 4, scopeTranslationUnit, mergeNo, - atomizeCFI), - ENTRY(typeLiteral4, 4, scopeLinkageUnit, mergeByContent, - atomizeFixedSize), - ENTRY(typeLiteral8, 8, scopeLinkageUnit, mergeByContent, - atomizeFixedSize), - ENTRY(typeLiteral16, 16, scopeLinkageUnit, mergeByContent, - atomizeFixedSize), - ENTRY(typeCFString, 4, scopeLinkageUnit, mergeByContent, - atomizeCFString), - ENTRY(typeInitializerPtr, 4, scopeTranslationUnit, mergeNo, - atomizePointerSize), - ENTRY(typeTerminatorPtr, 4, scopeTranslationUnit, mergeNo, - atomizePointerSize), - ENTRY(typeCompactUnwindInfo, 4, scopeTranslationUnit, mergeNo, - atomizeCU), - ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent, - atomizePointerSize), - ENTRY(typeObjC2CategoryList, 4, scopeTranslationUnit, mergeByContent, - atomizePointerSize), - ENTRY(typeUnknown, 1, scopeGlobal, mergeNo, - atomizeAtSymbols) - }; - #undef ENTRY - const int tableLen = sizeof(parseInfo) / sizeof(ParseInfo); - for (int i=0; i < tableLen; ++i) { - if (parseInfo[i].atomType == atomType) { - sizeMultiple = parseInfo[i].sizeMultiple; - scope = parseInfo[i].scope; - merge = parseInfo[i].merge; - atomizeModel = parseInfo[i].atomizeModel; - return; - } - } - - // Unknown type is atomized by symbols. - sizeMultiple = 1; - scope = DefinedAtom::scopeGlobal; - merge = DefinedAtom::mergeNo; - atomizeModel = atomizeAtSymbols; -} - - -Atom::Scope atomScope(uint8_t scope) { - switch (scope) { - case N_EXT: - return Atom::scopeGlobal; - case N_PEXT: - case N_PEXT | N_EXT: - return Atom::scopeLinkageUnit; - case 0: - return Atom::scopeTranslationUnit; - } - llvm_unreachable("unknown scope value!"); -} - -void appendSymbolsInSection( - const std::vector<lld::mach_o::normalized::Symbol> &inSymbols, - uint32_t sectionIndex, - SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) { - for (const lld::mach_o::normalized::Symbol &sym : inSymbols) { - // Only look at definition symbols. - if ((sym.type & N_TYPE) != N_SECT) - continue; - if (sym.sect != sectionIndex) - continue; - outSyms.push_back(&sym); - } -} - -void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion, - MachOFile &file, uint64_t symbolAddr, StringRef symbolName, - uint16_t symbolDescFlags, Atom::Scope symbolScope, - uint64_t nextSymbolAddr, bool scatterable, bool copyRefs) { - // Mach-O symbol table does have size in it. Instead the size is the - // difference between this and the next symbol. - uint64_t size = nextSymbolAddr - symbolAddr; - uint64_t offset = symbolAddr - section.address; - bool noDeadStrip = (symbolDescFlags & N_NO_DEAD_STRIP) || !scatterable; - if (isZeroFillSection(section.type)) { - file.addZeroFillDefinedAtom(symbolName, symbolScope, offset, size, - noDeadStrip, copyRefs, §ion); - } else { - DefinedAtom::Merge merge = (symbolDescFlags & N_WEAK_DEF) - ? DefinedAtom::mergeAsWeak : DefinedAtom::mergeNo; - bool thumb = (symbolDescFlags & N_ARM_THUMB_DEF); - if (atomType == DefinedAtom::typeUnknown) { - // Mach-O needs a segment and section name. Concatenate those two - // with a / separator (e.g. "seg/sect") to fit into the lld model - // of just a section name. - std::string segSectName = section.segmentName.str() - + "/" + section.sectionName.str(); - file.addDefinedAtomInCustomSection(symbolName, symbolScope, atomType, - merge, thumb, noDeadStrip, offset, - size, segSectName, true, §ion); - } else { - if ((atomType == lld::DefinedAtom::typeCode) && - (symbolDescFlags & N_SYMBOL_RESOLVER)) { - atomType = lld::DefinedAtom::typeResolver; - } - file.addDefinedAtom(symbolName, symbolScope, atomType, merge, - offset, size, thumb, noDeadStrip, copyRefs, §ion); - } - } -} - -llvm::Error processSymboledSection(DefinedAtom::ContentType atomType, - const Section §ion, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { - // Find section's index. - uint32_t sectIndex = 1; - for (auto § : normalizedFile.sections) { - if (§ == §ion) - break; - ++sectIndex; - } - - // Find all symbols in this section. - SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols; - appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols); - appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols); - - // Sort symbols. - std::sort(symbols.begin(), symbols.end(), - [](const lld::mach_o::normalized::Symbol *lhs, - const lld::mach_o::normalized::Symbol *rhs) -> bool { - if (lhs == rhs) - return false; - // First by address. - uint64_t lhsAddr = lhs->value; - uint64_t rhsAddr = rhs->value; - if (lhsAddr != rhsAddr) - return lhsAddr < rhsAddr; - // If same address, one is an alias so sort by scope. - Atom::Scope lScope = atomScope(lhs->scope); - Atom::Scope rScope = atomScope(rhs->scope); - if (lScope != rScope) - return lScope < rScope; - // If same address and scope, see if one might be better as - // the alias. - bool lPrivate = (lhs->name.front() == 'l'); - bool rPrivate = (rhs->name.front() == 'l'); - if (lPrivate != rPrivate) - return lPrivate; - // If same address and scope, sort by name. - return lhs->name < rhs->name; - }); - - // Debug logging of symbols. - // for (const Symbol *sym : symbols) - // llvm::errs() << " sym: " - // << llvm::format("0x%08llx ", (uint64_t)sym->value) - // << ", " << sym->name << "\n"; - - // If section has no symbols and no content, there are no atoms. - if (symbols.empty() && section.content.empty()) - return llvm::Error::success(); - - if (symbols.empty()) { - // Section has no symbols, put all content in one anonymous atom. - atomFromSymbol(atomType, section, file, section.address, StringRef(), - 0, Atom::scopeTranslationUnit, - section.address + section.content.size(), - scatterable, copyRefs); - } - else if (symbols.front()->value != section.address) { - // Section has anonymous content before first symbol. - atomFromSymbol(atomType, section, file, section.address, StringRef(), - 0, Atom::scopeTranslationUnit, symbols.front()->value, - scatterable, copyRefs); - } - - const lld::mach_o::normalized::Symbol *lastSym = nullptr; - for (const lld::mach_o::normalized::Symbol *sym : symbols) { - if (lastSym != nullptr) { - // Ignore any assembler added "ltmpNNN" symbol at start of section - // if there is another symbol at the start. - if ((lastSym->value != sym->value) - || lastSym->value != section.address - || !lastSym->name.startswith("ltmp")) { - atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name, - lastSym->desc, atomScope(lastSym->scope), sym->value, - scatterable, copyRefs); - } - } - lastSym = sym; - } - if (lastSym != nullptr) { - atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name, - lastSym->desc, atomScope(lastSym->scope), - section.address + section.content.size(), - scatterable, copyRefs); - } - - // If object built without .subsections_via_symbols, add reference chain. - if (!scatterable) { - MachODefinedAtom *prevAtom = nullptr; - file.eachAtomInSection(section, - [&](MachODefinedAtom *atom, uint64_t offset)->void { - if (prevAtom) - prevAtom->addReference(Reference::KindNamespace::all, - Reference::KindArch::all, - Reference::kindLayoutAfter, 0, atom, 0); - prevAtom = atom; - }); - } - - return llvm::Error::success(); -} - -llvm::Error processSection(DefinedAtom::ContentType atomType, - const Section §ion, - bool customSectionName, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - - // Get info on how to atomize section. - unsigned int sizeMultiple; - DefinedAtom::Scope scope; - DefinedAtom::Merge merge; - AtomizeModel atomizeModel; - sectionParseInfo(atomType, sizeMultiple, scope, merge, atomizeModel); - - // Validate section size. - if ((section.content.size() % sizeMultiple) != 0) - return llvm::make_error<GenericError>(Twine("Section ") - + section.segmentName - + "/" + section.sectionName - + " has size (" - + Twine(section.content.size()) - + ") which is not a multiple of " - + Twine(sizeMultiple)); - - if (atomizeModel == atomizeAtSymbols) { - // Break section up into atoms each with a fixed size. - return processSymboledSection(atomType, section, normalizedFile, file, - scatterable, copyRefs); - } else { - unsigned int size; - for (unsigned int offset = 0, e = section.content.size(); offset != e;) { - switch (atomizeModel) { - case atomizeFixedSize: - // Break section up into atoms each with a fixed size. - size = sizeMultiple; - break; - case atomizePointerSize: - // Break section up into atoms each the size of a pointer. - size = is64 ? 8 : 4; - break; - case atomizeUTF8: - // Break section up into zero terminated c-strings. - size = 0; - for (unsigned int i = offset; i < e; ++i) { - if (section.content[i] == 0) { - size = i + 1 - offset; - break; - } - } - break; - case atomizeUTF16: - // Break section up into zero terminated UTF16 strings. - size = 0; - for (unsigned int i = offset; i < e; i += 2) { - if ((section.content[i] == 0) && (section.content[i + 1] == 0)) { - size = i + 2 - offset; - break; - } - } - break; - case atomizeCFI: - // Break section up into dwarf unwind CFIs (FDE or CIE). - size = read32(§ion.content[offset], isBig) + 4; - if (offset+size > section.content.size()) { - return llvm::make_error<GenericError>(Twine("Section ") - + section.segmentName - + "/" + section.sectionName - + " is malformed. Size of CFI " - "starting at offset (" - + Twine(offset) - + ") is past end of section."); - } - break; - case atomizeCU: - // Break section up into compact unwind entries. - size = is64 ? 32 : 20; - break; - case atomizeCFString: - // Break section up into NS/CFString objects. - size = is64 ? 32 : 16; - break; - case atomizeAtSymbols: - break; - } - if (size == 0) { - return llvm::make_error<GenericError>(Twine("Section ") - + section.segmentName - + "/" + section.sectionName - + " is malformed. The last atom " - "is not zero terminated."); - } - if (customSectionName) { - // Mach-O needs a segment and section name. Concatenate those two - // with a / separator (e.g. "seg/sect") to fit into the lld model - // of just a section name. - std::string segSectName = section.segmentName.str() - + "/" + section.sectionName.str(); - file.addDefinedAtomInCustomSection(StringRef(), scope, atomType, - merge, false, false, offset, - size, segSectName, true, §ion); - } else { - file.addDefinedAtom(StringRef(), scope, atomType, merge, offset, size, - false, false, copyRefs, §ion); - } - offset += size; - } - } - return llvm::Error::success(); -} - -const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile, - uint64_t address) { - for (const Section &s : normalizedFile.sections) { - uint64_t sAddr = s.address; - if ((sAddr <= address) && (address < sAddr+s.content.size())) { - return &s; - } - } - return nullptr; -} - -const MachODefinedAtom * -findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file, - uint64_t addr, Reference::Addend &addend) { - const Section *sect = nullptr; - sect = findSectionCoveringAddress(normalizedFile, addr); - if (!sect) - return nullptr; - - uint32_t offsetInTarget; - uint64_t offsetInSect = addr - sect->address; - auto atom = - file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); - addend = offsetInTarget; - return atom; -} - -// Walks all relocations for a section in a normalized .o file and -// creates corresponding lld::Reference objects. -llvm::Error convertRelocs(const Section §ion, - const NormalizedFile &normalizedFile, - bool scatterable, - MachOFile &file, - ArchHandler &handler) { - // Utility function for ArchHandler to find atom by its address. - auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr, - const lld::Atom **atom, Reference::Addend *addend) - -> llvm::Error { - if (sectIndex > normalizedFile.sections.size()) - return llvm::make_error<GenericError>(Twine("out of range section " - "index (") + Twine(sectIndex) + ")"); - const Section *sect = nullptr; - if (sectIndex == 0) { - sect = findSectionCoveringAddress(normalizedFile, addr); - if (!sect) - return llvm::make_error<GenericError>(Twine("address (" + Twine(addr) - + ") is not in any section")); - } else { - sect = &normalizedFile.sections[sectIndex-1]; - } - uint32_t offsetInTarget; - uint64_t offsetInSect = addr - sect->address; - *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); - *addend = offsetInTarget; - return llvm::Error::success(); - }; - - // Utility function for ArchHandler to find atom by its symbol index. - auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result) - -> llvm::Error { - // Find symbol from index. - const lld::mach_o::normalized::Symbol *sym = nullptr; - uint32_t numStabs = normalizedFile.stabsSymbols.size(); - uint32_t numLocal = normalizedFile.localSymbols.size(); - uint32_t numGlobal = normalizedFile.globalSymbols.size(); - uint32_t numUndef = normalizedFile.undefinedSymbols.size(); - assert(symbolIndex >= numStabs && "Searched for stab via atomBySymbol?"); - if (symbolIndex < numStabs+numLocal) { - sym = &normalizedFile.localSymbols[symbolIndex-numStabs]; - } else if (symbolIndex < numStabs+numLocal+numGlobal) { - sym = &normalizedFile.globalSymbols[symbolIndex-numStabs-numLocal]; - } else if (symbolIndex < numStabs+numLocal+numGlobal+numUndef) { - sym = &normalizedFile.undefinedSymbols[symbolIndex-numStabs-numLocal- - numGlobal]; - } else { - return llvm::make_error<GenericError>(Twine("symbol index (") - + Twine(symbolIndex) + ") out of range"); - } - - // Find atom from symbol. - if ((sym->type & N_TYPE) == N_SECT) { - if (sym->sect > normalizedFile.sections.size()) - return llvm::make_error<GenericError>(Twine("symbol section index (") - + Twine(sym->sect) + ") out of range "); - const Section &symSection = normalizedFile.sections[sym->sect-1]; - uint64_t targetOffsetInSect = sym->value - symSection.address; - MachODefinedAtom *target = file.findAtomCoveringAddress(symSection, - targetOffsetInSect); - if (target) { - *result = target; - return llvm::Error::success(); - } - return llvm::make_error<GenericError>("no atom found for defined symbol"); - } else if ((sym->type & N_TYPE) == N_UNDF) { - const lld::Atom *target = file.findUndefAtom(sym->name); - if (target) { - *result = target; - return llvm::Error::success(); - } - return llvm::make_error<GenericError>("no undefined atom found for sym"); - } else { - // Search undefs - return llvm::make_error<GenericError>("no atom found for symbol"); - } - }; - - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - // Use old-school iterator so that paired relocations can be grouped. - for (auto it=section.relocations.begin(), e=section.relocations.end(); - it != e; ++it) { - const Relocation &reloc = *it; - // Find atom this relocation is in. - if (reloc.offset > section.content.size()) - return llvm::make_error<GenericError>( - Twine("r_address (") + Twine(reloc.offset) - + ") is larger than section size (" - + Twine(section.content.size()) + ")"); - uint32_t offsetInAtom; - MachODefinedAtom *inAtom = file.findAtomCoveringAddress(section, - reloc.offset, - &offsetInAtom); - assert(inAtom && "r_address in range, should have found atom"); - uint64_t fixupAddress = section.address + reloc.offset; - - const lld::Atom *target = nullptr; - Reference::Addend addend = 0; - Reference::KindValue kind; - if (handler.isPairedReloc(reloc)) { - // Handle paired relocations together. - const Relocation &reloc2 = *++it; - auto relocErr = handler.getPairReferenceInfo( - reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, - atomByAddr, atomBySymbol, &kind, &target, &addend); - if (relocErr) { - return handleErrors(std::move(relocErr), - [&](std::unique_ptr<GenericError> GE) { - return llvm::make_error<GenericError>( - Twine("bad relocation (") + GE->getMessage() - + ") in section " - + section.segmentName + "/" + section.sectionName - + " (r1_address=" + Twine::utohexstr(reloc.offset) - + ", r1_type=" + Twine(reloc.type) - + ", r1_extern=" + Twine(reloc.isExtern) - + ", r1_length=" + Twine((int)reloc.length) - + ", r1_pcrel=" + Twine(reloc.pcRel) - + (!reloc.scattered ? (Twine(", r1_symbolnum=") - + Twine(reloc.symbol)) - : (Twine(", r1_scattered=1, r1_value=") - + Twine(reloc.value))) - + ")" - + ", (r2_address=" + Twine::utohexstr(reloc2.offset) - + ", r2_type=" + Twine(reloc2.type) - + ", r2_extern=" + Twine(reloc2.isExtern) - + ", r2_length=" + Twine((int)reloc2.length) - + ", r2_pcrel=" + Twine(reloc2.pcRel) - + (!reloc2.scattered ? (Twine(", r2_symbolnum=") - + Twine(reloc2.symbol)) - : (Twine(", r2_scattered=1, r2_value=") - + Twine(reloc2.value))) - + ")" ); - }); - } - } - else { - // Use ArchHandler to convert relocation record into information - // needed to instantiate an lld::Reference object. - auto relocErr = handler.getReferenceInfo( - reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr, - atomBySymbol, &kind, &target, &addend); - if (relocErr) { - return handleErrors(std::move(relocErr), - [&](std::unique_ptr<GenericError> GE) { - return llvm::make_error<GenericError>( - Twine("bad relocation (") + GE->getMessage() - + ") in section " - + section.segmentName + "/" + section.sectionName - + " (r_address=" + Twine::utohexstr(reloc.offset) - + ", r_type=" + Twine(reloc.type) - + ", r_extern=" + Twine(reloc.isExtern) - + ", r_length=" + Twine((int)reloc.length) - + ", r_pcrel=" + Twine(reloc.pcRel) - + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) - : (Twine(", r_scattered=1, r_value=") - + Twine(reloc.value))) - + ")" ); - }); - } - } - // Instantiate an lld::Reference object and add to its atom. - inAtom->addReference(Reference::KindNamespace::mach_o, - handler.kindArch(), - kind, offsetInAtom, target, addend); - } - - return llvm::Error::success(); -} - -bool isDebugInfoSection(const Section §ion) { - if ((section.attributes & S_ATTR_DEBUG) == 0) - return false; - return section.segmentName.equals("__DWARF"); -} - -static const Atom* findDefinedAtomByName(MachOFile &file, Twine name) { - std::string strName = name.str(); - for (auto *atom : file.defined()) - if (atom->name() == strName) - return atom; - return nullptr; -} - -static StringRef copyDebugString(StringRef str, BumpPtrAllocator &alloc) { - char *strCopy = alloc.Allocate<char>(str.size() + 1); - memcpy(strCopy, str.data(), str.size()); - strCopy[str.size()] = '\0'; - return strCopy; -} - -llvm::Error parseStabs(MachOFile &file, - const NormalizedFile &normalizedFile, - bool copyRefs) { - - if (normalizedFile.stabsSymbols.empty()) - return llvm::Error::success(); - - // FIXME: Kill this off when we can move to sane yaml parsing. - std::unique_ptr<BumpPtrAllocator> allocator; - if (copyRefs) - allocator = std::make_unique<BumpPtrAllocator>(); - - enum { start, inBeginEnd } state = start; - - const Atom *currentAtom = nullptr; - uint64_t currentAtomAddress = 0; - StabsDebugInfo::StabsList stabsList; - for (const auto &stabSym : normalizedFile.stabsSymbols) { - Stab stab(nullptr, stabSym.type, stabSym.sect, stabSym.desc, - stabSym.value, stabSym.name); - switch (state) { - case start: - switch (static_cast<StabType>(stabSym.type)) { - case N_BNSYM: - state = inBeginEnd; - currentAtomAddress = stabSym.value; - Reference::Addend addend; - currentAtom = findAtomCoveringAddress(normalizedFile, file, - currentAtomAddress, addend); - if (addend != 0) - return llvm::make_error<GenericError>( - "Non-zero addend for BNSYM '" + stabSym.name + "' in " + - file.path()); - if (currentAtom) - stab.atom = currentAtom; - else { - // FIXME: ld64 just issues a warning here - should we match that? - return llvm::make_error<GenericError>( - "can't find atom for stabs BNSYM at " + - Twine::utohexstr(stabSym.value) + " in " + file.path()); - } - break; - case N_SO: - case N_OSO: - // Not associated with an atom, just copy. - if (copyRefs) - stab.str = copyDebugString(stabSym.name, *allocator); - else - stab.str = stabSym.name; - break; - case N_GSYM: { - auto colonIdx = stabSym.name.find(':'); - if (colonIdx != StringRef::npos) { - StringRef name = stabSym.name.substr(0, colonIdx); - currentAtom = findDefinedAtomByName(file, "_" + name); - stab.atom = currentAtom; - if (copyRefs) - stab.str = copyDebugString(stabSym.name, *allocator); - else - stab.str = stabSym.name; - } else { - currentAtom = findDefinedAtomByName(file, stabSym.name); - stab.atom = currentAtom; - if (copyRefs) - stab.str = copyDebugString(stabSym.name, *allocator); - else - stab.str = stabSym.name; - } - if (stab.atom == nullptr) - return llvm::make_error<GenericError>( - "can't find atom for N_GSYM stabs" + stabSym.name + - " in " + file.path()); - break; - } - case N_FUN: - return llvm::make_error<GenericError>( - "old-style N_FUN stab '" + stabSym.name + "' unsupported"); - default: - return llvm::make_error<GenericError>( - "unrecognized stab symbol '" + stabSym.name + "'"); - } - break; - case inBeginEnd: - stab.atom = currentAtom; - switch (static_cast<StabType>(stabSym.type)) { - case N_ENSYM: - state = start; - currentAtom = nullptr; - break; - case N_FUN: - // Just copy the string. - if (copyRefs) - stab.str = copyDebugString(stabSym.name, *allocator); - else - stab.str = stabSym.name; - break; - default: - return llvm::make_error<GenericError>( - "unrecognized stab symbol '" + stabSym.name + "'"); - } - } - llvm::dbgs() << "Adding to stabsList: " << stab << "\n"; - stabsList.push_back(stab); - } - - file.setDebugInfo(std::make_unique<StabsDebugInfo>(std::move(stabsList))); - - // FIXME: Kill this off when we fix YAML memory ownership. - file.debugInfo()->setAllocator(std::move(allocator)); - - return llvm::Error::success(); -} - -static llvm::DataExtractor -dataExtractorFromSection(const NormalizedFile &normalizedFile, - const Section &S) { - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - StringRef SecData(reinterpret_cast<const char*>(S.content.data()), - S.content.size()); - return llvm::DataExtractor(SecData, !isBig, is64 ? 8 : 4); -} - -// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE -// inspection" code if possible. -static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData, - uint64_t abbrCode) { - uint64_t offset = 0; - while (abbrevData.getULEB128(&offset) != abbrCode) { - // Tag - abbrevData.getULEB128(&offset); - // DW_CHILDREN - abbrevData.getU8(&offset); - // Attributes - while (abbrevData.getULEB128(&offset) | abbrevData.getULEB128(&offset)) - ; - } - return offset; -} - -// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE -// inspection" code if possible. -static Expected<const char *> -getIndexedString(const NormalizedFile &normalizedFile, - llvm::dwarf::Form form, llvm::DataExtractor infoData, - uint64_t &infoOffset, const Section &stringsSection) { - if (form == llvm::dwarf::DW_FORM_string) - return infoData.getCStr(&infoOffset); - if (form != llvm::dwarf::DW_FORM_strp) - return llvm::make_error<GenericError>( - "string field encoded without DW_FORM_strp"); - uint64_t stringOffset = infoData.getU32(&infoOffset); - llvm::DataExtractor stringsData = - dataExtractorFromSection(normalizedFile, stringsSection); - return stringsData.getCStr(&stringOffset); -} - -// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE -// inspection" code if possible. -static llvm::Expected<TranslationUnitSource> -readCompUnit(const NormalizedFile &normalizedFile, - const Section &info, - const Section &abbrev, - const Section &strings, - StringRef path) { - // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE - // inspection" code if possible. - uint64_t offset = 0; - llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32; - auto infoData = dataExtractorFromSection(normalizedFile, info); - uint32_t length = infoData.getU32(&offset); - if (length == llvm::dwarf::DW_LENGTH_DWARF64) { - Format = llvm::dwarf::DwarfFormat::DWARF64; - infoData.getU64(&offset); - } - else if (length >= llvm::dwarf::DW_LENGTH_lo_reserved) - return llvm::make_error<GenericError>("Malformed DWARF in " + path); - - uint16_t version = infoData.getU16(&offset); - - if (version < 2 || version > 4) - return llvm::make_error<GenericError>("Unsupported DWARF version in " + - path); - - infoData.getU32(&offset); // Abbrev offset (should be zero) - uint8_t addrSize = infoData.getU8(&offset); - - uint32_t abbrCode = infoData.getULEB128(&offset); - auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev); - uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode); - uint64_t tag = abbrevData.getULEB128(&abbrevOffset); - if (tag != llvm::dwarf::DW_TAG_compile_unit) - return llvm::make_error<GenericError>("top level DIE is not a compile unit"); - // DW_CHILDREN - abbrevData.getU8(&abbrevOffset); - uint32_t name; - llvm::dwarf::Form form; - llvm::dwarf::FormParams formParams = {version, addrSize, Format}; - TranslationUnitSource tu; - while ((name = abbrevData.getULEB128(&abbrevOffset)) | - (form = static_cast<llvm::dwarf::Form>( - abbrevData.getULEB128(&abbrevOffset))) && - (name != 0 || form != 0)) { - switch (name) { - case llvm::dwarf::DW_AT_name: { - if (auto eName = getIndexedString(normalizedFile, form, infoData, offset, - strings)) - tu.name = *eName; - else - return eName.takeError(); - break; - } - case llvm::dwarf::DW_AT_comp_dir: { - if (auto eName = getIndexedString(normalizedFile, form, infoData, offset, - strings)) - tu.path = *eName; - else - return eName.takeError(); - break; - } - default: - llvm::DWARFFormValue::skipValue(form, infoData, &offset, formParams); - } - } - return tu; -} - -llvm::Error parseDebugInfo(MachOFile &file, - const NormalizedFile &normalizedFile, bool copyRefs) { - - // Find the interesting debug info sections. - const Section *debugInfo = nullptr; - const Section *debugAbbrev = nullptr; - const Section *debugStrings = nullptr; - - for (auto &s : normalizedFile.sections) { - if (s.segmentName == "__DWARF") { - if (s.sectionName == "__debug_info") - debugInfo = &s; - else if (s.sectionName == "__debug_abbrev") - debugAbbrev = &s; - else if (s.sectionName == "__debug_str") - debugStrings = &s; - } - } - - if (!debugInfo) - return parseStabs(file, normalizedFile, copyRefs); - - if (debugInfo->content.size() == 0) - return llvm::Error::success(); - - if (debugInfo->content.size() < 12) - return llvm::make_error<GenericError>("Malformed __debug_info section in " + - file.path() + ": too small"); - - if (!debugAbbrev) - return llvm::make_error<GenericError>("Missing __dwarf_abbrev section in " + - file.path()); - - if (auto tuOrErr = readCompUnit(normalizedFile, *debugInfo, *debugAbbrev, - *debugStrings, file.path())) { - // FIXME: Kill of allocator and code under 'copyRefs' when we fix YAML - // memory ownership. - std::unique_ptr<BumpPtrAllocator> allocator; - if (copyRefs) { - allocator = std::make_unique<BumpPtrAllocator>(); - tuOrErr->name = copyDebugString(tuOrErr->name, *allocator); - tuOrErr->path = copyDebugString(tuOrErr->path, *allocator); - } - file.setDebugInfo(std::make_unique<DwarfDebugInfo>(std::move(*tuOrErr))); - if (copyRefs) - file.debugInfo()->setAllocator(std::move(allocator)); - } else - return tuOrErr.takeError(); - - return llvm::Error::success(); -} - -static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { - if (is64) - return read64(addr, isBig); - - int32_t res = read32(addr, isBig); - return res; -} - -/// --- Augmentation String Processing --- - -struct CIEInfo { - bool _augmentationDataPresent = false; - bool _mayHaveEH = false; - uint32_t _offsetOfLSDA = ~0U; - uint32_t _offsetOfPersonality = ~0U; - uint32_t _offsetOfFDEPointerEncoding = ~0U; - uint32_t _augmentationDataLength = ~0U; -}; - -typedef llvm::DenseMap<const MachODefinedAtom*, CIEInfo> CIEInfoMap; - -static llvm::Error processAugmentationString(const uint8_t *augStr, - CIEInfo &cieInfo, - unsigned &len) { - - if (augStr[0] == '\0') { - len = 1; - return llvm::Error::success(); - } - - if (augStr[0] != 'z') - return llvm::make_error<GenericError>("expected 'z' at start of " - "augmentation string"); - - cieInfo._augmentationDataPresent = true; - uint64_t idx = 1; - - uint32_t offsetInAugmentationData = 0; - while (augStr[idx] != '\0') { - if (augStr[idx] == 'L') { - cieInfo._offsetOfLSDA = offsetInAugmentationData; - // This adds a single byte to the augmentation data. - ++offsetInAugmentationData; - ++idx; - continue; - } - if (augStr[idx] == 'P') { - cieInfo._offsetOfPersonality = offsetInAugmentationData; - // This adds a single byte to the augmentation data for the encoding, - // then a number of bytes for the pointer data. - // FIXME: We are assuming 4 is correct here for the pointer size as we - // always currently use delta32ToGOT. - offsetInAugmentationData += 5; - ++idx; - continue; - } - if (augStr[idx] == 'R') { - cieInfo._offsetOfFDEPointerEncoding = offsetInAugmentationData; - // This adds a single byte to the augmentation data. - ++offsetInAugmentationData; - ++idx; - continue; - } - if (augStr[idx] == 'e') { - if (augStr[idx + 1] != 'h') - return llvm::make_error<GenericError>("expected 'eh' in " - "augmentation string"); - cieInfo._mayHaveEH = true; - idx += 2; - continue; - } - ++idx; - } - - cieInfo._augmentationDataLength = offsetInAugmentationData; - - len = idx + 1; - return llvm::Error::success(); -} - -static llvm::Error processCIE(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler, - const Section *ehFrameSection, - MachODefinedAtom *atom, - uint64_t offset, - CIEInfoMap &cieInfos) { - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - const uint8_t *frameData = atom->rawContent().data(); - - CIEInfo cieInfo; - - uint32_t size = read32(frameData, isBig); - uint64_t cieIDField = size == 0xffffffffU - ? sizeof(uint32_t) + sizeof(uint64_t) - : sizeof(uint32_t); - uint64_t versionField = cieIDField + sizeof(uint32_t); - uint64_t augmentationStringField = versionField + sizeof(uint8_t); - - unsigned augmentationStringLength = 0; - if (auto err = processAugmentationString(frameData + augmentationStringField, - cieInfo, augmentationStringLength)) - return err; - - if (cieInfo._offsetOfPersonality != ~0U) { - // If we have augmentation data for the personality function, then we may - // need to implicitly generate its relocation. - - // Parse the EH Data field which is pointer sized. - uint64_t EHDataField = augmentationStringField + augmentationStringLength; - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); - unsigned EHDataFieldSize = (cieInfo._mayHaveEH ? (is64 ? 8 : 4) : 0); - - // Parse Code Align Factor which is a ULEB128. - uint64_t CodeAlignField = EHDataField + EHDataFieldSize; - unsigned lengthFieldSize = 0; - llvm::decodeULEB128(frameData + CodeAlignField, &lengthFieldSize); - - // Parse Data Align Factor which is a SLEB128. - uint64_t DataAlignField = CodeAlignField + lengthFieldSize; - llvm::decodeSLEB128(frameData + DataAlignField, &lengthFieldSize); - - // Parse Return Address Register which is a byte. - uint64_t ReturnAddressField = DataAlignField + lengthFieldSize; - - // Parse the augmentation length which is a ULEB128. - uint64_t AugmentationLengthField = ReturnAddressField + 1; - uint64_t AugmentationLength = - llvm::decodeULEB128(frameData + AugmentationLengthField, - &lengthFieldSize); - - if (AugmentationLength != cieInfo._augmentationDataLength) - return llvm::make_error<GenericError>("CIE augmentation data length " - "mismatch"); - - // Get the start address of the augmentation data. - uint64_t AugmentationDataField = AugmentationLengthField + lengthFieldSize; - - // Parse the personality function from the augmentation data. - uint64_t PersonalityField = - AugmentationDataField + cieInfo._offsetOfPersonality; - - // Parse the personality encoding. - // FIXME: Verify that this is a 32-bit pcrel offset. - uint64_t PersonalityFunctionField = PersonalityField + 1; - - if (atom->begin() != atom->end()) { - // If we have an explicit relocation, then make sure it matches this - // offset as this is where we'd expect it to be applied to. - DefinedAtom::reference_iterator CurrentRef = atom->begin(); - if (CurrentRef->offsetInAtom() != PersonalityFunctionField) - return llvm::make_error<GenericError>("CIE personality reloc at " - "wrong offset"); - - if (++CurrentRef != atom->end()) - return llvm::make_error<GenericError>("CIE contains too many relocs"); - } else { - // Implicitly generate the personality function reloc. It's assumed to - // be a delta32 offset to a GOT entry. - // FIXME: Parse the encoding and check this. - int32_t funcDelta = read32(frameData + PersonalityFunctionField, isBig); - uint64_t funcAddress = ehFrameSection->address + offset + - PersonalityFunctionField; - funcAddress += funcDelta; - - const MachODefinedAtom *func = nullptr; - Reference::Addend addend; - func = findAtomCoveringAddress(normalizedFile, file, funcAddress, - addend); - atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(), - handler.unwindRefToPersonalityFunctionKind(), - PersonalityFunctionField, func, addend); - } - } else if (atom->begin() != atom->end()) { - // Otherwise, we expect there to be no relocations in this atom as the only - // relocation would have been to the personality function. - return llvm::make_error<GenericError>("unexpected relocation in CIE"); - } - - - cieInfos[atom] = std::move(cieInfo); - - return llvm::Error::success(); -} - -static llvm::Error processFDE(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler, - const Section *ehFrameSection, - MachODefinedAtom *atom, - uint64_t offset, - const CIEInfoMap &cieInfos) { - - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); - - // Compiler wasn't lazy and actually told us what it meant. - // Unfortunately, the compiler may not have generated references for all of - // [cie, func, lsda] and so we still need to parse the FDE and add references - // for any the compiler didn't generate. - if (atom->begin() != atom->end()) - atom->sortReferences(); - - DefinedAtom::reference_iterator CurrentRef = atom->begin(); - - // This helper returns the reference (if one exists) at the offset we are - // currently processing. It automatically increments the ref iterator if we - // do return a ref, and throws an error if we pass over a ref without - // comsuming it. - auto currentRefGetter = [&CurrentRef, - &atom](uint64_t Offset)->const Reference* { - // If there are no more refs found, then we are done. - if (CurrentRef == atom->end()) - return nullptr; - - const Reference *Ref = *CurrentRef; - - // If we haven't reached the offset for this reference, then return that - // we don't yet have a reference to process. - if (Offset < Ref->offsetInAtom()) - return nullptr; - - // If the offset is equal, then we want to process this ref. - if (Offset == Ref->offsetInAtom()) { - ++CurrentRef; - return Ref; - } - - // The current ref is at an offset which is earlier than the current - // offset, then we failed to consume it when we should have. In this case - // throw an error. - llvm::report_fatal_error("Skipped reference when processing FDE"); - }; - - // Helper to either get the reference at this current location, and verify - // that it is of the expected type, or add a reference of that type. - // Returns the reference target. - auto verifyOrAddReference = [&](uint64_t targetAddress, - Reference::KindValue refKind, - uint64_t refAddress, - bool allowsAddend)->const Atom* { - if (auto *ref = currentRefGetter(refAddress)) { - // The compiler already emitted a relocation for the CIE ref. This should - // have been converted to the correct type of reference in - // get[Pair]ReferenceInfo(). - assert(ref->kindValue() == refKind && - "Incorrect EHFrame reference kind"); - return ref->target(); - } - Reference::Addend addend; - auto *target = findAtomCoveringAddress(normalizedFile, file, - targetAddress, addend); - atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(), - refKind, refAddress, target, addend); - - if (!allowsAddend) - assert(!addend && "EHFrame reference cannot have addend"); - return target; - }; - - const uint8_t *startFrameData = atom->rawContent().data(); - const uint8_t *frameData = startFrameData; - - uint32_t size = read32(frameData, isBig); - uint64_t cieFieldInFDE = size == 0xffffffffU - ? sizeof(uint32_t) + sizeof(uint64_t) - : sizeof(uint32_t); - - // Linker needs to fixup a reference from the FDE to its parent CIE (a - // 32-bit byte offset backwards in the __eh_frame section). - uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); - uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; - cieAddress -= cieDelta; - - auto *cieRefTarget = verifyOrAddReference(cieAddress, - handler.unwindRefToCIEKind(), - cieFieldInFDE, false); - const MachODefinedAtom *cie = dyn_cast<MachODefinedAtom>(cieRefTarget); - assert(cie && cie->contentType() == DefinedAtom::typeCFI && - "FDE's CIE field does not point at the start of a CIE."); - - const CIEInfo &cieInfo = cieInfos.find(cie)->second; - - // Linker needs to fixup reference from the FDE to the function it's - // describing. FIXME: there are actually different ways to do this, and the - // particular method used is specified in the CIE's augmentation fields - // (hopefully) - uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); - - int64_t functionFromFDE = readSPtr(is64, isBig, - frameData + rangeFieldInFDE); - uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; - rangeStart += functionFromFDE; - - verifyOrAddReference(rangeStart, - handler.unwindRefToFunctionKind(), - rangeFieldInFDE, true); - - // Handle the augmentation data if there is any. - if (cieInfo._augmentationDataPresent) { - // First process the augmentation data length field. - uint64_t augmentationDataLengthFieldInFDE = - rangeFieldInFDE + 2 * (is64 ? sizeof(uint64_t) : sizeof(uint32_t)); - unsigned lengthFieldSize = 0; - uint64_t augmentationDataLength = - llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE, - &lengthFieldSize); - - if (cieInfo._offsetOfLSDA != ~0U && augmentationDataLength > 0) { - - // Look at the augmentation data field. - uint64_t augmentationDataFieldInFDE = - augmentationDataLengthFieldInFDE + lengthFieldSize; - - int64_t lsdaFromFDE = readSPtr(is64, isBig, - frameData + augmentationDataFieldInFDE); - uint64_t lsdaStart = - ehFrameSection->address + offset + augmentationDataFieldInFDE + - lsdaFromFDE; - - verifyOrAddReference(lsdaStart, - handler.unwindRefToFunctionKind(), - augmentationDataFieldInFDE, true); - } - } - - return llvm::Error::success(); -} - -llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler) { - - const Section *ehFrameSection = nullptr; - for (auto §ion : normalizedFile.sections) - if (section.segmentName == "__TEXT" && - section.sectionName == "__eh_frame") { - ehFrameSection = §ion; - break; - } - - // No __eh_frame so nothing to do. - if (!ehFrameSection) - return llvm::Error::success(); - - llvm::Error ehFrameErr = llvm::Error::success(); - CIEInfoMap cieInfos; - - file.eachAtomInSection(*ehFrameSection, - [&](MachODefinedAtom *atom, uint64_t offset) -> void { - assert(atom->contentType() == DefinedAtom::typeCFI); - - // Bail out if we've encountered an error. - if (ehFrameErr) - return; - - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - if (ArchHandler::isDwarfCIE(isBig, atom)) - ehFrameErr = processCIE(normalizedFile, file, handler, ehFrameSection, - atom, offset, cieInfos); - else - ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection, - atom, offset, cieInfos); - }); - - return ehFrameErr; -} - -llvm::Error parseObjCImageInfo(const Section §, - const NormalizedFile &normalizedFile, - MachOFile &file) { - - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - - ArrayRef<uint8_t> content = sect.content; - if (content.size() != 8) - return llvm::make_error<GenericError>(sect.segmentName + "/" + - sect.sectionName + - " in file " + file.path() + - " should be 8 bytes in size"); - - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - uint32_t version = read32(content.data(), isBig); - if (version) - return llvm::make_error<GenericError>(sect.segmentName + "/" + - sect.sectionName + - " in file " + file.path() + - " should have version=0"); - - uint32_t flags = read32(content.data() + 4, isBig); - if (flags & (MachOLinkingContext::objc_supports_gc | - MachOLinkingContext::objc_gc_only)) - return llvm::make_error<GenericError>(sect.segmentName + "/" + - sect.sectionName + - " in file " + file.path() + - " uses GC. This is not supported"); - - if (flags & MachOLinkingContext::objc_retainReleaseForSimulator) - file.setObjcConstraint(MachOLinkingContext::objc_retainReleaseForSimulator); - else - file.setObjcConstraint(MachOLinkingContext::objc_retainRelease); - - file.setSwiftVersion((flags >> 8) & 0xFF); - - return llvm::Error::success(); -} - -/// Converts normalized mach-o file into an lld::File and lld::Atoms. -llvm::Expected<std::unique_ptr<lld::File>> -objectToAtoms(const NormalizedFile &normalizedFile, StringRef path, - bool copyRefs) { - auto file = std::make_unique<MachOFile>(path); - if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs)) - return std::move(ec); - return std::unique_ptr<File>(std::move(file)); -} - -llvm::Expected<std::unique_ptr<lld::File>> -dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, - bool copyRefs) { - // Instantiate SharedLibraryFile object. - auto file = std::make_unique<MachODylibFile>(path); - if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs)) - return std::move(ec); - return std::unique_ptr<File>(std::move(file)); -} - -} // anonymous namespace - -namespace normalized { - -static bool isObjCImageInfo(const Section §) { - return (sect.segmentName == "__OBJC" && sect.sectionName == "__image_info") || - (sect.segmentName == "__DATA" && sect.sectionName == "__objc_imageinfo"); -} - -llvm::Error -normalizedObjectToAtoms(MachOFile *file, - const NormalizedFile &normalizedFile, - bool copyRefs) { - LLVM_DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: " - << file->path() << "\n"); - bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0); - - // Create atoms from each section. - for (auto § : normalizedFile.sections) { - - // If this is a debug-info section parse it specially. - if (isDebugInfoSection(sect)) - continue; - - // If the file contains an objc_image_info struct, then we should parse the - // ObjC flags and Swift version. - if (isObjCImageInfo(sect)) { - if (auto ec = parseObjCImageInfo(sect, normalizedFile, *file)) - return ec; - // We then skip adding atoms for this section as we use the ObjCPass to - // re-emit this data after it has been aggregated for all files. - continue; - } - - bool customSectionName; - DefinedAtom::ContentType atomType = atomTypeFromSection(sect, - customSectionName); - if (auto ec = processSection(atomType, sect, customSectionName, - normalizedFile, *file, scatterable, copyRefs)) - return ec; - } - // Create atoms from undefined symbols. - for (auto &sym : normalizedFile.undefinedSymbols) { - // Undefined symbols with n_value != 0 are actually tentative definitions. - if (sym.value == Hex64(0)) { - file->addUndefinedAtom(sym.name, copyRefs); - } else { - file->addTentativeDefAtom(sym.name, atomScope(sym.scope), sym.value, - DefinedAtom::Alignment(1 << (sym.desc >> 8)), - copyRefs); - } - } - - // Convert mach-o relocations to References - std::unique_ptr<mach_o::ArchHandler> handler - = ArchHandler::create(normalizedFile.arch); - for (auto § : normalizedFile.sections) { - if (isDebugInfoSection(sect)) - continue; - if (llvm::Error ec = convertRelocs(sect, normalizedFile, scatterable, - *file, *handler)) - return ec; - } - - // Add additional arch-specific References - file->eachDefinedAtom([&](MachODefinedAtom* atom) -> void { - handler->addAdditionalReferences(*atom); - }); - - // Each __eh_frame section needs references to both __text (the function we're - // providing unwind info for) and itself (FDE -> CIE). These aren't - // represented in the relocations on some architectures, so we have to add - // them back in manually there. - if (auto ec = addEHFrameReferences(normalizedFile, *file, *handler)) - return ec; - - // Process mach-o data-in-code regions array. That information is encoded in - // atoms as References at each transition point. - unsigned nextIndex = 0; - for (const DataInCode &entry : normalizedFile.dataInCode) { - ++nextIndex; - const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset); - if (!s) { - return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE address (" - + Twine(entry.offset) - + ") is not in any section")); - } - uint64_t offsetInSect = entry.offset - s->address; - uint32_t offsetInAtom; - MachODefinedAtom *atom = file->findAtomCoveringAddress(*s, offsetInSect, - &offsetInAtom); - if (offsetInAtom + entry.length > atom->size()) { - return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE entry " - "(offset=" - + Twine(entry.offset) - + ", length=" - + Twine(entry.length) - + ") crosses atom boundary.")); - } - // Add reference that marks start of data-in-code. - atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(), - handler->dataInCodeTransitionStart(*atom), - offsetInAtom, atom, entry.kind); - - // Peek at next entry, if it starts where this one ends, skip ending ref. - if (nextIndex < normalizedFile.dataInCode.size()) { - const DataInCode &nextEntry = normalizedFile.dataInCode[nextIndex]; - if (nextEntry.offset == (entry.offset + entry.length)) - continue; - } - - // If data goes to end of function, skip ending ref. - if ((offsetInAtom + entry.length) == atom->size()) - continue; - - // Add reference that marks end of data-in-code. - atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(), - handler->dataInCodeTransitionEnd(*atom), - offsetInAtom+entry.length, atom, 0); - } - - // Cache some attributes on the file for use later. - file->setFlags(normalizedFile.flags); - file->setArch(normalizedFile.arch); - file->setOS(normalizedFile.os); - file->setMinVersion(normalizedFile.minOSverson); - file->setMinVersionLoadCommandKind(normalizedFile.minOSVersionKind); - - // Sort references in each atom to their canonical order. - for (const DefinedAtom* defAtom : file->defined()) { - reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences(); - } - - if (auto err = parseDebugInfo(*file, normalizedFile, copyRefs)) - return err; - - return llvm::Error::success(); -} - -llvm::Error -normalizedDylibToAtoms(MachODylibFile *file, - const NormalizedFile &normalizedFile, - bool copyRefs) { - file->setInstallName(normalizedFile.installName); - file->setCompatVersion(normalizedFile.compatVersion); - file->setCurrentVersion(normalizedFile.currentVersion); - - // Tell MachODylibFile object about all symbols it exports. - if (!normalizedFile.exportInfo.empty()) { - // If exports trie exists, use it instead of traditional symbol table. - for (const Export &exp : normalizedFile.exportInfo) { - bool weakDef = (exp.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); - // StringRefs from export iterator are ephemeral, so force copy. - file->addExportedSymbol(exp.name, weakDef, true); - } - } else { - for (auto &sym : normalizedFile.globalSymbols) { - assert((sym.scope & N_EXT) && "only expect external symbols here"); - bool weakDef = (sym.desc & N_WEAK_DEF); - file->addExportedSymbol(sym.name, weakDef, copyRefs); - } - } - // Tell MachODylibFile object about all dylibs it re-exports. - for (const DependentDylib &dep : normalizedFile.dependentDylibs) { - if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB) - file->addReExportedDylib(dep.path); - } - return llvm::Error::success(); -} - -void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, - StringRef &segmentName, - StringRef §ionName, - SectionType §ionType, - SectionAttr §ionAttrs, - bool &relocsToDefinedCanBeImplicit) { - - for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ; - p->atomType != DefinedAtom::typeUnknown; ++p) { - if (p->atomType != atomType) - continue; - // Wild carded entries are ignored for reverse lookups. - if (p->segmentName.empty() || p->sectionName.empty()) - continue; - segmentName = p->segmentName; - sectionName = p->sectionName; - sectionType = p->sectionType; - sectionAttrs = 0; - relocsToDefinedCanBeImplicit = false; - if (atomType == DefinedAtom::typeCode) - sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; - if (atomType == DefinedAtom::typeCFI) - relocsToDefinedCanBeImplicit = true; - return; - } - llvm_unreachable("content type not yet supported"); -} - -llvm::Expected<std::unique_ptr<lld::File>> -normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, - bool copyRefs) { - switch (normalizedFile.fileType) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return dylibToAtoms(normalizedFile, path, copyRefs); - case MH_OBJECT: - return objectToAtoms(normalizedFile, path, copyRefs); - default: - llvm_unreachable("unhandled MachO file type!"); - } -} - -} // namespace normalized -} // namespace mach_o -} // namespace lld diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp deleted file mode 100644 index 3826e97d62b9..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachOPasses.h b/lld/lib/ReaderWriter/MachO/MachOPasses.h deleted file mode 100644 index 93cd3e4df281..000000000000 --- a/lld/lib/ReaderWriter/MachO/MachOPasses.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ObjCPass.cpp b/lld/lib/ReaderWriter/MachO/ObjCPass.cpp deleted file mode 100644 index 02a95b5aa0c0..000000000000 --- a/lld/lib/ReaderWriter/MachO/ObjCPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/SectCreateFile.h b/lld/lib/ReaderWriter/MachO/SectCreateFile.h deleted file mode 100644 index 7bb98e16695c..000000000000 --- a/lld/lib/ReaderWriter/MachO/SectCreateFile.h +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/ShimPass.cpp b/lld/lib/ReaderWriter/MachO/ShimPass.cpp deleted file mode 100644 index 4c62ef9d330f..000000000000 --- a/lld/lib/ReaderWriter/MachO/ShimPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/StubsPass.cpp b/lld/lib/ReaderWriter/MachO/StubsPass.cpp deleted file mode 100644 index fbbd8b2c7584..000000000000 --- a/lld/lib/ReaderWriter/MachO/StubsPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/TLVPass.cpp b/lld/lib/ReaderWriter/MachO/TLVPass.cpp deleted file mode 100644 index e0a031cfb07b..000000000000 --- a/lld/lib/ReaderWriter/MachO/TLVPass.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/WriterMachO.cpp b/lld/lib/ReaderWriter/MachO/WriterMachO.cpp deleted file mode 100644 index 60e0e9dd9a81..000000000000 --- a/lld/lib/ReaderWriter/MachO/WriterMachO.cpp +++ /dev/null @@ -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 diff --git a/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp deleted file mode 100644 index c0e6e0334fa6..000000000000 --- a/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ /dev/null @@ -1,1403 +0,0 @@ -//===- lib/ReaderWriter/YAML/ReaderWriterYAML.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/AbsoluteAtom.h" -#include "lld/Core/ArchiveLibraryFile.h" -#include "lld/Core/Atom.h" -#include "lld/Core/DefinedAtom.h" -#include "lld/Core/Error.h" -#include "lld/Core/File.h" -#include "lld/Core/LinkingContext.h" -#include "lld/Core/Reader.h" -#include "lld/Core/Reference.h" -#include "lld/Core/SharedLibraryAtom.h" -#include "lld/Core/Simple.h" -#include "lld/Core/UndefinedAtom.h" -#include "lld/Core/Writer.h" -#include "lld/ReaderWriter/YamlContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/Magic.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/YAMLTraits.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> -#include <cstdint> -#include <cstring> -#include <memory> -#include <string> -#include <system_error> -#include <vector> - -using llvm::file_magic; -using llvm::yaml::MappingTraits; -using llvm::yaml::ScalarEnumerationTraits; -using llvm::yaml::ScalarTraits; -using llvm::yaml::IO; -using llvm::yaml::SequenceTraits; -using llvm::yaml::DocumentListTraits; - -using namespace lld; - -/// The conversion of Atoms to and from YAML uses LLVM's YAML I/O. This -/// file just defines template specializations on the lld types which control -/// how the mapping is done to and from YAML. - -namespace { - -/// Used when writing yaml files. -/// In most cases, atoms names are unambiguous, so references can just -/// use the atom name as the target (e.g. target: foo). But in a few -/// cases that does not work, so ref-names are added. These are labels -/// used only in yaml. The labels do not exist in the Atom model. -/// -/// One need for ref-names are when atoms have no user supplied name -/// (e.g. c-string literal). Another case is when two object files with -/// identically named static functions are merged (ld -r) into one object file. -/// In that case referencing the function by name is ambiguous, so a unique -/// ref-name is added. -class RefNameBuilder { -public: - RefNameBuilder(const lld::File &file) - : _collisionCount(0), _unnamedCounter(0) { - // visit all atoms - for (const lld::DefinedAtom *atom : file.defined()) { - // Build map of atoms names to detect duplicates - if (!atom->name().empty()) - buildDuplicateNameMap(*atom); - - // Find references to unnamed atoms and create ref-names for them. - for (const lld::Reference *ref : *atom) { - // create refname for any unnamed reference target - const lld::Atom *target = ref->target(); - if ((target != nullptr) && target->name().empty()) { - std::string storage; - llvm::raw_string_ostream buffer(storage); - buffer << llvm::format("L%03d", _unnamedCounter++); - StringRef newName = copyString(buffer.str()); - _refNames[target] = std::string(newName); - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "unnamed atom: creating ref-name: '" - << newName << "' (" - << (const void *)newName.data() << ", " - << newName.size() << ")\n"); - } - } - } - for (const lld::UndefinedAtom *undefAtom : file.undefined()) { - buildDuplicateNameMap(*undefAtom); - } - for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) { - buildDuplicateNameMap(*shlibAtom); - } - for (const lld::AbsoluteAtom *absAtom : file.absolute()) { - if (!absAtom->name().empty()) - buildDuplicateNameMap(*absAtom); - } - } - - void buildDuplicateNameMap(const lld::Atom &atom) { - assert(!atom.name().empty()); - NameToAtom::iterator pos = _nameMap.find(atom.name()); - if (pos != _nameMap.end()) { - // Found name collision, give each a unique ref-name. - std::string Storage; - llvm::raw_string_ostream buffer(Storage); - buffer << atom.name() << llvm::format(".%03d", ++_collisionCount); - StringRef newName = copyString(buffer.str()); - _refNames[&atom] = std::string(newName); - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "name collision: creating ref-name: '" - << newName << "' (" - << (const void *)newName.data() - << ", " << newName.size() << ")\n"); - const lld::Atom *prevAtom = pos->second; - AtomToRefName::iterator pos2 = _refNames.find(prevAtom); - if (pos2 == _refNames.end()) { - // Only create ref-name for previous if none already created. - std::string Storage2; - llvm::raw_string_ostream buffer2(Storage2); - buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount); - StringRef newName2 = copyString(buffer2.str()); - _refNames[prevAtom] = std::string(newName2); - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "name collision: creating ref-name: '" - << newName2 << "' (" - << (const void *)newName2.data() << ", " - << newName2.size() << ")\n"); - } - } else { - // First time we've seen this name, just add it to map. - _nameMap[atom.name()] = &atom; - DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() - << "atom name seen for first time: '" - << atom.name() << "' (" - << (const void *)atom.name().data() - << ", " << atom.name().size() << ")\n"); - } - } - - bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); } - - StringRef refName(const lld::Atom *atom) { - return _refNames.find(atom)->second; - } - -private: - typedef llvm::StringMap<const lld::Atom *> NameToAtom; - typedef llvm::DenseMap<const lld::Atom *, std::string> AtomToRefName; - - // Allocate a new copy of this string in _storage, so the strings - // can be freed when RefNameBuilder is destroyed. - StringRef copyString(StringRef str) { - char *s = _storage.Allocate<char>(str.size()); - memcpy(s, str.data(), str.size()); - return StringRef(s, str.size()); - } - - unsigned int _collisionCount; - unsigned int _unnamedCounter; - NameToAtom _nameMap; - AtomToRefName _refNames; - llvm::BumpPtrAllocator _storage; -}; - -/// Used when reading yaml files to find the target of a reference -/// that could be a name or ref-name. -class RefNameResolver { -public: - RefNameResolver(const lld::File *file, IO &io); - - const lld::Atom *lookup(StringRef name) const { - NameToAtom::const_iterator pos = _nameMap.find(name); - if (pos != _nameMap.end()) - return pos->second; - _io.setError(Twine("no such atom name: ") + name); - return nullptr; - } - -private: - typedef llvm::StringMap<const lld::Atom *> NameToAtom; - - void add(StringRef name, const lld::Atom *atom) { - if (_nameMap.count(name)) { - _io.setError(Twine("duplicate atom name: ") + name); - } else { - _nameMap[name] = atom; - } - } - - IO &_io; - NameToAtom _nameMap; -}; - -/// Mapping of Atoms. -template <typename T> class AtomList { - using Ty = std::vector<OwningAtomPtr<T>>; - -public: - typename Ty::iterator begin() { return _atoms.begin(); } - typename Ty::iterator end() { return _atoms.end(); } - Ty _atoms; -}; - -/// Mapping of kind: field in yaml files. -enum FileKinds { - fileKindObjectAtoms, // atom based object file encoded in yaml - fileKindArchive, // static archive library encoded in yaml - fileKindObjectMachO // mach-o object files encoded in yaml -}; - -struct ArchMember { - FileKinds _kind; - StringRef _name; - const lld::File *_content; -}; - -// The content bytes in a DefinedAtom are just uint8_t but we want -// special formatting, so define a strong type. -LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8) - -// SharedLibraryAtoms have a bool canBeNull() method which we'd like to be -// more readable than just true/false. -LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull) - -// lld::Reference::Kind is a tuple of <namespace, arch, value>. -// For yaml, we just want one string that encapsulates the tuple. -struct RefKind { - Reference::KindNamespace ns; - Reference::KindArch arch; - Reference::KindValue value; -}; - -} // end anonymous namespace - -LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember) -LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *) -// Always write DefinedAtoms content bytes as a flow sequence. -LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8) - -// for compatibility with gcc-4.7 in C++11 mode, add extra namespace -namespace llvm { -namespace yaml { - -// This is a custom formatter for RefKind -template <> struct ScalarTraits<RefKind> { - static void output(const RefKind &kind, void *ctxt, raw_ostream &out) { - assert(ctxt != nullptr); - YamlContext *info = reinterpret_cast<YamlContext *>(ctxt); - assert(info->_registry); - StringRef str; - if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value, - str)) - out << str; - else - out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value; - } - - static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) { - assert(ctxt != nullptr); - YamlContext *info = reinterpret_cast<YamlContext *>(ctxt); - assert(info->_registry); - if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch, - kind.value)) - return StringRef(); - return StringRef("unknown reference kind"); - } - - static QuotingType mustQuote(StringRef) { return QuotingType::None; } -}; - -template <> struct ScalarEnumerationTraits<lld::File::Kind> { - static void enumeration(IO &io, lld::File::Kind &value) { - io.enumCase(value, "error-object", lld::File::kindErrorObject); - io.enumCase(value, "object", lld::File::kindMachObject); - io.enumCase(value, "shared-library", lld::File::kindSharedLibrary); - io.enumCase(value, "static-library", lld::File::kindArchiveLibrary); - } -}; - -template <> struct ScalarEnumerationTraits<lld::Atom::Scope> { - static void enumeration(IO &io, lld::Atom::Scope &value) { - io.enumCase(value, "global", lld::Atom::scopeGlobal); - io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit); - io.enumCase(value, "static", lld::Atom::scopeTranslationUnit); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> { - static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) { - io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent); - io.enumCase(value, "custom", lld::DefinedAtom::sectionCustomPreferred); - io.enumCase(value, "custom-required", - lld::DefinedAtom::sectionCustomRequired); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> { - static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) { - io.enumCase(value, "no", DefinedAtom::interposeNo); - io.enumCase(value, "yes", DefinedAtom::interposeYes); - io.enumCase(value, "yes-and-weak", DefinedAtom::interposeYesAndRuntimeWeak); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> { - static void enumeration(IO &io, lld::DefinedAtom::Merge &value) { - io.enumCase(value, "no", lld::DefinedAtom::mergeNo); - io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative); - io.enumCase(value, "as-weak", lld::DefinedAtom::mergeAsWeak); - io.enumCase(value, "as-addressed-weak", - lld::DefinedAtom::mergeAsWeakAndAddressUsed); - io.enumCase(value, "by-content", lld::DefinedAtom::mergeByContent); - io.enumCase(value, "same-name-and-size", - lld::DefinedAtom::mergeSameNameAndSize); - io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> { - static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) { - io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal); - io.enumCase(value, "never", lld::DefinedAtom::deadStripNever); - io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DynamicExport> { - static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) { - io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal); - io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> { - static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) { - io.enumCase(value, "none", lld::DefinedAtom::codeNA); - io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC); - io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro); - io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC); - io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16); - io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb); - io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a); - io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d); - io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t); - } -}; - -template <> -struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> { - static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) { - io.enumCase(value, "---", lld::DefinedAtom::perm___); - io.enumCase(value, "r--", lld::DefinedAtom::permR__); - io.enumCase(value, "r-x", lld::DefinedAtom::permR_X); - io.enumCase(value, "rw-", lld::DefinedAtom::permRW_); - io.enumCase(value, "rwx", lld::DefinedAtom::permRWX); - io.enumCase(value, "rw-l", lld::DefinedAtom::permRW_L); - io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown); - } -}; - -template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> { - static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) { - io.enumCase(value, "unknown", DefinedAtom::typeUnknown); - io.enumCase(value, "code", DefinedAtom::typeCode); - io.enumCase(value, "stub", DefinedAtom::typeStub); - io.enumCase(value, "constant", DefinedAtom::typeConstant); - io.enumCase(value, "data", DefinedAtom::typeData); - io.enumCase(value, "quick-data", DefinedAtom::typeDataFast); - io.enumCase(value, "zero-fill", DefinedAtom::typeZeroFill); - io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast); - io.enumCase(value, "const-data", DefinedAtom::typeConstData); - io.enumCase(value, "got", DefinedAtom::typeGOT); - io.enumCase(value, "resolver", DefinedAtom::typeResolver); - io.enumCase(value, "branch-island", DefinedAtom::typeBranchIsland); - io.enumCase(value, "branch-shim", DefinedAtom::typeBranchShim); - io.enumCase(value, "stub-helper", DefinedAtom::typeStubHelper); - io.enumCase(value, "c-string", DefinedAtom::typeCString); - io.enumCase(value, "utf16-string", DefinedAtom::typeUTF16String); - io.enumCase(value, "unwind-cfi", DefinedAtom::typeCFI); - io.enumCase(value, "unwind-lsda", DefinedAtom::typeLSDA); - io.enumCase(value, "const-4-byte", DefinedAtom::typeLiteral4); - io.enumCase(value, "const-8-byte", DefinedAtom::typeLiteral8); - io.enumCase(value, "const-16-byte", DefinedAtom::typeLiteral16); - io.enumCase(value, "lazy-pointer", DefinedAtom::typeLazyPointer); - io.enumCase(value, "lazy-dylib-pointer", - DefinedAtom::typeLazyDylibPointer); - io.enumCase(value, "cfstring", DefinedAtom::typeCFString); - io.enumCase(value, "initializer-pointer", - DefinedAtom::typeInitializerPtr); - io.enumCase(value, "terminator-pointer", - DefinedAtom::typeTerminatorPtr); - io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr); - io.enumCase(value, "objc-class-pointer", - DefinedAtom::typeObjCClassPtr); - io.enumCase(value, "objc-category-list", - DefinedAtom::typeObjC2CategoryList); - io.enumCase(value, "objc-image-info", - DefinedAtom::typeObjCImageInfo); - io.enumCase(value, "objc-method-list", - DefinedAtom::typeObjCMethodList); - io.enumCase(value, "objc-class1", DefinedAtom::typeObjC1Class); - io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF); - io.enumCase(value, "interposing-tuples", - DefinedAtom::typeInterposingTuples); - io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO); - io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo); - io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo); - io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV); - io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData); - io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill); - io.enumCase(value, "tlv-initializer-ptr", - DefinedAtom::typeTLVInitializerPtr); - io.enumCase(value, "mach_header", DefinedAtom::typeMachHeader); - io.enumCase(value, "dso_handle", DefinedAtom::typeDSOHandle); - io.enumCase(value, "sectcreate", DefinedAtom::typeSectCreate); - } -}; - -template <> struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> { - static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) { - io.enumCase(value, "never", lld::UndefinedAtom::canBeNullNever); - io.enumCase(value, "at-runtime", lld::UndefinedAtom::canBeNullAtRuntime); - io.enumCase(value, "at-buildtime",lld::UndefinedAtom::canBeNullAtBuildtime); - } -}; - -template <> struct ScalarEnumerationTraits<ShlibCanBeNull> { - static void enumeration(IO &io, ShlibCanBeNull &value) { - io.enumCase(value, "never", false); - io.enumCase(value, "at-runtime", true); - } -}; - -template <> -struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> { - static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) { - io.enumCase(value, "code", lld::SharedLibraryAtom::Type::Code); - io.enumCase(value, "data", lld::SharedLibraryAtom::Type::Data); - io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown); - } -}; - -/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look -/// like: -/// 8 # 8-byte aligned -/// 7 mod 16 # 16-byte aligned plus 7 bytes -template <> struct ScalarTraits<lld::DefinedAtom::Alignment> { - static void output(const lld::DefinedAtom::Alignment &value, void *ctxt, - raw_ostream &out) { - if (value.modulus == 0) { - out << llvm::format("%d", value.value); - } else { - out << llvm::format("%d mod %d", value.modulus, value.value); - } - } - - static StringRef input(StringRef scalar, void *ctxt, - lld::DefinedAtom::Alignment &value) { - value.modulus = 0; - size_t modStart = scalar.find("mod"); - if (modStart != StringRef::npos) { - StringRef modStr = scalar.slice(0, modStart); - modStr = modStr.rtrim(); - unsigned int modulus; - if (modStr.getAsInteger(0, modulus)) { - return "malformed alignment modulus"; - } - value.modulus = modulus; - scalar = scalar.drop_front(modStart + 3); - scalar = scalar.ltrim(); - } - unsigned int power; - if (scalar.getAsInteger(0, power)) { - return "malformed alignment power"; - } - value.value = power; - if (value.modulus >= power) { - return "malformed alignment, modulus too large for power"; - } - return StringRef(); // returning empty string means success - } - - static QuotingType mustQuote(StringRef) { return QuotingType::None; } -}; - -template <> struct ScalarEnumerationTraits<FileKinds> { - static void enumeration(IO &io, FileKinds &value) { - io.enumCase(value, "object", fileKindObjectAtoms); - io.enumCase(value, "archive", fileKindArchive); - io.enumCase(value, "object-mach-o", fileKindObjectMachO); - } -}; - -template <> struct MappingTraits<ArchMember> { - static void mapping(IO &io, ArchMember &member) { - io.mapOptional("kind", member._kind, fileKindObjectAtoms); - io.mapOptional("name", member._name); - io.mapRequired("content", member._content); - } -}; - -// Declare that an AtomList is a yaml sequence. -template <typename T> struct SequenceTraits<AtomList<T> > { - static size_t size(IO &io, AtomList<T> &seq) { return seq._atoms.size(); } - static T *&element(IO &io, AtomList<T> &seq, size_t index) { - if (index >= seq._atoms.size()) - seq._atoms.resize(index + 1); - return seq._atoms[index].get(); - } -}; - -// Declare that an AtomRange is a yaml sequence. -template <typename T> struct SequenceTraits<File::AtomRange<T> > { - static size_t size(IO &io, File::AtomRange<T> &seq) { return seq.size(); } - static T *&element(IO &io, File::AtomRange<T> &seq, size_t index) { - assert(io.outputting() && "AtomRange only used when outputting"); - assert(index < seq.size() && "Out of range access"); - return seq[index].get(); - } -}; - -// Used to allow DefinedAtom content bytes to be a flow sequence of -// two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A) -template <> struct ScalarTraits<ImplicitHex8> { - static void output(const ImplicitHex8 &val, void *, raw_ostream &out) { - uint8_t num = val; - out << llvm::format("%02X", num); - } - - static StringRef input(StringRef str, void *, ImplicitHex8 &val) { - unsigned long long n; - if (getAsUnsignedInteger(str, 16, n)) - return "invalid two-digit-hex number"; - if (n > 0xFF) - return "out of range two-digit-hex number"; - val = n; - return StringRef(); // returning empty string means success - } - - static QuotingType mustQuote(StringRef) { return QuotingType::None; } -}; - -// YAML conversion for std::vector<const lld::File*> -template <> struct DocumentListTraits<std::vector<const lld::File *> > { - static size_t size(IO &io, std::vector<const lld::File *> &seq) { - return seq.size(); - } - static const lld::File *&element(IO &io, std::vector<const lld::File *> &seq, - size_t index) { - if (index >= seq.size()) - seq.resize(index + 1); - return seq[index]; - } -}; - -// YAML conversion for const lld::File* -template <> struct MappingTraits<const lld::File *> { - class NormArchiveFile : public lld::ArchiveLibraryFile { - public: - NormArchiveFile(IO &io) : ArchiveLibraryFile("") {} - - NormArchiveFile(IO &io, const lld::File *file) - : ArchiveLibraryFile(file->path()), _path(file->path()) { - // If we want to support writing archives, this constructor would - // need to populate _members. - } - - const lld::File *denormalize(IO &io) { return this; } - - const AtomRange<lld::DefinedAtom> defined() const override { - return _noDefinedAtoms; - } - - const AtomRange<lld::UndefinedAtom> undefined() const override { - return _noUndefinedAtoms; - } - - const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override { - return _noSharedLibraryAtoms; - } - - const AtomRange<lld::AbsoluteAtom> absolute() const override { - return _noAbsoluteAtoms; - } - - void clearAtoms() override { - _noDefinedAtoms.clear(); - _noUndefinedAtoms.clear(); - _noSharedLibraryAtoms.clear(); - _noAbsoluteAtoms.clear(); - } - - File *find(StringRef name) override { - for (const ArchMember &member : _members) - for (const lld::DefinedAtom *atom : member._content->defined()) - if (name == atom->name()) - return const_cast<File *>(member._content); - return nullptr; - } - - std::error_code - parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { - return std::error_code(); - } - - StringRef _path; - std::vector<ArchMember> _members; - }; - - class NormalizedFile : public lld::File { - public: - NormalizedFile(IO &io) - : File("", kindNormalizedObject), _io(io), _rnb(nullptr), - _definedAtomsRef(_definedAtoms._atoms), - _undefinedAtomsRef(_undefinedAtoms._atoms), - _sharedLibraryAtomsRef(_sharedLibraryAtoms._atoms), - _absoluteAtomsRef(_absoluteAtoms._atoms) {} - - NormalizedFile(IO &io, const lld::File *file) - : File(file->path(), kindNormalizedObject), _io(io), - _rnb(new RefNameBuilder(*file)), _path(file->path()), - _definedAtomsRef(file->defined()), - _undefinedAtomsRef(file->undefined()), - _sharedLibraryAtomsRef(file->sharedLibrary()), - _absoluteAtomsRef(file->absolute()) { - } - - ~NormalizedFile() override { - } - - const lld::File *denormalize(IO &io); - - const AtomRange<lld::DefinedAtom> defined() const override { - return _definedAtomsRef; - } - - const AtomRange<lld::UndefinedAtom> undefined() const override { - return _undefinedAtomsRef; - } - - const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override { - return _sharedLibraryAtomsRef; - } - - const AtomRange<lld::AbsoluteAtom> absolute() const override { - return _absoluteAtomsRef; - } - - void clearAtoms() override { - _definedAtoms._atoms.clear(); - _undefinedAtoms._atoms.clear(); - _sharedLibraryAtoms._atoms.clear(); - _absoluteAtoms._atoms.clear(); - } - - // Allocate a new copy of this string in _storage, so the strings - // can be freed when File is destroyed. - StringRef copyString(StringRef str) { - char *s = _storage.Allocate<char>(str.size()); - memcpy(s, str.data(), str.size()); - return StringRef(s, str.size()); - } - - IO &_io; - std::unique_ptr<RefNameBuilder> _rnb; - StringRef _path; - AtomList<lld::DefinedAtom> _definedAtoms; - AtomList<lld::UndefinedAtom> _undefinedAtoms; - AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms; - AtomList<lld::AbsoluteAtom> _absoluteAtoms; - AtomRange<lld::DefinedAtom> _definedAtomsRef; - AtomRange<lld::UndefinedAtom> _undefinedAtomsRef; - AtomRange<lld::SharedLibraryAtom> _sharedLibraryAtomsRef; - AtomRange<lld::AbsoluteAtom> _absoluteAtomsRef; - llvm::BumpPtrAllocator _storage; - }; - - static void mapping(IO &io, const lld::File *&file) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - // Let any register tag handler process this. - if (info->_registry && info->_registry->handleTaggedDoc(io, file)) - return; - // If no registered handler claims this tag and there is no tag, - // grandfather in as "!native". - if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map")) - mappingAtoms(io, file); - } - - static void mappingAtoms(IO &io, const lld::File *&file) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedFile, const lld::File *> - keys(io, file, nullptr); - assert(info != nullptr); - info->_file = keys.operator->(); - - io.mapOptional("path", keys->_path); - - if (io.outputting()) { - io.mapOptional("defined-atoms", keys->_definedAtomsRef); - io.mapOptional("undefined-atoms", keys->_undefinedAtomsRef); - io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtomsRef); - io.mapOptional("absolute-atoms", keys->_absoluteAtomsRef); - } else { - io.mapOptional("defined-atoms", keys->_definedAtoms); - io.mapOptional("undefined-atoms", keys->_undefinedAtoms); - io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms); - io.mapOptional("absolute-atoms", keys->_absoluteAtoms); - } - } - - static void mappingArchive(IO &io, const lld::File *&file) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormArchiveFile, const lld::File *> - keys(io, file, &info->_file->allocator()); - - io.mapOptional("path", keys->_path); - io.mapOptional("members", keys->_members); - } -}; - -// YAML conversion for const lld::Reference* -template <> struct MappingTraits<const lld::Reference *> { - class NormalizedReference : public lld::Reference { - public: - NormalizedReference(IO &io) - : lld::Reference(lld::Reference::KindNamespace::all, - lld::Reference::KindArch::all, 0), - _target(nullptr), _offset(0), _addend(0), _tag(0) {} - - NormalizedReference(IO &io, const lld::Reference *ref) - : lld::Reference(ref->kindNamespace(), ref->kindArch(), - ref->kindValue()), - _target(nullptr), _targetName(targetName(io, ref)), - _offset(ref->offsetInAtom()), _addend(ref->addend()), - _tag(ref->tag()) { - _mappedKind.ns = ref->kindNamespace(); - _mappedKind.arch = ref->kindArch(); - _mappedKind.value = ref->kindValue(); - } - - const lld::Reference *denormalize(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - if (!_targetName.empty()) - _targetName = f->copyString(_targetName); - DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() - << "created Reference to name: '" - << _targetName << "' (" - << (const void *)_targetName.data() - << ", " << _targetName.size() << ")\n"); - setKindNamespace(_mappedKind.ns); - setKindArch(_mappedKind.arch); - setKindValue(_mappedKind.value); - return this; - } - - void bind(const RefNameResolver &); - static StringRef targetName(IO &io, const lld::Reference *ref); - - uint64_t offsetInAtom() const override { return _offset; } - const lld::Atom *target() const override { return _target; } - Addend addend() const override { return _addend; } - void setAddend(Addend a) override { _addend = a; } - void setTarget(const lld::Atom *a) override { _target = a; } - - const lld::Atom *_target; - StringRef _targetName; - uint32_t _offset; - Addend _addend; - RefKind _mappedKind; - uint32_t _tag; - }; - - static void mapping(IO &io, const lld::Reference *&ref) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys( - io, ref, &info->_file->allocator()); - - io.mapRequired("kind", keys->_mappedKind); - io.mapOptional("offset", keys->_offset); - io.mapOptional("target", keys->_targetName); - io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0); - io.mapOptional("tag", keys->_tag, 0u); - } -}; - -// YAML conversion for const lld::DefinedAtom* -template <> struct MappingTraits<const lld::DefinedAtom *> { - - class NormalizedAtom : public lld::DefinedAtom { - public: - NormalizedAtom(IO &io) - : _file(fileFromContext(io)), _contentType(), _alignment(1) { - static uint32_t ordinalCounter = 1; - _ordinal = ordinalCounter++; - } - - NormalizedAtom(IO &io, const lld::DefinedAtom *atom) - : _file(fileFromContext(io)), _name(atom->name()), - _scope(atom->scope()), _interpose(atom->interposable()), - _merge(atom->merge()), _contentType(atom->contentType()), - _alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()), - _deadStrip(atom->deadStrip()), _dynamicExport(atom->dynamicExport()), - _codeModel(atom->codeModel()), - _permissions(atom->permissions()), _size(atom->size()), - _sectionName(atom->customSectionName()), - _sectionSize(atom->sectionSize()) { - for (const lld::Reference *r : *atom) - _references.push_back(r); - if (!atom->occupiesDiskSpace()) - return; - ArrayRef<uint8_t> cont = atom->rawContent(); - _content.reserve(cont.size()); - for (uint8_t x : cont) - _content.push_back(x); - } - - ~NormalizedAtom() override = default; - - const lld::DefinedAtom *denormalize(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - if (!_name.empty()) - _name = f->copyString(_name); - if (!_refName.empty()) - _refName = f->copyString(_refName); - if (!_sectionName.empty()) - _sectionName = f->copyString(_sectionName); - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "created DefinedAtom named: '" << _name - << "' (" << (const void *)_name.data() - << ", " << _name.size() << ")\n"); - return this; - } - - void bind(const RefNameResolver &); - - // Extract current File object from YAML I/O parsing context - const lld::File &fileFromContext(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - assert(info->_file != nullptr); - return *info->_file; - } - - const lld::File &file() const override { return _file; } - StringRef name() const override { return _name; } - uint64_t size() const override { return _size; } - Scope scope() const override { return _scope; } - Interposable interposable() const override { return _interpose; } - Merge merge() const override { return _merge; } - ContentType contentType() const override { return _contentType; } - Alignment alignment() const override { return _alignment; } - SectionChoice sectionChoice() const override { return _sectionChoice; } - StringRef customSectionName() const override { return _sectionName; } - uint64_t sectionSize() const override { return _sectionSize; } - DeadStripKind deadStrip() const override { return _deadStrip; } - DynamicExport dynamicExport() const override { return _dynamicExport; } - CodeModel codeModel() const override { return _codeModel; } - ContentPermissions permissions() const override { return _permissions; } - ArrayRef<uint8_t> rawContent() const override { - if (!occupiesDiskSpace()) - return ArrayRef<uint8_t>(); - return ArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(_content.data()), _content.size()); - } - - uint64_t ordinal() const override { return _ordinal; } - - reference_iterator begin() const override { - uintptr_t index = 0; - const void *it = reinterpret_cast<const void *>(index); - return reference_iterator(*this, it); - } - reference_iterator end() const override { - uintptr_t index = _references.size(); - const void *it = reinterpret_cast<const void *>(index); - return reference_iterator(*this, it); - } - const lld::Reference *derefIterator(const void *it) const override { - uintptr_t index = reinterpret_cast<uintptr_t>(it); - assert(index < _references.size()); - return _references[index]; - } - void incrementIterator(const void *&it) const override { - uintptr_t index = reinterpret_cast<uintptr_t>(it); - ++index; - it = reinterpret_cast<const void *>(index); - } - - void addReference(Reference::KindNamespace ns, - Reference::KindArch arch, - Reference::KindValue kindValue, uint64_t off, - const Atom *target, Reference::Addend a) override { - assert(target && "trying to create reference to nothing"); - auto node = new (file().allocator()) SimpleReference(ns, arch, kindValue, - off, target, a); - _references.push_back(node); - } - - const lld::File &_file; - StringRef _name; - StringRef _refName; - Scope _scope; - Interposable _interpose; - Merge _merge; - ContentType _contentType; - Alignment _alignment; - SectionChoice _sectionChoice; - DeadStripKind _deadStrip; - DynamicExport _dynamicExport; - CodeModel _codeModel; - ContentPermissions _permissions; - uint32_t _ordinal; - std::vector<ImplicitHex8> _content; - uint64_t _size; - StringRef _sectionName; - uint64_t _sectionSize; - std::vector<const lld::Reference *> _references; - }; - - static void mapping(IO &io, const lld::DefinedAtom *&atom) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedAtom, const lld::DefinedAtom *> keys( - io, atom, &info->_file->allocator()); - if (io.outputting()) { - // If writing YAML, check if atom needs a ref-name. - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - assert(info != nullptr); - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - assert(f); - assert(f->_rnb); - if (f->_rnb->hasRefName(atom)) { - keys->_refName = f->_rnb->refName(atom); - } - } - - io.mapOptional("name", keys->_name, StringRef()); - io.mapOptional("ref-name", keys->_refName, StringRef()); - io.mapOptional("scope", keys->_scope, - DefinedAtom::scopeTranslationUnit); - io.mapOptional("type", keys->_contentType, - DefinedAtom::typeCode); - io.mapOptional("content", keys->_content); - io.mapOptional("size", keys->_size, (uint64_t)keys->_content.size()); - io.mapOptional("interposable", keys->_interpose, - DefinedAtom::interposeNo); - io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo); - io.mapOptional("alignment", keys->_alignment, - DefinedAtom::Alignment(1)); - io.mapOptional("section-choice", keys->_sectionChoice, - DefinedAtom::sectionBasedOnContent); - io.mapOptional("section-name", keys->_sectionName, StringRef()); - io.mapOptional("section-size", keys->_sectionSize, (uint64_t)0); - io.mapOptional("dead-strip", keys->_deadStrip, - DefinedAtom::deadStripNormal); - io.mapOptional("dynamic-export", keys->_dynamicExport, - DefinedAtom::dynamicExportNormal); - io.mapOptional("code-model", keys->_codeModel, DefinedAtom::codeNA); - // default permissions based on content type - io.mapOptional("permissions", keys->_permissions, - DefinedAtom::permissions( - keys->_contentType)); - io.mapOptional("references", keys->_references); - } -}; - -template <> struct MappingTraits<lld::DefinedAtom *> { - static void mapping(IO &io, lld::DefinedAtom *&atom) { - const lld::DefinedAtom *atomPtr = atom; - MappingTraits<const lld::DefinedAtom *>::mapping(io, atomPtr); - atom = const_cast<lld::DefinedAtom *>(atomPtr); - } -}; - -// YAML conversion for const lld::UndefinedAtom* -template <> struct MappingTraits<const lld::UndefinedAtom *> { - class NormalizedAtom : public lld::UndefinedAtom { - public: - NormalizedAtom(IO &io) - : _file(fileFromContext(io)), _canBeNull(canBeNullNever) {} - - NormalizedAtom(IO &io, const lld::UndefinedAtom *atom) - : _file(fileFromContext(io)), _name(atom->name()), - _canBeNull(atom->canBeNull()) {} - - ~NormalizedAtom() override = default; - - const lld::UndefinedAtom *denormalize(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - if (!_name.empty()) - _name = f->copyString(_name); - - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "created UndefinedAtom named: '" << _name - << "' (" << (const void *)_name.data() << ", " - << _name.size() << ")\n"); - return this; - } - - // Extract current File object from YAML I/O parsing context - const lld::File &fileFromContext(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - assert(info->_file != nullptr); - return *info->_file; - } - - const lld::File &file() const override { return _file; } - StringRef name() const override { return _name; } - CanBeNull canBeNull() const override { return _canBeNull; } - - const lld::File &_file; - StringRef _name; - CanBeNull _canBeNull; - }; - - static void mapping(IO &io, const lld::UndefinedAtom *&atom) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedAtom, const lld::UndefinedAtom *> keys( - io, atom, &info->_file->allocator()); - - io.mapRequired("name", keys->_name); - io.mapOptional("can-be-null", keys->_canBeNull, - lld::UndefinedAtom::canBeNullNever); - } -}; - -template <> struct MappingTraits<lld::UndefinedAtom *> { - static void mapping(IO &io, lld::UndefinedAtom *&atom) { - const lld::UndefinedAtom *atomPtr = atom; - MappingTraits<const lld::UndefinedAtom *>::mapping(io, atomPtr); - atom = const_cast<lld::UndefinedAtom *>(atomPtr); - } -}; - -// YAML conversion for const lld::SharedLibraryAtom* -template <> struct MappingTraits<const lld::SharedLibraryAtom *> { - class NormalizedAtom : public lld::SharedLibraryAtom { - public: - NormalizedAtom(IO &io) - : _file(fileFromContext(io)), _canBeNull(false), - _type(Type::Unknown), _size(0) {} - - NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom) - : _file(fileFromContext(io)), _name(atom->name()), - _loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()), - _type(atom->type()), _size(atom->size()) {} - - ~NormalizedAtom() override = default; - - const lld::SharedLibraryAtom *denormalize(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - if (!_name.empty()) - _name = f->copyString(_name); - if (!_loadName.empty()) - _loadName = f->copyString(_loadName); - - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "created SharedLibraryAtom named: '" - << _name << "' (" - << (const void *)_name.data() - << ", " << _name.size() << ")\n"); - return this; - } - - // Extract current File object from YAML I/O parsing context - const lld::File &fileFromContext(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - assert(info->_file != nullptr); - return *info->_file; - } - - const lld::File &file() const override { return _file; } - StringRef name() const override { return _name; } - StringRef loadName() const override { return _loadName; } - bool canBeNullAtRuntime() const override { return _canBeNull; } - Type type() const override { return _type; } - uint64_t size() const override { return _size; } - - const lld::File &_file; - StringRef _name; - StringRef _loadName; - ShlibCanBeNull _canBeNull; - Type _type; - uint64_t _size; - }; - - static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) { - - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedAtom, const lld::SharedLibraryAtom *> - keys(io, atom, &info->_file->allocator()); - - io.mapRequired("name", keys->_name); - io.mapOptional("load-name", keys->_loadName); - io.mapOptional("can-be-null", keys->_canBeNull, (ShlibCanBeNull) false); - io.mapOptional("type", keys->_type, SharedLibraryAtom::Type::Code); - io.mapOptional("size", keys->_size, uint64_t(0)); - } -}; - -template <> struct MappingTraits<lld::SharedLibraryAtom *> { - static void mapping(IO &io, lld::SharedLibraryAtom *&atom) { - const lld::SharedLibraryAtom *atomPtr = atom; - MappingTraits<const lld::SharedLibraryAtom *>::mapping(io, atomPtr); - atom = const_cast<lld::SharedLibraryAtom *>(atomPtr); - } -}; - -// YAML conversion for const lld::AbsoluteAtom* -template <> struct MappingTraits<const lld::AbsoluteAtom *> { - class NormalizedAtom : public lld::AbsoluteAtom { - public: - NormalizedAtom(IO &io) - : _file(fileFromContext(io)), _scope(), _value(0) {} - - NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom) - : _file(fileFromContext(io)), _name(atom->name()), - _scope(atom->scope()), _value(atom->value()) {} - - ~NormalizedAtom() override = default; - - const lld::AbsoluteAtom *denormalize(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - if (!_name.empty()) - _name = f->copyString(_name); - - DEBUG_WITH_TYPE("WriterYAML", - llvm::dbgs() << "created AbsoluteAtom named: '" << _name - << "' (" << (const void *)_name.data() - << ", " << _name.size() << ")\n"); - return this; - } - - // Extract current File object from YAML I/O parsing context - const lld::File &fileFromContext(IO &io) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - assert(info->_file != nullptr); - return *info->_file; - } - - const lld::File &file() const override { return _file; } - StringRef name() const override { return _name; } - uint64_t value() const override { return _value; } - Scope scope() const override { return _scope; } - - const lld::File &_file; - StringRef _name; - StringRef _refName; - Scope _scope; - Hex64 _value; - }; - - static void mapping(IO &io, const lld::AbsoluteAtom *&atom) { - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - MappingNormalizationHeap<NormalizedAtom, const lld::AbsoluteAtom *> keys( - io, atom, &info->_file->allocator()); - - if (io.outputting()) { - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - assert(f); - assert(f->_rnb); - if (f->_rnb->hasRefName(atom)) { - keys->_refName = f->_rnb->refName(atom); - } - } - - io.mapRequired("name", keys->_name); - io.mapOptional("ref-name", keys->_refName, StringRef()); - io.mapOptional("scope", keys->_scope); - io.mapRequired("value", keys->_value); - } -}; - -template <> struct MappingTraits<lld::AbsoluteAtom *> { - static void mapping(IO &io, lld::AbsoluteAtom *&atom) { - const lld::AbsoluteAtom *atomPtr = atom; - MappingTraits<const lld::AbsoluteAtom *>::mapping(io, atomPtr); - atom = const_cast<lld::AbsoluteAtom *>(atomPtr); - } -}; - -} // end namespace llvm -} // end namespace yaml - -RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) { - typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom - NormalizedAtom; - for (const lld::DefinedAtom *a : file->defined()) { - const auto *na = (const NormalizedAtom *)a; - if (!na->_refName.empty()) - add(na->_refName, a); - else if (!na->_name.empty()) - add(na->_name, a); - } - - for (const lld::UndefinedAtom *a : file->undefined()) - add(a->name(), a); - - for (const lld::SharedLibraryAtom *a : file->sharedLibrary()) - add(a->name(), a); - - typedef MappingTraits<const lld::AbsoluteAtom *>::NormalizedAtom NormAbsAtom; - for (const lld::AbsoluteAtom *a : file->absolute()) { - const auto *na = (const NormAbsAtom *)a; - if (na->_refName.empty()) - add(na->_name, a); - else - add(na->_refName, a); - } -} - -inline const lld::File * -MappingTraits<const lld::File *>::NormalizedFile::denormalize(IO &io) { - typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom - NormalizedAtom; - - RefNameResolver nameResolver(this, io); - // Now that all atoms are parsed, references can be bound. - for (const lld::DefinedAtom *a : this->defined()) { - auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(a); - normAtom->bind(nameResolver); - } - - return this; -} - -inline void MappingTraits<const lld::DefinedAtom *>::NormalizedAtom::bind( - const RefNameResolver &resolver) { - typedef MappingTraits<const lld::Reference *>::NormalizedReference - NormalizedReference; - for (const lld::Reference *ref : _references) { - auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref); - normRef->bind(resolver); - } -} - -inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind( - const RefNameResolver &resolver) { - _target = resolver.lookup(_targetName); -} - -inline StringRef -MappingTraits<const lld::Reference *>::NormalizedReference::targetName( - IO &io, const lld::Reference *ref) { - if (ref->target() == nullptr) - return StringRef(); - YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); - assert(info != nullptr); - typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; - NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); - RefNameBuilder &rnb = *f->_rnb; - if (rnb.hasRefName(ref->target())) - return rnb.refName(ref->target()); - return ref->target()->name(); -} - -namespace lld { -namespace yaml { - -class Writer : public lld::Writer { -public: - Writer(const LinkingContext &context) : _ctx(context) {} - - llvm::Error writeFile(const lld::File &file, StringRef outPath) override { - // Create stream to path. - std::error_code ec; - llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::OF_TextWithCRLF); - if (ec) - return llvm::errorCodeToError(ec); - - // Create yaml Output writer, using yaml options for context. - YamlContext yamlContext; - yamlContext._ctx = &_ctx; - yamlContext._registry = &_ctx.registry(); - llvm::yaml::Output yout(out, &yamlContext); - - // Write yaml output. - const lld::File *fileRef = &file; - yout << fileRef; - - return llvm::Error::success(); - } - -private: - const LinkingContext &_ctx; -}; - -} // end namespace yaml - -namespace { - -/// Handles !native tagged yaml documents. -class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { - bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override { - if (io.mapTag("!native")) { - MappingTraits<const lld::File *>::mappingAtoms(io, file); - return true; - } - return false; - } -}; - -/// Handles !archive tagged yaml documents. -class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { - bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override { - if (io.mapTag("!archive")) { - MappingTraits<const lld::File *>::mappingArchive(io, file); - return true; - } - return false; - } -}; - -class YAMLReader : public Reader { -public: - YAMLReader(const Registry ®istry) : _registry(registry) {} - - bool canParse(file_magic magic, MemoryBufferRef mb) const override { - StringRef name = mb.getBufferIdentifier(); - return name.endswith(".objtxt") || name.endswith(".yaml"); - } - - ErrorOr<std::unique_ptr<File>> - loadFile(std::unique_ptr<MemoryBuffer> mb, - const class Registry &) const override { - // Create YAML Input Reader. - YamlContext yamlContext; - yamlContext._registry = &_registry; - yamlContext._path = mb->getBufferIdentifier(); - llvm::yaml::Input yin(mb->getBuffer(), &yamlContext); - - // Fill vector with File objects created by parsing yaml. - std::vector<const lld::File *> createdFiles; - yin >> createdFiles; - assert(createdFiles.size() == 1); - - // Error out now if there were parsing errors. - if (yin.error()) - return make_error_code(lld::YamlReaderError::illegal_value); - - std::shared_ptr<MemoryBuffer> smb(mb.release()); - const File *file = createdFiles[0]; - // Note: loadFile() should return vector of *const* File - File *f = const_cast<File *>(file); - f->setLastError(std::error_code()); - f->setSharedMemoryBuffer(smb); - return std::unique_ptr<File>(f); - } - -private: - const Registry &_registry; -}; - -} // end anonymous namespace - -void Registry::addSupportYamlFiles() { - add(std::unique_ptr<Reader>(new YAMLReader(*this))); - add(std::unique_ptr<YamlIOTaggedDocumentHandler>( - new NativeYamlIOTaggedDocumentHandler())); - add(std::unique_ptr<YamlIOTaggedDocumentHandler>( - new ArchiveYamlIOTaggedDocumentHandler())); -} - -std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) { - return std::unique_ptr<Writer>(new lld::yaml::Writer(context)); -} - -} // end namespace lld |