diff options
Diffstat (limited to 'lib/ReaderWriter/MachO')
29 files changed, 2050 insertions, 858 deletions
diff --git a/lib/ReaderWriter/MachO/ArchHandler.h b/lib/ReaderWriter/MachO/ArchHandler.h index 120f5dfd4cd2..70a63bd1004b 100644 --- a/lib/ReaderWriter/MachO/ArchHandler.h +++ b/lib/ReaderWriter/MachO/ArchHandler.h @@ -7,18 +7,19 @@ // //===----------------------------------------------------------------------===// +#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/Core/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" -#ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H -#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H - namespace lld { namespace mach_o { @@ -78,6 +79,11 @@ public: /// 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; @@ -92,6 +98,10 @@ public: /// __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 @@ -107,20 +117,20 @@ public: /// Prototype for a helper function. Given a sectionIndex and address, /// finds the atom and offset with that atom of that address. - typedef std::function<std::error_code (uint32_t sectionIndex, uint64_t addr, + 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<std::error_code (uint32_t symbolIndex, + 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 std::error_code + virtual llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -135,7 +145,7 @@ public: /// (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 std::error_code + virtual llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, @@ -169,7 +179,7 @@ public: FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) = 0; + 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, @@ -251,7 +261,10 @@ public: ReferenceInfo stubHelperReferenceToImm; ReferenceInfo stubHelperReferenceToHelperCommon; + DefinedAtom::ContentType stubHelperImageCacheContentType; + uint32_t stubHelperCommonSize; + uint8_t stubHelperCommonAlignment; uint8_t stubHelperCommonBytes[36]; ReferenceInfo stubHelperCommonReferenceToCache; OptionalRefInfo optStubHelperCommonReferenceToCache; diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp index 4e15a2d434c6..3286fe064535 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -51,6 +51,10 @@ public: return invalid; } + Reference::KindValue unwindRefToPersonalityFunctionKind() override { + return invalid; + } + Reference::KindValue unwindRefToCIEKind() override { return invalid; } @@ -63,21 +67,25 @@ public: return invalid; } + Reference::KindValue pointerKind() override { + return invalid; + } + uint32_t dwarfCompactUnwindType() override { // FIXME return -1; } - std::error_code 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; - std::error_code + 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, @@ -93,7 +101,7 @@ public: FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) override; + llvm::MutableArrayRef<uint8_t> atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, @@ -256,8 +264,13 @@ const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { { 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 @@ -505,13 +518,12 @@ uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { return value; } -std::error_code ArchHandler_arm::getReferenceInfo( +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) { - typedef std::error_code E; const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; uint32_t instruction = *(const ulittle32_t *)fixupContent; @@ -523,12 +535,12 @@ std::error_code ArchHandler_arm::getReferenceInfo( *kind = thumb_b22; else *kind = thumb_bl22; - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); *addend = fixupAddress + 4 + displacement; - return std::error_code(); + return llvm::Error(); case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if ((instruction & 0xD000F800) == 0x9000F000) @@ -546,12 +558,12 @@ std::error_code ArchHandler_arm::getReferenceInfo( *kind = thumb_bl22; displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); targetAddress = fixupAddress + 4 + displacement; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + 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 std::error_code(); + return llvm::Error(); case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: // ex: bl _foo (and _foo is undefined) if (((instruction & 0x0F000000) == 0x0A000000) @@ -559,12 +571,12 @@ std::error_code ArchHandler_arm::getReferenceInfo( *kind = arm_b24; else *kind = arm_bl24; - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromArmBranch(instruction); *addend = fixupAddress + 8 + displacement; - return std::error_code(); + return llvm::Error(); case ARM_RELOC_BR24 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if (((instruction & 0x0F000000) == 0x0A000000) @@ -584,40 +596,40 @@ std::error_code ArchHandler_arm::getReferenceInfo( *kind = arm_bl24; displacement = getDisplacementFromArmBranch(instruction); targetAddress = fixupAddress + 8 + displacement; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + 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 std::error_code(); + return llvm::Error(); case ARM_RELOC_VANILLA | rExtern | rLength4: // ex: .long _foo (and _foo is undefined) *kind = pointer32; - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = instruction; - return std::error_code(); + return llvm::Error(); case ARM_RELOC_VANILLA | rLength4: // ex: .long _foo (and _foo is defined) *kind = pointer32; - if (E ec = atomFromAddress(reloc.symbol, instruction, target, addend)) + if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend)) return ec; *addend = clearThumbBit((uint32_t) * addend, *target); - return std::error_code(); + return llvm::Error(); case ARM_RELOC_VANILLA | rScattered | rLength4: // ex: .long _foo+a (and _foo is defined) *kind = pointer32; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend += (clearThumbBit(instruction, *target) - reloc.value); - return std::error_code(); + return llvm::Error(); default: - return make_dynamic_error_code("unsupported arm relocation type"); + return llvm::make_error<GenericError>("unsupported arm relocation type"); } - return std::error_code(); + return llvm::Error(); } -std::error_code +llvm::Error ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, @@ -770,10 +782,9 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, pointerDiff = true; break; default: - return make_dynamic_error_code("unsupported arm relocation pair"); + return llvm::make_error<GenericError>("unsupported arm relocation pair"); } const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; - std::error_code ec; uint32_t instruction = *(const ulittle32_t *)fixupContent; uint32_t value; uint32_t fromAddress; @@ -786,14 +797,12 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, if (pointerDiff) { toAddress = reloc1.value; fromAddress = reloc2.value; - ec = atomFromAddr(0, toAddress, target, &offsetInTo); - if (ec) + if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; - ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); - if (ec) + if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (scatterable && (fromTarget != inAtom)) - return make_dynamic_error_code( + return llvm::make_error<GenericError>( "SECTDIFF relocation where subtrahend label is not in atom"); *kind = delta32; value = clearThumbBit(instruction, *target); @@ -801,35 +810,33 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, } else if (funcRel) { toAddress = reloc1.value; fromAddress = reloc2.value; - ec = atomFromAddr(0, toAddress, target, &offsetInTo); - if (ec) + if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; - ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); - if (ec) + if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (fromTarget != inAtom) - return make_dynamic_error_code("ARM_RELOC_HALF_SECTDIFF relocation " - "where subtrahend label is not in atom"); + 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 make_dynamic_error_code("expected movt instruction"); + return llvm::make_error<GenericError>("expected movt instruction"); } else { if (!isThumbMovw(instruction)) - return make_dynamic_error_code("expected movw instruction"); + return llvm::make_error<GenericError>("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) - return make_dynamic_error_code("expected movt instruction"); + return llvm::make_error<GenericError>("expected movt instruction"); } else { if (!isArmMovw(instruction)) - return make_dynamic_error_code("expected movw instruction"); + return llvm::make_error<GenericError>("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } @@ -840,28 +847,28 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, value = clearThumbBit(value, *target); int64_t ta = (int64_t) value - (toAddress - fromAddress); *addend = ta - offsetInFrom; - return std::error_code(); + return llvm::Error(); } else { uint32_t sectIndex; if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) - return make_dynamic_error_code("expected movt instruction"); + return llvm::make_error<GenericError>("expected movt instruction"); } else { if (!isThumbMovw(instruction)) - return make_dynamic_error_code("expected movw instruction"); + return llvm::make_error<GenericError>("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) - return make_dynamic_error_code("expected movt instruction"); + return llvm::make_error<GenericError>("expected movt instruction"); } else { if (!isArmMovw(instruction)) - return make_dynamic_error_code("expected movw instruction"); + return llvm::make_error<GenericError>("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } @@ -871,8 +878,7 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, else value = (other16 << 16) | instruction16; if (reloc1.isExtern) { - ec = atomFromSymbolIndex(reloc1.symbol, target); - if (ec) + if (auto ec = atomFromSymbolIndex(reloc1.symbol, target)) return ec; *addend = value; } else { @@ -883,14 +889,13 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, toAddress = value; sectIndex = reloc1.symbol; } - ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo); - if (ec) + if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo)) return ec; *addend = value - toAddress; } } - return std::error_code(); + return llvm::Error(); } void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, @@ -1006,9 +1011,10 @@ void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) { + llvm::MutableArrayRef<uint8_t> atomContentBuffer) { // Copy raw bytes. - memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + std::copy(atom.rawContent().begin(), atom.rawContent().end(), + atomContentBuffer.begin()); // Apply fix-ups. bool thumbMode = false; for (const Reference *ref : atom) { @@ -1384,7 +1390,8 @@ void ArchHandler_arm::appendSectionRelocations( void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { if (atom.isThumb()) { - atom.addReference(0, modeThumbCode, &atom, 0, Reference::KindArch::ARM); + atom.addReference(Reference::KindNamespace::mach_o, + Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0); } } @@ -1415,6 +1422,8 @@ public: _name = tmp.copy(file.allocator()); } + ~Thumb2ToArmShimAtom() override = default; + StringRef name() const override { return _name; } @@ -1458,6 +1467,8 @@ public: _name = tmp.copy(file.allocator()); } + ~ArmToThumbShimAtom() override = default; + StringRef name() const override { return _name; } diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp index 778f6f4add74..a61f6aac05e1 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp @@ -53,6 +53,9 @@ public: case delta32ToGOT: canBypassGOT = false; return true; + case unwindCIEToPersonalityFunction: + canBypassGOT = false; + return true; case imageOffsetGot: canBypassGOT = false; return true; @@ -108,6 +111,10 @@ public: return imageOffsetGot; } + Reference::KindValue unwindRefToPersonalityFunctionKind() override { + return unwindCIEToPersonalityFunction; + } + Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } @@ -120,20 +127,24 @@ public: return unwindInfoToEhFrame; } + Reference::KindValue pointerKind() override { + return pointer64; + } + uint32_t dwarfCompactUnwindType() override { return 0x03000000; } - std::error_code 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; - std::error_code + 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, @@ -153,7 +164,7 @@ public: FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) override; + llvm::MutableArrayRef<uint8_t> atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, @@ -197,6 +208,9 @@ private: 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 @@ -244,6 +258,7 @@ const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = { 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), @@ -279,8 +294,13 @@ const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = { { 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]! @@ -355,7 +375,7 @@ uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) { return (instruction & 0xFFC003FF) | imm12; } -std::error_code ArchHandler_arm64::getReferenceInfo( +llvm::Error ArchHandler_arm64::getReferenceInfo( const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, @@ -369,56 +389,56 @@ std::error_code ArchHandler_arm64::getReferenceInfo( if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; - return std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); 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 std::error_code(); + return llvm::Error(); case ARM64_RELOC_UNSIGNED | rLength8: // ex: .quad Lfoo + N *kind = pointer64; @@ -430,27 +450,33 @@ std::error_code ArchHandler_arm64::getReferenceInfo( if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; - return std::error_code(); + return llvm::Error(); case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4: // ex: .long _foo@GOT - . - *kind = delta32ToGOT; + + // 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 std::error_code(); + return llvm::Error(); default: - return make_dynamic_error_code("unsupported arm64 relocation type"); + return llvm::make_error<GenericError>("unsupported arm64 relocation type"); } } -std::error_code ArchHandler_arm64::getPairReferenceInfo( +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]; - const uint32_t *cont32 = reinterpret_cast<const uint32_t *>(fixupContent); switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4): @@ -459,7 +485,7 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo( if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; - return std::error_code(); + return llvm::Error(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4): // ex: adrp x1, _foo@PAGE @@ -467,26 +493,36 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo( if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; - return std::error_code(); + return llvm::Error(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | - ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): + ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): { // ex: ldr w0, [x1, _foo@PAGEOFF] - *kind = offset12KindFromInstruction(*cont32); + 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 std::error_code(); + return llvm::Error(); + } case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength8): // ex: .quad _foo - . - *kind = delta64; 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 make_dynamic_error_code("paired relocs must have the same offset"); + return llvm::make_error<GenericError>( + "paired relocs must have the same offset"); *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; - return std::error_code(); + return llvm::Error(); case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength4): // ex: .quad _foo - . @@ -494,18 +530,19 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo( if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; - return std::error_code(); + return llvm::Error(); default: - return make_dynamic_error_code("unsupported arm64 relocation pair"); + 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, - uint8_t *atomContentBuffer) { + llvm::MutableArrayRef<uint8_t> atomContentBuffer) { // Copy raw bytes. - memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + std::copy(atom.rawContent().begin(), atom.rawContent().end(), + atomContentBuffer.begin()); // Apply fix-ups. #ifndef NDEBUG if (atom.begin() != atom.end()) { @@ -620,6 +657,7 @@ void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc, return; case delta32: case delta32ToGOT: + case unwindCIEToPersonalityFunction: *loc32 = (targetAddress - fixupAddress) + ref.addend(); return; case negDelta32: @@ -710,6 +748,13 @@ void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref, 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: @@ -832,6 +877,7 @@ void ArchHandler_arm64::appendSectionRelocations( case imageOffset: case imageOffsetGot: llvm_unreachable("deltas from mach_header can only be in final images"); + case unwindCIEToPersonalityFunction: case unwindFDEToFunction: case unwindInfoToEhFrame: case negDelta32: diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp index 7aac2584d078..15f1f793b5d7 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -58,6 +58,10 @@ public: return invalid; } + Reference::KindValue unwindRefToPersonalityFunctionKind() override { + return invalid; + } + Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } @@ -70,20 +74,24 @@ public: return invalid; } + Reference::KindValue pointerKind() override { + return invalid; + } + uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } - std::error_code 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; - std::error_code + 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, @@ -99,7 +107,7 @@ public: FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) override; + llvm::MutableArrayRef<uint8_t> atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, @@ -212,8 +220,13 @@ const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = { { 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 @@ -238,7 +251,7 @@ bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) { (reloc.type == GENERIC_RELOC_SECTDIFF); } -std::error_code +llvm::Error ArchHandler_x86::getReferenceInfo(const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -248,7 +261,6 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { - typedef std::error_code E; DefinedAtom::ContentPermissions perms; const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; @@ -256,7 +268,7 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4: // ex: call _foo (and _foo undefined) *kind = branch32; - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent; break; @@ -272,14 +284,14 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, *kind = branch32; targetAddress = fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + 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 (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent; break; @@ -295,7 +307,7 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, *kind = branch16; targetAddress = fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend = targetAddress - reloc.value; break; @@ -306,7 +318,7 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 : pointer32; - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const ulittle32_t *)fixupContent; break; @@ -326,17 +338,17 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 : pointer32; - if (E ec = atomFromAddress(0, reloc.value, target, addend)) + if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend = *(const ulittle32_t *)fixupContent - reloc.value; break; default: - return make_dynamic_error_code("unsupported i386 relocation type"); + return llvm::make_error<GenericError>("unsupported i386 relocation type"); } - return std::error_code(); + return llvm::Error(); } -std::error_code +llvm::Error ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, @@ -349,7 +361,6 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; - std::error_code ec; DefinedAtom::ContentPermissions perms = inAtom->permissions(); uint32_t fromAddress; uint32_t toAddress; @@ -365,15 +376,13 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, toAddress = reloc1.value; fromAddress = reloc2.value; value = *(const little32_t *)fixupContent; - ec = atomFromAddr(0, toAddress, target, &offsetInTo); - if (ec) + if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; - ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); - if (ec) + if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (fromTarget != inAtom) { if (*target != inAtom) - return make_dynamic_error_code( + return llvm::make_error<GenericError>( "SECTDIFF relocation where neither target is in atom"); *kind = negDelta32; *addend = toAddress - value - fromAddress; @@ -394,10 +403,10 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, *addend = fromAddress + value - toAddress; } } - return std::error_code(); + return llvm::Error(); break; default: - return make_dynamic_error_code("unsupported i386 relocation type"); + return llvm::make_error<GenericError>("unsupported i386 relocation type"); } } @@ -406,9 +415,10 @@ void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) { + llvm::MutableArrayRef<uint8_t> atomContentBuffer) { // Copy raw bytes. - memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + std::copy(atom.rawContent().begin(), atom.rawContent().end(), + atomContentBuffer.begin()); // Apply fix-ups. for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index 8b4d1cf38cba..c36982a77b13 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -104,6 +104,10 @@ public: return imageOffsetGot; } + Reference::KindValue unwindRefToPersonalityFunctionKind() override { + return ripRel32Got; + } + Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } @@ -116,6 +120,10 @@ public: return unwindInfoToEhFrame; } + Reference::KindValue pointerKind() override { + return pointer64; + } + uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } @@ -130,16 +138,16 @@ public: bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; - std::error_code 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; - std::error_code + 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, @@ -159,7 +167,7 @@ public: FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBase, - uint8_t *atomContentBuffer) override; + llvm::MutableArrayRef<uint8_t> atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, @@ -195,6 +203,7 @@ private: delta32, /// ex: .long _foo - . delta64Anon, /// ex: .quad L1 - . delta32Anon, /// ex: .long L1 - . + negDelta64, /// ex: .quad . - _foo negDelta32, /// ex: .long . - _foo // Kinds introduced by Passes: @@ -216,8 +225,6 @@ private: }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); - Reference::KindValue kindFromRelocPair(const normalized::Relocation &reloc1, - const normalized::Relocation &reloc2); void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, @@ -246,6 +253,7 @@ const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { 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), @@ -280,8 +288,13 @@ const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = { { 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) @@ -348,7 +361,7 @@ ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { } } -std::error_code +llvm::Error ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -358,34 +371,33 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { - typedef std::error_code E; *kind = kindFromReloc(reloc); if (*kind == invalid) - return make_dynamic_error_code("unknown type"); + return llvm::make_error<GenericError>("unknown type"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; switch (*kind) { case branch32: case ripRel32: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; - return std::error_code(); + return llvm::Error(); case ripRel32Minus1: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 1; - return std::error_code(); + return llvm::Error(); case ripRel32Minus2: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 2; - return std::error_code(); + return llvm::Error(); case ripRel32Minus4: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 4; - return std::error_code(); + return llvm::Error(); case ripRel32Anon: targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); @@ -401,13 +413,13 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, case ripRel32GotLoad: case ripRel32Got: case ripRel32Tlv: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; - return std::error_code(); + return llvm::Error(); case tlvInitSectionOffset: case pointer64: - if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + 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. @@ -417,7 +429,7 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, assert(*addend == 0 && "TLV-init has non-zero addend?"); } else *addend = *(const little64_t *)fixupContent; - return std::error_code(); + return llvm::Error(); case pointer64Anon: targetAddress = *(const little64_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); @@ -426,28 +438,7 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, } } -Reference::KindValue -ArchHandler_x86_64::kindFromRelocPair(const normalized::Relocation &reloc1, - const normalized::Relocation &reloc2) { - switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { - case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | - X86_64_RELOC_UNSIGNED | rExtern | rLength8): - return delta64; - case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | - X86_64_RELOC_UNSIGNED | rExtern | rLength4): - return delta32; - case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | - X86_64_RELOC_UNSIGNED | rLength8): - return delta64Anon; - case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | - X86_64_RELOC_UNSIGNED | rLength4): - return delta32Anon; - default: - llvm_unreachable("bad reloc pairs"); - } -} - -std::error_code +llvm::Error ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, @@ -459,45 +450,71 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { - *kind = kindFromRelocPair(reloc1, reloc2); - if (*kind == invalid) - return make_dynamic_error_code("unknown pair"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; - typedef std::error_code E; uint64_t targetAddress; const lld::Atom *fromTarget; - if (E ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) + if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) return ec; - if (fromTarget != inAtom) - return make_dynamic_error_code("pointer diff not in base atom"); - switch (*kind) { - case delta64: - if (E ec = atomFromSymbolIndex(reloc2.symbol, target)) + + 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; - *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; - return std::error_code(); - case delta32: - if (E ec = atomFromSymbolIndex(reloc2.symbol, target)) + uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent; + if (inAtom == fromTarget) { + *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(); + } + case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | + X86_64_RELOC_UNSIGNED | rExtern | rLength4): { + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; - *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; - return std::error_code(); - case delta64Anon: + 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(); + } + 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 delta32Anon: + 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: - llvm_unreachable("bad reloc pair kind"); + return llvm::make_error<GenericError>("unknown pair"); } } void ArchHandler_x86_64::generateAtomContent( const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, - uint8_t *atomContentBuffer) { + llvm::MutableArrayRef<uint8_t> atomContentBuffer) { // Copy raw bytes. - memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + std::copy(atom.rawContent().begin(), atom.rawContent().end(), + atomContentBuffer.begin()); // Apply fix-ups. for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); @@ -571,6 +588,9 @@ void ArchHandler_x86_64::applyFixupFinal( 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; @@ -675,8 +695,11 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, // 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 = fixupAddress - targetAddress + ref.addend(); + *loc32 = ref.addend() + fixupAddress - inAtomAddress; return; case ripRel32GotLoadNowLea: llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); @@ -796,7 +819,18 @@ void ArchHandler_x86_64::appendSectionRelocations( 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"); diff --git a/lib/ReaderWriter/MachO/Atoms.h b/lib/ReaderWriter/MachO/Atoms.h index 9f2e5acad99a..573efca9f6f9 100644 --- a/lib/ReaderWriter/MachO/Atoms.h +++ b/lib/ReaderWriter/MachO/Atoms.h @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/Atoms.h -------------------------------------===// +//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===// // // The LLVM Linker // @@ -10,10 +10,21 @@ #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, @@ -32,6 +43,8 @@ public: _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; } @@ -61,15 +74,6 @@ public: bool isThumb() const { return _thumb; } - void addReference(uint32_t offsetInAtom, uint16_t relocType, - const Atom *target, Reference::Addend addend, - Reference::KindArch arch = Reference::KindArch::x86_64, - Reference::KindNamespace ns - = Reference::KindNamespace::mach_o) { - SimpleDefinedAtom::addReference(ns, arch, relocType, offsetInAtom, target, - addend); - } - private: const StringRef _name; const ArrayRef<uint8_t> _content; @@ -92,6 +96,8 @@ public: content, align), _sectionName(sectionName) {} + ~MachODefinedCustomSectionAtom() override = default; + SectionChoice sectionChoice() const override { return DefinedAtom::sectionCustomRequired; } @@ -110,6 +116,8 @@ public: : SimpleDefinedAtom(f), _name(name), _scope(scope), _size(size), _align(align) {} + ~MachOTentativeDefAtom() override = default; + uint64_t size() const override { return _size; } Merge merge() const override { return DefinedAtom::mergeAsTentative; } @@ -167,7 +175,7 @@ private: StringRef _dylibInstallName; }; -} // namespace mach_o -} // namespace lld +} // end namespace mach_o +} // end namespace lld #endif // LLD_READER_WRITER_MACHO_ATOMS_H diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt index a389ca51ddfd..70f451c997b3 100644 --- a/lib/ReaderWriter/MachO/CMakeLists.txt +++ b/lib/ReaderWriter/MachO/CMakeLists.txt @@ -13,6 +13,7 @@ add_lld_library(lldMachO MachONormalizedFileFromAtoms.cpp MachONormalizedFileToAtoms.cpp MachONormalizedFileYAML.cpp + ObjCPass.cpp ShimPass.cpp StubsPass.cpp TLVPass.cpp @@ -22,6 +23,7 @@ add_lld_library(lldMachO lldYAML LLVMObject LLVMSupport + ${PTHREAD_LIB} ) include_directories(.) diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp index 4b8644a67e70..6f5ab83dbda6 100644 --- a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp +++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp @@ -59,7 +59,7 @@ struct CompactUnwindEntry { }; struct UnwindInfoPage { - std::vector<CompactUnwindEntry> entries; + ArrayRef<CompactUnwindEntry> entries; }; } @@ -88,6 +88,8 @@ public: addSecondLevelPages(pages); } + ~UnwindInfoAtom() override = default; + ContentType contentType() const override { return DefinedAtom::typeProcessedUnwindInfo; } @@ -179,7 +181,7 @@ public: } // Finally, write out the final sentinel index - CompactUnwindEntry &finalEntry = pages[pages.size() - 1].entries.back(); + auto &finalEntry = pages[pages.size() - 1].entries.back(); addImageReference(_topLevelIndexOffset + 3 * pages.size() * sizeof(uint32_t), finalEntry.rangeStart, finalEntry.rangeLength); @@ -273,11 +275,13 @@ class CompactUnwindPass : public Pass { public: CompactUnwindPass(const MachOLinkingContext &context) : _ctx(context), _archHandler(_ctx.archHandler()), - _file("<mach-o Compact Unwind Pass>"), - _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {} + _file(*_ctx.make_file<MachOFile>("<mach-o Compact Unwind Pass>")), + _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } private: - std::error_code perform(SimpleFile &mergedFile) override { + llvm::Error perform(SimpleFile &mergedFile) override { DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); std::map<const Atom *, CompactUnwindEntry> unwindLocs; @@ -294,7 +298,7 @@ private: // Skip rest of pass if no unwind info. if (unwindLocs.empty() && dwarfFrames.empty()) - return std::error_code(); + return llvm::Error(); // 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 @@ -321,26 +325,23 @@ private: // boundaries. That might be worth doing, or perhaps we could perform some // minor balancing for expected number of lookups. std::vector<UnwindInfoPage> pages; - unsigned pageStart = 0; + 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)unwindInfos.size() - pageStart); + unsigned entriesInPage = std::min(1021U, (unsigned)remainingInfos.size()); - std::copy(unwindInfos.begin() + pageStart, - unwindInfos.begin() + pageStart + entriesInPage, - std::back_inserter(pages.back().entries)); - pageStart += entriesInPage; + pages.back().entries = remainingInfos.slice(0, entriesInPage); + remainingInfos = remainingInfos.slice(entriesInPage); 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 (pageStart < unwindInfos.size()); + } while (!remainingInfos.empty()); auto *unwind = new (_file.allocator()) UnwindInfoAtom(_archHandler, _file, _isBig, personalities, @@ -352,7 +353,7 @@ private: return atom->contentType() == DefinedAtom::typeCompactUnwindInfo; }); - return std::error_code(); + return llvm::Error(); } void collectCompactUnwindEntries( @@ -568,7 +569,7 @@ private: const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; - MachOFile _file; + MachOFile &_file; bool _isBig; }; diff --git a/lib/ReaderWriter/MachO/ExecutableAtoms.h b/lib/ReaderWriter/MachO/ExecutableAtoms.h index 2e99af903dbd..acced33b7e74 100644 --- a/lib/ReaderWriter/MachO/ExecutableAtoms.h +++ b/lib/ReaderWriter/MachO/ExecutableAtoms.h @@ -11,10 +11,10 @@ #define LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H #include "Atoms.h" +#include "File.h" #include "llvm/Support/MachO.h" -#include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/DefinedAtom.h" #include "lld/Core/File.h" #include "lld/Core/LinkingContext.h" @@ -34,7 +34,7 @@ namespace mach_o { class CEntryFile : public SimpleFile { public: CEntryFile(const MachOLinkingContext &context) - : SimpleFile("C entry"), + : SimpleFile("C entry", kindCEntryObject), _undefMain(*this, context.entrySymbolName()) { this->addAtom(_undefMain); } @@ -51,7 +51,7 @@ private: class StubHelperFile : public SimpleFile { public: StubHelperFile(const MachOLinkingContext &context) - : SimpleFile("stub runtime"), + : SimpleFile("stub runtime", kindStubHelperObject), _undefBinder(*this, context.binderSymbolName()) { this->addAtom(_undefBinder); } @@ -65,66 +65,88 @@ private: // MachHeaderAliasFile lazily instantiates the magic symbols that mark the start // of the mach_header for final linked images. // -class MachHeaderAliasFile : public ArchiveLibraryFile { +class MachHeaderAliasFile : public SimpleFile { public: MachHeaderAliasFile(const MachOLinkingContext &context) - : ArchiveLibraryFile("mach_header symbols") { - switch (context.outputMachOType()) { - case llvm::MachO::MH_EXECUTE: - _machHeaderSymbolName = "__mh_execute_header"; - break; - case llvm::MachO::MH_DYLIB: - _machHeaderSymbolName = "__mh_dylib_header"; - break; - case llvm::MachO::MH_BUNDLE: - _machHeaderSymbolName = "__mh_bundle_header"; - break; - case llvm::MachO::MH_DYLINKER: - _machHeaderSymbolName = "__mh_dylinker_header"; - break; - case llvm::MachO::MH_PRELOAD: - _machHeaderSymbolName = "__mh_preload_header"; - break; - default: - llvm_unreachable("no mach_header symbol for file type"); - } - } - - std::error_code - parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { - return std::error_code(); - } - - File *find(StringRef sym, bool dataSymbolOnly) override { - if (sym.equals("___dso_handle") || sym.equals(_machHeaderSymbolName)) { + : 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, sym, DefinedAtom::scopeLinkageUnit, - DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false, false, + *this, machHeaderSymbolName, symbolScope, + DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false, + true /* noDeadStrip */, ArrayRef<uint8_t>(), DefinedAtom::Alignment(4096))); - return this; - } - return nullptr; + + 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 AtomVector<DefinedAtom> &defined() const override { + const AtomRange<DefinedAtom> defined() const override { return _definedAtoms; } - const AtomVector<UndefinedAtom> &undefined() const override { + const AtomRange<UndefinedAtom> undefined() const override { return _noUndefinedAtoms; } - const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + const AtomRange<SharedLibraryAtom> sharedLibrary() const override { return _noSharedLibraryAtoms; } - const AtomVector<AbsoluteAtom> &absolute() const override { + const AtomRange<AbsoluteAtom> absolute() const override { return _noAbsoluteAtoms; } + void clearAtoms() override { + _definedAtoms.clear(); + _noUndefinedAtoms.clear(); + _noSharedLibraryAtoms.clear(); + _noAbsoluteAtoms.clear(); + } + private: mutable AtomVector<DefinedAtom> _definedAtoms; - StringRef _machHeaderSymbolName; }; } // namespace mach_o diff --git a/lib/ReaderWriter/MachO/File.h b/lib/ReaderWriter/MachO/File.h index c97dfa142b8d..64a0fcf82844 100644 --- a/lib/ReaderWriter/MachO/File.h +++ b/lib/ReaderWriter/MachO/File.h @@ -14,6 +14,7 @@ #include "MachONormalizedFile.h" #include "lld/Core/SharedLibraryFile.h" #include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include <unordered_map> @@ -25,9 +26,10 @@ using lld::mach_o::normalized::Section; class MachOFile : public SimpleFile { public: MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx) - : SimpleFile(mb->getBufferIdentifier()), _mb(std::move(mb)), _ctx(ctx) {} + : SimpleFile(mb->getBufferIdentifier(), File::kindMachObject), + _mb(std::move(mb)), _ctx(ctx) {} - MachOFile(StringRef path) : SimpleFile(path) {} + MachOFile(StringRef path) : SimpleFile(path, File::kindMachObject) {} void addDefinedAtom(StringRef name, Atom::Scope scope, DefinedAtom::ContentType type, DefinedAtom::Merge merge, @@ -187,16 +189,51 @@ public: 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; + } + protected: std::error_code doParse() override { // Convert binary file to normalized mach-o. auto normFile = normalized::readBinary(_mb, _ctx->arch()); - if (std::error_code ec = normFile.getError()) - return ec; + if (auto ec = normFile.takeError()) + return llvm::errorToErrorCode(std::move(ec)); // Convert normalized mach-o to atoms. - if (std::error_code ec = normalized::normalizedObjectToAtoms( - this, **normFile, false)) - return ec; + if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false)) + return llvm::errorToErrorCode(std::move(ec)); return std::error_code(); } @@ -220,6 +257,14 @@ private: 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; }; class MachODylibFile : public SharedLibraryFile { @@ -230,7 +275,7 @@ public: MachODylibFile(StringRef path) : SharedLibraryFile(path) {} - const SharedLibraryAtom *exports(StringRef name, bool isData) const override { + 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. @@ -273,35 +318,39 @@ public: std::error_code doParse() override { // Convert binary file to normalized mach-o. auto normFile = normalized::readBinary(_mb, _ctx->arch()); - if (std::error_code ec = normFile.getError()) - return ec; + if (auto ec = normFile.takeError()) + return llvm::errorToErrorCode(std::move(ec)); // Convert normalized mach-o to atoms. - if (std::error_code ec = normalized::normalizedDylibToAtoms( - this, **normFile, false)) - return ec; + if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false)) + return llvm::errorToErrorCode(std::move(ec)); return std::error_code(); } private: - const SharedLibraryAtom *exports(StringRef name, + 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()) { - if (!entry->second.atom) { - // Lazily create SharedLibraryAtom. - entry->second.atom = - new (allocator()) MachOSharedLibraryAtom(*this, name, installName, - entry->second.weakDef); - } - return entry->second.atom; + // 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) + if (atom.get()) return atom; } diff --git a/lib/ReaderWriter/MachO/FlatNamespaceFile.h b/lib/ReaderWriter/MachO/FlatNamespaceFile.h index 6c6a9262ba2e..76d295841c9d 100644 --- a/lib/ReaderWriter/MachO/FlatNamespaceFile.h +++ b/lib/ReaderWriter/MachO/FlatNamespaceFile.h @@ -25,34 +25,34 @@ public: FlatNamespaceFile(const MachOLinkingContext &context) : SharedLibraryFile("flat namespace") { } - const SharedLibraryAtom *exports(StringRef name, - bool dataSymbolOnly) const override { - _sharedLibraryAtoms.push_back( - new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(), - false)); - - return _sharedLibraryAtoms.back(); + OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override { + return new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(), + false); } StringRef getDSOName() const override { return "flat-namespace"; } - const AtomVector<DefinedAtom> &defined() const override { + const AtomRange<DefinedAtom> defined() const override { return _noDefinedAtoms; } - const AtomVector<UndefinedAtom> &undefined() const override { + const AtomRange<UndefinedAtom> undefined() const override { return _noUndefinedAtoms; } - const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { - return _sharedLibraryAtoms; + const AtomRange<SharedLibraryAtom> sharedLibrary() const override { + return _noSharedLibraryAtoms; } - const AtomVector<AbsoluteAtom> &absolute() const override { + const AtomRange<AbsoluteAtom> absolute() const override { return _noAbsoluteAtoms; } -private: - mutable AtomVector<SharedLibraryAtom> _sharedLibraryAtoms; + void clearAtoms() override { + _noDefinedAtoms.clear(); + _noUndefinedAtoms.clear(); + _noSharedLibraryAtoms.clear(); + _noAbsoluteAtoms.clear(); + } }; } // namespace mach_o diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp index a5816277dd71..6cdca0a9e055 100644 --- a/lib/ReaderWriter/MachO/GOTPass.cpp +++ b/lib/ReaderWriter/MachO/GOTPass.cpp @@ -54,6 +54,8 @@ 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; } @@ -91,10 +93,12 @@ class GOTPass : public Pass { public: GOTPass(const MachOLinkingContext &context) : _ctx(context), _archHandler(_ctx.archHandler()), - _file("<mach-o GOT Pass>") {} + _file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } private: - std::error_code perform(SimpleFile &mergedFile) override { + llvm::Error perform(SimpleFile &mergedFile) override { // Scan all references in all atoms. for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { @@ -130,7 +134,7 @@ private: for (const GOTEntryAtom *slot : entries) mergedFile.addAtom(*slot); - return std::error_code(); + return llvm::Error(); } bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) { @@ -167,7 +171,7 @@ private: const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; - MachOFile _file; + MachOFile &_file; llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT; }; diff --git a/lib/ReaderWriter/MachO/LayoutPass.cpp b/lib/ReaderWriter/MachO/LayoutPass.cpp index 0c14ee9d39ab..dd2ee8567ec9 100644 --- a/lib/ReaderWriter/MachO/LayoutPass.cpp +++ b/lib/ReaderWriter/MachO/LayoutPass.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Debug.h" #include <algorithm> #include <set> +#include <utility> using namespace lld; @@ -133,7 +134,7 @@ static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots, } } -static void printDefinedAtoms(const SimpleFile::DefinedAtomRange &atomRange) { +static void printDefinedAtoms(const File::AtomRange<DefinedAtom> &atomRange) { for (const DefinedAtom *atom : atomRange) { llvm::dbgs() << " file=" << atom->file().path() << ", name=" << atom->name() @@ -146,7 +147,7 @@ static void printDefinedAtoms(const SimpleFile::DefinedAtomRange &atomRange) { /// Verify that the followon chain is sane. Should not be called in /// release binary. -void LayoutPass::checkFollowonChain(SimpleFile::DefinedAtomRange &range) { +void LayoutPass::checkFollowonChain(const File::AtomRange<DefinedAtom> &range) { ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain"); // Verify that there's no cycle in follow-on chain. @@ -176,8 +177,8 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, const LayoutPass::SortKey &rc, LayoutPass::SortOverride customSorter, std::string &reason) { - const DefinedAtom *left = lc._atom; - const DefinedAtom *right = rc._atom; + const DefinedAtom *left = lc._atom.get(); + const DefinedAtom *right = rc._atom.get(); if (left == right) { reason = "same"; return false; @@ -252,14 +253,15 @@ static bool compareAtoms(const LayoutPass::SortKey &lc, bool result = compareAtomsSub(lc, rc, customSorter, reason); DEBUG({ StringRef comp = result ? "<" : ">="; - llvm::dbgs() << "Layout: '" << lc._atom->name() << "' " << comp << " '" - << rc._atom->name() << "' (" << reason << ")\n"; + 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(sorter) {} + : _registry(registry), _customSorter(std::move(sorter)) {} // Returns the atom immediately followed by the given atom in the followon // chain. @@ -329,12 +331,12 @@ void LayoutPass::setChainRoot(const DefinedAtom *targetAtom, /// 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(SimpleFile::DefinedAtomRange &range) { +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.resize(range.size()); - _followOnNexts.resize(range.size()); + _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 || @@ -397,7 +399,8 @@ void LayoutPass::buildFollowOnTable(SimpleFile::DefinedAtomRange &range) { /// 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(SimpleFile::DefinedAtomRange &range) { +void +LayoutPass::buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range) { ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap"); uint64_t index = 0; for (const DefinedAtom *ai : range) { @@ -417,31 +420,31 @@ void LayoutPass::buildOrdinalOverrideMap(SimpleFile::DefinedAtomRange &range) { } std::vector<LayoutPass::SortKey> -LayoutPass::decorate(SimpleFile::DefinedAtomRange &atomRange) const { +LayoutPass::decorate(File::AtomRange<DefinedAtom> &atomRange) const { std::vector<SortKey> ret; - for (const DefinedAtom *atom : atomRange) { - auto ri = _followOnRoots.find(atom); - auto oi = _ordinalOverrideMap.find(atom); - const DefinedAtom *root = (ri == _followOnRoots.end()) ? atom : ri->second; + 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(atom, root, override)); + ret.push_back(SortKey(std::move(atom), root, override)); } return ret; } -void LayoutPass::undecorate(SimpleFile::DefinedAtomRange &atomRange, +void LayoutPass::undecorate(File::AtomRange<DefinedAtom> &atomRange, std::vector<SortKey> &keys) const { size_t i = 0; for (SortKey &k : keys) - atomRange[i++] = k._atom; + atomRange[i++] = std::move(k._atom); } /// Perform the actual pass -std::error_code LayoutPass::perform(SimpleFile &mergedFile) { +llvm::Error LayoutPass::perform(SimpleFile &mergedFile) { DEBUG(llvm::dbgs() << "******** Laying out atoms:\n"); // sort the atoms ScopedTask task(getDefaultDomain(), "LayoutPass"); - SimpleFile::DefinedAtomRange atomRange = mergedFile.definedAtoms(); + File::AtomRange<DefinedAtom> atomRange = mergedFile.defined(); // Build follow on tables buildFollowOnTable(atomRange); @@ -471,7 +474,7 @@ std::error_code LayoutPass::perform(SimpleFile &mergedFile) { }); DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n"); - return std::error_code(); + return llvm::Error(); } void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) { diff --git a/lib/ReaderWriter/MachO/LayoutPass.h b/lib/ReaderWriter/MachO/LayoutPass.h index d6072b0ca4fb..c18777eded0a 100644 --- a/lib/ReaderWriter/MachO/LayoutPass.h +++ b/lib/ReaderWriter/MachO/LayoutPass.h @@ -33,11 +33,31 @@ namespace mach_o { class LayoutPass : public Pass { public: struct SortKey { - SortKey(const DefinedAtom *atom, const DefinedAtom *root, uint64_t override) - : _atom(atom), _root(root), _override(override) {} - const DefinedAtom *_atom; + 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, @@ -46,17 +66,17 @@ public: LayoutPass(const Registry ®istry, SortOverride sorter); /// Sorts atoms in mergedFile by content type then by command line order. - std::error_code perform(SimpleFile &mergedFile) override; + llvm::Error perform(SimpleFile &mergedFile) override; ~LayoutPass() override = default; private: // Build the followOn atoms chain as specified by the kindLayoutAfter // reference type - void buildFollowOnTable(SimpleFile::DefinedAtomRange &range); + void buildFollowOnTable(const File::AtomRange<DefinedAtom> &range); // Build a map of Atoms to ordinals for sorting the atoms - void buildOrdinalOverrideMap(SimpleFile::DefinedAtomRange &range); + void buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range); const Registry &_registry; SortOverride _customSorter; @@ -84,12 +104,13 @@ private: void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root); - std::vector<SortKey> decorate(SimpleFile::DefinedAtomRange &atomRange) const; - void undecorate(SimpleFile::DefinedAtomRange &atomRange, + 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(SimpleFile::DefinedAtomRange &range); + void checkFollowonChain(const File::AtomRange<DefinedAtom> &range); }; } // namespace mach_o diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index 1c97c5a39d3f..05375f145d34 100644 --- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -35,6 +35,7 @@ #endif using lld::mach_o::ArchHandler; +using lld::mach_o::MachOFile; using lld::mach_o::MachODylibFile; using namespace llvm::MachO; @@ -75,6 +76,35 @@ bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) { 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 }, @@ -139,44 +169,52 @@ bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size); } -MachOLinkingContext::MachOLinkingContext() - : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false), - _doNothing(false), _pie(false), _arch(arch_unknown), _os(OS::macOSX), - _osMinVersion(0), _pageZeroSize(0), _pageSize(4096), _baseAddress(0), - _stackSize(0), _compatibilityVersion(0), _currentVersion(0), - _flatNamespace(false), _undefinedMode(UndefinedMode::error), - _deadStrippableDylib(false), _printAtoms(false), _testingFileUsage(false), - _keepPrivateExterns(false), _demangle(false), _archHandler(nullptr), - _exportMode(ExportMode::globals), - _debugInfoMode(DebugInfoMode::addDebugMap), _orderFileEntries(0), - _flatNamespaceFile(nullptr) {} - -MachOLinkingContext::~MachOLinkingContext() {} +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) { + uint32_t minOSVersion, + bool exportDynamicSymbols) { _outputMachOType = type; _arch = arch; _os = os; _osMinVersion = minOSVersion; // If min OS not specified on command line, use reasonable defaults. - 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; + // 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; + } } } @@ -217,9 +255,10 @@ void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os, case OS::unknown: break; } + setGlobalsAreDeadStripRoots(exportDynamicSymbols); break; case llvm::MachO::MH_DYLIB: - setGlobalsAreDeadStripRoots(true); + setGlobalsAreDeadStripRoots(exportDynamicSymbols); break; case llvm::MachO::MH_BUNDLE: break; @@ -325,6 +364,11 @@ bool MachOLinkingContext::needsCompactUnwindPass() const { } } +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) @@ -368,9 +412,11 @@ bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const { return false; return _osMinVersion >= parsedVersion; case OS::unknown: - break; + // 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("target not configured for iOS or MacOSX"); + llvm_unreachable("invalid OS enum"); } bool MachOLinkingContext::addEntryPointLoadCommand() const { @@ -484,7 +530,7 @@ void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath, _frameworkDirs.push_back(fwPath); } -ErrorOr<StringRef> +llvm::Optional<StringRef> MachOLinkingContext::searchDirForLibrary(StringRef path, StringRef libName) const { SmallString<256> fullPath; @@ -494,7 +540,7 @@ MachOLinkingContext::searchDirForLibrary(StringRef path, llvm::sys::path::append(fullPath, libName); if (fileExists(fullPath)) return fullPath.str().copy(_allocator); - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } // Search for dynamic library @@ -509,21 +555,23 @@ MachOLinkingContext::searchDirForLibrary(StringRef path, if (fileExists(fullPath)) return fullPath.str().copy(_allocator); - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } -ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const { +llvm::Optional<StringRef> +MachOLinkingContext::searchLibrary(StringRef libName) const { SmallString<256> path; for (StringRef dir : searchDirs()) { - ErrorOr<StringRef> ec = searchDirForLibrary(dir, libName); - if (ec) - return ec; + llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName); + if (searchDir) + return searchDir; } - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } -ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) const{ +llvm::Optional<StringRef> +MachOLinkingContext::findPathForFramework(StringRef fwName) const{ SmallString<256> fullPath; for (StringRef dir : frameworkDirs()) { fullPath.assign(dir); @@ -532,7 +580,7 @@ ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) c return fullPath.str().copy(_allocator); } - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { @@ -589,6 +637,10 @@ bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { } 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); @@ -656,9 +708,8 @@ MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) { if (leafName.startswith("lib") && leafName.endswith(".dylib")) { // FIXME: Need to enhance searchLibrary() to only look for .dylib auto libPath = searchLibrary(leafName); - if (!libPath.getError()) { - return loadIndirectDylib(libPath.get()); - } + if (libPath) + return loadIndirectDylib(libPath.getValue()); } // Try full path with sysroot. @@ -990,4 +1041,77 @@ void MachOLinkingContext::finalizeInputFiles() { elements.push_back(llvm::make_unique<GroupEnd>(numLibs)); } +llvm::Error MachOLinkingContext::handleLoadedFile(File &file) { + auto *machoFile = dyn_cast<MachOFile>(&file); + if (!machoFile) + return llvm::Error(); + + // 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(); +} + } // end namespace lld diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h index cccf180f1043..92a21f7ef83d 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFile.h +++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h @@ -39,6 +39,9 @@ /// +-------+ /// +#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H +#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H + #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" #include "lld/ReaderWriter/MachOLinkingContext.h" @@ -50,9 +53,6 @@ #include "llvm/Support/MachO.h" #include "llvm/Support/YAMLTraits.h" -#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H -#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H - using llvm::BumpPtrAllocator; using llvm::yaml::Hex64; using llvm::yaml::Hex32; @@ -105,6 +105,9 @@ 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 { @@ -115,7 +118,7 @@ struct Section { StringRef sectionName; SectionType type; SectionAttr attributes; - uint16_t alignment; + SectionAlignment alignment; Hex64 address; ArrayRef<uint8_t> content; Relocations relocations; @@ -172,7 +175,8 @@ struct Segment { StringRef name; Hex64 address; Hex64 size; - VMProtect access; + VMProtect init_access; + VMProtect max_access; }; /// Only used in normalized final linked images to specify on which dylibs @@ -245,6 +249,8 @@ struct NormalizedFile { 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; @@ -252,6 +258,7 @@ struct NormalizedFile { 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; @@ -260,6 +267,7 @@ struct NormalizedFile { std::vector<BindLocation> weakBindingInfo; std::vector<BindLocation> lazyBindingInfo; std::vector<Export> exportInfo; + std::vector<uint8_t> functionStarts; std::vector<DataInCode> dataInCode; // TODO: @@ -281,40 +289,40 @@ 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. -ErrorOr<std::unique_ptr<NormalizedFile>> +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. -std::error_code writeBinary(const NormalizedFile &file, StringRef path); +llvm::Error writeBinary(const NormalizedFile &file, StringRef path); size_t headerAndLoadCommandsSize(const NormalizedFile &file); /// Parses a yaml encoded mach-o file to produce an in-memory normalized view. -ErrorOr<std::unique_ptr<NormalizedFile>> +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); -std::error_code +llvm::Error normalizedObjectToAtoms(MachOFile *file, const NormalizedFile &normalizedFile, bool copyRefs); -std::error_code +llvm::Error normalizedDylibToAtoms(MachODylibFile *file, const NormalizedFile &normalizedFile, bool copyRefs); /// Takes in-memory normalized dylib or object and parses it into lld::File -ErrorOr<std::unique_ptr<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. -ErrorOr<std::unique_ptr<NormalizedFile>> +llvm::Expected<std::unique_ptr<NormalizedFile>> normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &ctxt); diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp index 1013d3ddaef3..a17de5be1742 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp @@ -53,7 +53,7 @@ namespace mach_o { namespace normalized { // Utility to call a lambda expression on each load command. -static std::error_code forEachLoadCommand( +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(); @@ -67,15 +67,15 @@ static std::error_code forEachLoadCommand( slc = &lcCopy; } if ( (p + slc->cmdsize) > lcRange.end() ) - return make_error_code(llvm::errc::executable_format_error); + return llvm::make_error<GenericError>("Load command exceeds range"); if (func(slc->cmd, slc->cmdsize, p)) - return std::error_code(); + return llvm::Error(); p += slc->cmdsize; } - return std::error_code(); + return llvm::Error(); } static std::error_code appendRelocations(Relocations &relocs, StringRef buffer, @@ -199,7 +199,7 @@ bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch, } /// Reads a mach-o file and produces an in-memory normalized view. -ErrorOr<std::unique_ptr<NormalizedFile>> +llvm::Expected<std::unique_ptr<NormalizedFile>> readBinary(std::unique_ptr<MemoryBuffer> &mb, const MachOLinkingContext::Arch arch) { // Make empty NormalizedFile. @@ -220,7 +220,7 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, // Determine endianness and pointer size for mach-o file. bool is64, isBig; if (!isMachOHeader(mh, is64, isBig)) - return make_error_code(llvm::errc::executable_format_error); + return llvm::make_error<GenericError>("File is not a mach-o"); // Endian swap header, if needed. mach_header headerCopy; @@ -237,12 +237,13 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)); StringRef lcRange(lcStart, smh->sizeofcmds); if (lcRange.end() > (start + objSize)) - return make_error_code(llvm::errc::executable_format_error); + 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 make_dynamic_error_code(Twine("file is wrong architecture. Expected " + return llvm::make_error<GenericError>( + Twine("file is wrong architecture. Expected " "(" + MachOLinkingContext::nameFromArch(arch) + ") found (" + MachOLinkingContext::nameFromArch(f->arch) @@ -256,9 +257,9 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, // Pre-scan load commands looking for indirect symbol table. uint32_t indirectSymbolTableOffset = 0; uint32_t indirectSymbolTableCount = 0; - std::error_code ec = forEachLoadCommand(lcRange, lcCount, isBig, is64, - [&](uint32_t cmd, uint32_t size, - const char *lc) -> bool { + 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); @@ -268,7 +269,7 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, return false; }); if (ec) - return ec; + return std::move(ec); // Walk load commands looking for segments/sections and the symbol table. const data_in_code_entry *dataInCode = nullptr; @@ -380,11 +381,11 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, 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) { - const nlist_64 *sin = &symbols[i]; nlist_64 tempSym; - if (isBig != llvm::sys::IsBigEndianHost) { - tempSym = *sin; swapStruct(tempSym); sin = &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; @@ -471,11 +472,20 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, 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 ec; + return std::move(ec); if (dataInCode) { // Convert on-disk data_in_code_entry array to DataInCode vector. diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h index 1226860b021e..86823efa33c9 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#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/Core/Error.h" @@ -16,16 +18,54 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MachO.h" #include <system_error> -#ifndef LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H -#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H - 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; diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp index 4ecfece0629e..f3e159684e15 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp @@ -25,6 +25,8 @@ #include "MachONormalizedFileBinaryUtils.h" #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -35,7 +37,6 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Format.h" #include "llvm/Support/Host.h" -#include "llvm/Support/LEB128.h" #include "llvm/Support/MachO.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -50,6 +51,72 @@ 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_traits<TrieEdge> + : public ilist_default_traits<TrieEdge> { + private: + mutable ilist_half_node<TrieEdge> Sentinel; + public: + TrieEdge *createSentinel() const { + return static_cast<TrieEdge*>(&Sentinel); + } + void destroySentinel(TrieEdge *) const {} + + TrieEdge *provideInitialHead() const { return createSentinel(); } + TrieEdge *ensureHead(TrieEdge*) const { return createSentinel(); } + static void noteHead(TrieEdge*, TrieEdge*) {} + void deleteNode(TrieEdge *N) {} + + private: + void createNode(const 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); + 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; +}; + /// Utility class for writing a mach-o binary file given an in-memory /// normalized file. class MachOFileLayout { @@ -66,13 +133,13 @@ public: /// 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. - std::error_code writeBinary(StringRef path); + llvm::Error writeBinary(StringRef path); private: uint32_t loadCommandsSize(uint32_t &count); void buildFileOffsets(); void writeMachHeader(); - std::error_code writeLoadCommands(); + llvm::Error writeLoadCommands(); void writeSectionContent(); void writeRelocations(); void writeSymbolTable(); @@ -80,6 +147,7 @@ private: void writeBindingInfo(); void writeLazyBindingInfo(); void writeExportInfo(); + void writeFunctionStartsInfo(); void writeDataInCodeInfo(); void writeLinkEditContent(); void buildLinkEditInfo(); @@ -87,6 +155,7 @@ private: void buildBindInfo(); void buildLazyBindInfo(); void buildExportTrie(); + void computeFunctionStartsSize(); void computeDataInCodeSize(); void computeSymbolTableSizes(); void buildSectionRelocations(); @@ -110,83 +179,12 @@ private: }; template <typename T> - std::error_code writeSingleSegmentLoadCommand(uint8_t *&lc); - template <typename T> std::error_code writeSegmentLoadCommands(uint8_t *&lc); + llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc); + template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc); uint32_t pointerAlign(uint32_t value); static StringRef dyldPath(); - 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; - }; - - struct TrieNode; // Forward declaration. - - struct TrieEdge { - TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} - - StringRef _subString; - struct TrieNode *_child; - }; - - struct TrieNode { - 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); - bool updateOffset(uint32_t &offset); - void appendToByteBuffer(ByteBuffer &out); - - private: - StringRef _cummulativeString; - std::list<TrieEdge> _children; - uint64_t _address; - uint64_t _flags; - uint64_t _other; - StringRef _importedName; - uint32_t _trieOffset; - bool _hasExportInfo; - }; - struct SegExtraInfo { uint32_t fileOffset; uint32_t fileSize; @@ -209,6 +207,7 @@ private: uint32_t _countOfLoadCommands; uint32_t _endOfLoadCommands; uint32_t _startOfRelocations; + uint32_t _startOfFunctionStarts; uint32_t _startOfDataInCode; uint32_t _startOfSymbols; uint32_t _startOfIndirectSymbols; @@ -219,6 +218,7 @@ private: uint32_t _symbolTableUndefinesStartIndex; uint32_t _symbolStringPoolSize; uint32_t _symbolTableSize; + uint32_t _functionStartsSize; uint32_t _dataInCodeSize; uint32_t _indirectSymbolTableCount; // Used in object file creation only @@ -255,7 +255,7 @@ StringRef MachOFileLayout::dyldPath() { } uint32_t MachOFileLayout::pointerAlign(uint32_t value) { - return llvm::RoundUpToAlignment(value, _is64 ? 8 : 4); + return llvm::alignTo(value, _is64 ? 8 : 4); } @@ -280,7 +280,15 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) + file.sections.size() * sectsSize + sizeof(symtab_command); _countOfLoadCommands = 2; - if (!_file.dataInCode.empty()) { + if (file.hasMinVersionLoadCommand) { + _endOfLoadCommands += sizeof(version_min_command); + _countOfLoadCommands++; + } + if (!_file.functionStarts.empty()) { + _endOfLoadCommands += sizeof(linkedit_data_command); + _countOfLoadCommands++; + } + if (_file.generateDataInCodeLoadCommand) { _endOfLoadCommands += sizeof(linkedit_data_command); _countOfLoadCommands++; } @@ -292,7 +300,7 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) if (isZeroFillSection(sect.type)) _sectInfo[§].fileOffset = 0; else { - offset = llvm::RoundUpToAlignment(offset, sect.alignment); + offset = llvm::alignTo(offset, sect.alignment); _sectInfo[§].fileOffset = offset; offset += sect.content.size(); } @@ -301,11 +309,13 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) _endOfSectionsContent = offset; computeSymbolTableSizes(); + computeFunctionStartsSize(); computeDataInCodeSize(); // Align start of relocations. _startOfRelocations = pointerAlign(_endOfSectionsContent); - _startOfDataInCode = _startOfRelocations + relocCount * 8; + _startOfFunctionStarts = _startOfRelocations + relocCount * 8; + _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; _startOfSymbols = _startOfDataInCode + _dataInCodeSize; // Add Indirect symbol table. _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; @@ -346,7 +356,8 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size(); _startOfExportTrie = _endOfLazyBindingInfo; _endOfExportTrie = _startOfExportTrie + _exportTrie.size(); - _startOfDataInCode = _endOfExportTrie; + _startOfFunctionStarts = _endOfExportTrie; + _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize; _startOfSymbols = _startOfDataInCode + _dataInCodeSize; _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; _startOfSymbolStrings = _startOfIndirectSymbols @@ -368,6 +379,7 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n" << " startOfExportTrie=" << _startOfExportTrie << "\n" << " endOfExportTrie=" << _endOfExportTrie << "\n" + << " startOfFunctionStarts=" << _startOfFunctionStarts << "\n" << " startOfDataInCode=" << _startOfDataInCode << "\n" << " startOfSymbols=" << _startOfSymbols << "\n" << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" @@ -389,9 +401,6 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { count += _file.segments.size(); // Add section record for each section. size += _file.sections.size() * sectionSize; - // Add one LC_SEGMENT for implicit __LINKEDIT segment - size += segCommandSize; - ++count; // If creating a dylib, add LC_ID_DYLIB. if (_file.fileType == llvm::MachO::MH_DYLIB) { @@ -413,10 +422,25 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { ++count; } - // If main executable add LC_LOAD_DYLINKER and LC_MAIN + // 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; } @@ -433,8 +457,15 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { ++count; } - // Add LC_DATA_IN_CODE if needed - if (!_file.dataInCode.empty()) { + // Add LC_FUNCTION_STARTS if needed + if (!_file.functionStarts.empty()) { + 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; } @@ -517,7 +548,7 @@ void MachOFileLayout::buildFileOffsets() { llvm::dbgs() << "buildFileOffsets()\n"); for (const Segment &sg : _file.segments) { _segInfo[&sg].fileOffset = fileOffset; - if ((_seg1addr == INT64_MAX) && sg.access) + if ((_seg1addr == INT64_MAX) && sg.init_access) _seg1addr = sg.address; DEBUG_WITH_TYPE("MachOFileLayout", llvm::dbgs() << " segment=" << sg.name @@ -525,7 +556,7 @@ void MachOFileLayout::buildFileOffsets() { uint32_t segFileSize = 0; // A segment that is not zero-fill must use a least one page of disk space. - if (sg.access) + if (sg.init_access) segFileSize = _file.pageSize; for (const Section *s : _segInfo[&sg].sections) { uint32_t sectOffset = s->address - sg.address; @@ -539,10 +570,11 @@ void MachOFileLayout::buildFileOffsets() { << ", fileOffset=" << fileOffset << "\n"); } - _segInfo[&sg].fileSize = llvm::RoundUpToAlignment(segFileSize, - _file.pageSize); - fileOffset = llvm::RoundUpToAlignment(fileOffset + segFileSize, - _file.pageSize); + // 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; @@ -553,10 +585,23 @@ size_t MachOFileLayout::size() const { } 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 = MachOLinkingContext::cpuSubtypeFromArch(_file.arch); + mh->cpusubtype = cpusubtype; mh->filetype = _file.fileType; mh->ncmds = _countOfLoadCommands; mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands; @@ -583,7 +628,7 @@ uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) { } template <typename T> -std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { +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) @@ -623,15 +668,37 @@ std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { ++sout; } lc = next; - return std::error_code(); + return llvm::Error(); } template <typename T> -std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { +llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { uint32_t indirectSymRunningIndex = 0; for (const Segment &seg : _file.segments) { - // Write segment command with trailing sections. + // 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) @@ -642,8 +709,8 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { cmd->vmsize = seg.size; cmd->fileoff = segInfo.fileOffset; cmd->filesize = segInfo.fileSize; - cmd->maxprot = seg.access; - cmd->initprot = seg.access; + cmd->initprot = seg.init_access; + cmd->maxprot = seg.max_access; cmd->nsects = segInfo.sections.size(); cmd->flags = 0; if (_swap) @@ -671,36 +738,52 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { } lc = reinterpret_cast<uint8_t*>(next); } - // Add implicit __LINKEDIT segment - 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::RoundUpToAlignment(linkeditSize, _file.pageSize); - cmd->fileoff = _startOfLinkEdit; - cmd->filesize = linkeditSize; - cmd->maxprot = VM_PROT_READ; - cmd->initprot = VM_PROT_READ; - cmd->nsects = 0; - cmd->flags = 0; + return llvm::Error(); +} + +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(*cmd); - lc = next; - return std::error_code(); + swapStruct(*vm); + lc += sizeof(version_min_command); } -std::error_code MachOFileLayout::writeLoadCommands() { - std::error_code ec; +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) - ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc); - else - ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc); + 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; @@ -713,8 +796,25 @@ std::error_code MachOFileLayout::writeLoadCommands() { if (_swap) swapStruct(*st); lc += sizeof(symtab_command); - // Add LC_DATA_IN_CODE if needed. - if (_dataInCodeSize != 0) { + + // 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); @@ -726,10 +826,13 @@ std::error_code MachOFileLayout::writeLoadCommands() { } } else { // Final linked images have sections under segments. - if (_is64) - ec = writeSegmentLoadCommands<MachO64Trait>(lc); - else - ec = writeSegmentLoadCommands<MachO32Trait>(lc); + 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) { @@ -809,7 +912,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += sizeof(dysymtab_command); } - // If main executable, add LC_LOAD_DYLINKER and LC_MAIN. + // 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); @@ -822,14 +925,39 @@ std::error_code MachOFileLayout::writeLoadCommands() { 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. - entry_point_command* ep = reinterpret_cast<entry_point_command*>(lc); - ep->cmd = LC_MAIN; - ep->cmdsize = sizeof(entry_point_command); - ep->entryoff = _file.entryAddress - _seg1addr; - ep->stacksize = _file.stackSize; + // 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); + swapStruct(ep); + memcpy(lc, &ep, sizeof(entry_point_command)); lc += sizeof(entry_point_command); } @@ -865,8 +993,20 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += size; } - // Add LC_DATA_IN_CODE if needed. - if (_dataInCodeSize != 0) { + // 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); @@ -877,7 +1017,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { lc += sizeof(linkedit_data_command); } } - return ec; + return llvm::Error(); } void MachOFileLayout::writeSectionContent() { @@ -936,6 +1076,13 @@ void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols, } } +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) { @@ -1011,6 +1158,7 @@ void MachOFileLayout::buildLinkEditInfo() { buildLazyBindInfo(); buildExportTrie(); computeSymbolTableSizes(); + computeFunctionStartsSize(); computeDataInCodeSize(); } @@ -1079,9 +1227,9 @@ void MachOFileLayout::buildLazyBindInfo() { _lazyBindingInfo.align(_is64 ? 8 : 4); } -void MachOFileLayout::TrieNode::addSymbol(const Export& entry, - BumpPtrAllocator &allocator, - std::vector<TrieNode*> &allNodes) { +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; @@ -1110,7 +1258,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, abEdge._subString = abEdgeStr; abEdge._child = bNode; auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); - bNode->_children.push_back(std::move(*bcEdge)); + bNode->_children.insert(bNode->_children.end(), bcEdge); bNode->addSymbol(entry, allocator, allNodes); return; } @@ -1125,7 +1273,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, // 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.push_back(std::move(*newEdge)); + _children.insert(_children.end(), newEdge); DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() << "new TrieNode('" << entry.name << "') with edge '" << partialStr << "' from node='" @@ -1139,7 +1287,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, allNodes.push_back(newNode); } -bool MachOFileLayout::TrieNode::updateOffset(uint32_t& offset) { +bool TrieNode::updateOffset(uint32_t& offset) { uint32_t nodeSize = 1; // Length when no export info if (_hasExportInfo) { if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { @@ -1171,7 +1319,7 @@ bool MachOFileLayout::TrieNode::updateOffset(uint32_t& offset) { return result; } -void MachOFileLayout::TrieNode::appendToByteBuffer(ByteBuffer &out) { +void TrieNode::appendToByteBuffer(ByteBuffer &out) { if (_hasExportInfo) { if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { if (!_importedName.empty()) { @@ -1290,6 +1438,10 @@ void MachOFileLayout::computeSymbolTableSizes() { } } +void MachOFileLayout::computeFunctionStartsSize() { + _functionStartsSize = _file.functionStarts.size(); +} + void MachOFileLayout::computeDataInCodeSize() { _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry); } @@ -1297,6 +1449,7 @@ void MachOFileLayout::computeDataInCodeSize() { void MachOFileLayout::writeLinkEditContent() { if (_file.fileType == llvm::MachO::MH_OBJECT) { writeRelocations(); + writeFunctionStartsInfo(); writeDataInCodeInfo(); writeSymbolTable(); } else { @@ -1305,15 +1458,16 @@ void MachOFileLayout::writeLinkEditContent() { writeLazyBindingInfo(); // TODO: add weak binding info writeExportInfo(); + writeFunctionStartsInfo(); writeDataInCodeInfo(); writeSymbolTable(); } } -std::error_code MachOFileLayout::writeBinary(StringRef path) { +llvm::Error MachOFileLayout::writeBinary(StringRef path) { // Check for pending error from constructor. if (_ec) - return _ec; + return llvm::errorCodeToError(_ec); // Create FileOutputBuffer with calculated size. unsigned flags = 0; if (_file.fileType != llvm::MachO::MH_OBJECT) @@ -1321,23 +1475,22 @@ std::error_code MachOFileLayout::writeBinary(StringRef path) { ErrorOr<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr = llvm::FileOutputBuffer::create(path, size(), flags); if (std::error_code ec = fobOrErr.getError()) - return ec; + return llvm::errorCodeToError(ec); std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr; // Write content. _buffer = fob->getBufferStart(); writeMachHeader(); - std::error_code ec = writeLoadCommands(); - if (ec) + if (auto ec = writeLoadCommands()) return ec; writeSectionContent(); writeLinkEditContent(); fob->commit(); - return std::error_code(); + return llvm::Error(); } /// Takes in-memory normalized view and writes a mach-o object file. -std::error_code writeBinary(const NormalizedFile &file, StringRef path) { +llvm::Error writeBinary(const NormalizedFile &file, StringRef path) { MachOFileLayout layout(file); return layout.writeBinary(path); } diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index 575bc1a2b3a9..4775c75f7211 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -92,23 +92,27 @@ struct SegmentInfo { StringRef name; uint64_t address; uint64_t size; - uint32_t access; + 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), access(0), normalizedSegmentIndex(0) { + : 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) {} + _hasTLVDescriptors(false), _subsectionsViaSymbols(true) {} ~Util(); - void assignAtomsToSections(const lld::File &atomFile); + 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(); @@ -116,16 +120,29 @@ public: void copySectionInfo(NormalizedFile &file); void updateSectionInfo(NormalizedFile &file); void buildAtomToAddressMap(); - std::error_code addSymbols(const lld::File &atomFile, 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; @@ -147,9 +164,9 @@ private: uint8_t &segmentIndex, uint64_t &segmentStartAddr); const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom); const Atom *targetOfStub(const DefinedAtom *stubAtom); - std::error_code getSymbolTableRegion(const DefinedAtom* atom, - bool &inGlobalsRegion, - SymbolScope &symbolScope); + llvm::Error getSymbolTableRegion(const DefinedAtom* atom, + bool &inGlobalsRegion, + SymbolScope &symbolScope); void appendSection(SectionInfo *si, NormalizedFile &file); uint32_t sectionIndexForAtom(const Atom *atom); @@ -180,6 +197,10 @@ private: AtomToIndex _atomToSymbolIndex; std::vector<const Atom *> _machHeaderAliasAtoms; bool _hasTLVDescriptors; + bool _subsectionsViaSymbols; + bool _allSourceFilesHaveMinVersions = true; + LoadCommandType _minVersionCommandType = (LoadCommandType)0; + uint32_t _minVersion = 0; }; Util::~Util() { @@ -239,6 +260,7 @@ struct MachOFinalSectionFromAtomType { 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), @@ -261,6 +283,8 @@ const MachOFinalSectionFromAtomType sectsToAtomType[] = { 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, @@ -280,10 +304,11 @@ SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { continue; SectionAttr sectionAttrs = 0; switch (atomType) { + case DefinedAtom::typeMachHeader: case DefinedAtom::typeCode: case DefinedAtom::typeStub: case DefinedAtom::typeStubHelper: - sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; + sectionAttrs = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS; break; case DefinedAtom::typeThunkTLV: _hasTLVDescriptors = true; @@ -366,27 +391,85 @@ void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) { sect->size = offset + atom->size(); } -void Util::assignAtomsToSections(const lld::File &atomFile) { +void Util::processDefinedAtoms(const lld::File &atomFile) { for (const DefinedAtom *atom : atomFile.defined()) { - if (atom->contentType() == DefinedAtom::typeMachHeader) - _machHeaderAliasAtoms.push_back(atom); + 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 - appendAtom(sectionForAtom(atom), atom); + _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->access = VM_PROT_READ | VM_PROT_EXECUTE; - else if (segName.equals("__DATA")) - info->access = VM_PROT_READ | VM_PROT_WRITE; + info->init_access = VM_PROT_READ | VM_PROT_EXECUTE; else if (segName.equals("__PAGEZERO")) - info->access = 0; + 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; } @@ -436,6 +519,8 @@ void Util::organizeSections() { default: break; } + segmentForName("__LINKEDIT"); + // Group sections into segments. for (SectionInfo *si : _sectionInfos) { SegmentInfo *seg = segmentForName(si->segmentName); @@ -465,10 +550,10 @@ void Util::organizeSections() { void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) { seg->address = addr; for (SectionInfo *sect : seg->sections) { - sect->address = llvm::RoundUpToAlignment(addr, sect->alignment); + sect->address = llvm::alignTo(addr, sect->alignment); addr = sect->address + sect->size; } - seg->size = llvm::RoundUpToAlignment(addr - seg->address, _ctx.pageSize()); + seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); } // __TEXT segment lays out backwards so padding is at front after load commands. @@ -488,10 +573,10 @@ void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, // Start assigning section address starting at padded offset. addr += (padding + hlcSize); for (SectionInfo *sect : seg->sections) { - sect->address = llvm::RoundUpToAlignment(addr, sect->alignment); + sect->address = llvm::alignTo(addr, sect->alignment); addr = sect->address + sect->size; } - seg->size = llvm::RoundUpToAlignment(addr - seg->address, _ctx.pageSize()); + seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); } void Util::assignAddressesToSections(const NormalizedFile &file) { @@ -511,7 +596,7 @@ void Util::assignAddressesToSections(const NormalizedFile &file) { } else layoutSectionsInSegment(seg, address); - address = llvm::RoundUpToAlignment(address, _ctx.pageSize()); + address = llvm::alignTo(address, _ctx.pageSize()); } DEBUG_WITH_TYPE("WriterMachO-norm", llvm::dbgs() << "assignAddressesToSections()\n"; @@ -536,7 +621,8 @@ void Util::copySegmentInfo(NormalizedFile &file) { seg.name = sgi->name; seg.address = sgi->address; seg.size = sgi->size; - seg.access = sgi->access; + seg.init_access = sgi->init_access; + seg.max_access = sgi->max_access; file.segments.push_back(seg); } } @@ -583,11 +669,20 @@ void Util::copySectionContent(NormalizedFile &file) { continue; } // Copy content from atoms to content buffer for section. - uint8_t *sectionContent = file.ownedAllocations.Allocate<uint8_t>(si->size); - normSect->content = llvm::makeArrayRef(sectionContent, si->size); + 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) { - uint8_t *atomContent = reinterpret_cast<uint8_t*> - (§ionContent[ai.offsetInSection]); + 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); @@ -620,6 +715,11 @@ void Util::updateSectionInfo(NormalizedFile &file) { } void Util::copyEntryPointAddress(NormalizedFile &nFile) { + if (!_entryAtom) { + nFile.entryAddress = 0; + return; + } + if (_ctx.outputTypeHasEntry()) { if (_archHandler.isThumbFunction(*_entryAtom)) nFile.entryAddress = (_atomToAddress[_entryAtom] | 1); @@ -703,6 +803,8 @@ uint16_t Util::descBits(const DefinedAtom* atom) { } 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) { @@ -718,56 +820,56 @@ bool Util::AtomSorter::operator()(const AtomAndIndex &left, return (left.atom->name().compare(right.atom->name()) < 0); } -std::error_code Util::getSymbolTableRegion(const DefinedAtom* atom, - bool &inGlobalsRegion, - SymbolScope &scope) { +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 std::error_code(); + return llvm::Error(); case Atom::scopeLinkageUnit: if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) && _ctx.exportSymbolNamed(atom->name())) { - return make_dynamic_error_code(Twine("cannot export hidden symbol ") - + 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 std::error_code(); + return llvm::Error(); } } // scopeLinkageUnit symbols are no longer global once linked. scope = N_PEXT; inGlobalsRegion = false; - return std::error_code(); + return llvm::Error(); case Atom::scopeGlobal: if (_ctx.exportRestrictMode()) { if (_ctx.exportSymbolNamed(atom->name())) { scope = N_EXT; inGlobalsRegion = true; - return std::error_code(); + return llvm::Error(); } else { scope = N_PEXT; inGlobalsRegion = false; - return std::error_code(); + return llvm::Error(); } } else { scope = N_EXT; inGlobalsRegion = true; - return std::error_code(); + return llvm::Error(); } break; } llvm_unreachable("atom->scope() unknown enum value"); } -std::error_code Util::addSymbols(const lld::File &atomFile, - NormalizedFile &file) { +llvm::Error Util::addSymbols(const lld::File &atomFile, + NormalizedFile &file) { bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); // Mach-O symbol table has three regions: locals, globals, undefs. @@ -863,7 +965,7 @@ std::error_code Util::addSymbols(const lld::File &atomFile, file.undefinedSymbols.push_back(sym); } - return std::error_code(); + return llvm::Error(); } const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) { @@ -1055,7 +1157,53 @@ void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { } } +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 @@ -1183,7 +1331,7 @@ void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { uint32_t Util::fileFlags() { // FIXME: these need to determined at runtime. if (_ctx.outputMachOType() == MH_OBJECT) { - return MH_SUBSECTIONS_VIA_SYMBOLS; + return _subsectionsViaSymbols ? MH_SUBSECTIONS_VIA_SYMBOLS : 0; } else { uint32_t flags = MH_DYLDLINK; if (!_ctx.useFlatNamespace()) @@ -1203,12 +1351,12 @@ namespace mach_o { namespace normalized { /// Convert a set of Atoms into a normalized mach-o file. -ErrorOr<std::unique_ptr<NormalizedFile>> +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.assignAtomsToSections(atomFile); + util.processDefinedAtoms(atomFile); util.organizeSections(); std::unique_ptr<NormalizedFile> f(new NormalizedFile()); @@ -1220,6 +1368,35 @@ normalizedFromAtoms(const lld::File &atomFile, 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); @@ -1230,12 +1407,13 @@ normalizedFromAtoms(const lld::File &atomFile, util.updateSectionInfo(normFile); util.copySectionContent(normFile); if (auto ec = util.addSymbols(atomFile, normFile)) { - return ec; + 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); diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index f9499b603214..fc760a3eddd0 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -88,6 +88,8 @@ const MachORelocatableSectionToAtomType sectsToAtomType[] = { 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), @@ -180,6 +182,8 @@ void sectionParseInfo(DefinedAtom::ContentType atomType, atomizeCU), ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent, atomizePointerSize), + ENTRY(typeObjC2CategoryList, 4, scopeTranslationUnit, mergeByContent, + atomizePointerSize), ENTRY(typeUnknown, 1, scopeGlobal, mergeNo, atomizeAtSymbols) }; @@ -265,11 +269,11 @@ void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion, } } -std::error_code processSymboledSection(DefinedAtom::ContentType atomType, - const Section §ion, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { +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) { @@ -316,7 +320,7 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType, // If section has no symbols and no content, there are no atoms. if (symbols.empty() && section.content.empty()) - return std::error_code(); + return llvm::Error(); if (symbols.empty()) { // Section has no symbols, put all content in one anoymous atom. @@ -360,22 +364,22 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType, file.eachAtomInSection(section, [&](MachODefinedAtom *atom, uint64_t offset)->void { if (prevAtom) - prevAtom->addReference(0, Reference::kindLayoutAfter, atom, 0, + prevAtom->addReference(Reference::KindNamespace::all, Reference::KindArch::all, - Reference::KindNamespace::all); + Reference::kindLayoutAfter, 0, atom, 0); prevAtom = atom; }); } - return std::error_code(); + return llvm::Error(); } -std::error_code processSection(DefinedAtom::ContentType atomType, - const Section §ion, - bool customSectionName, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { +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); @@ -388,12 +392,13 @@ std::error_code processSection(DefinedAtom::ContentType atomType, // Validate section size. if ((section.content.size() % sizeMultiple) != 0) - return make_dynamic_error_code(Twine("Section ") + section.segmentName - + "/" + section.sectionName - + " has size (" - + Twine(section.content.size()) - + ") which is not a multiple of " - + Twine(sizeMultiple) ); + 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. @@ -435,13 +440,13 @@ std::error_code processSection(DefinedAtom::ContentType atomType, // Break section up into dwarf unwind CFIs (FDE or CIE). size = read32(§ion.content[offset], isBig) + 4; if (offset+size > section.content.size()) { - return make_dynamic_error_code(Twine(Twine("Section ") - + section.segmentName - + "/" + section.sectionName - + " is malformed. Size of CFI " - "starting at offset (" - + Twine(offset) - + ") is past end of section.")); + 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: @@ -456,10 +461,11 @@ std::error_code processSection(DefinedAtom::ContentType atomType, break; } if (size == 0) { - return make_dynamic_error_code(Twine("Section ") + section.segmentName - + "/" + section.sectionName - + " is malformed. The last atom is " - "not zero terminated."); + 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. Concatentate those two @@ -477,7 +483,7 @@ std::error_code processSection(DefinedAtom::ContentType atomType, offset += size; } } - return std::error_code(); + return llvm::Error(); } const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile, @@ -509,23 +515,23 @@ findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file, // Walks all relocations for a section in a normalized .o file and // creates corresponding lld::Reference objects. -std::error_code convertRelocs(const Section §ion, - const NormalizedFile &normalizedFile, - bool scatterable, - MachOFile &file, - ArchHandler &handler) { +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) - -> std::error_code { + -> llvm::Error { if (sectIndex > normalizedFile.sections.size()) - return make_dynamic_error_code(Twine("out of range section " + 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 make_dynamic_error_code(Twine("address (" + Twine(addr) + return llvm::make_error<GenericError>(Twine("address (" + Twine(addr) + ") is not in any section")); } else { sect = &normalizedFile.sections[sectIndex-1]; @@ -534,12 +540,12 @@ std::error_code convertRelocs(const Section §ion, uint64_t offsetInSect = addr - sect->address; *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); *addend = offsetInTarget; - return std::error_code(); + return llvm::Error(); }; // Utility function for ArchHandler to find atom by its symbol index. auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result) - -> std::error_code { + -> llvm::Error { // Find symbol from index. const Symbol *sym = nullptr; uint32_t numLocal = normalizedFile.localSymbols.size(); @@ -552,13 +558,13 @@ std::error_code convertRelocs(const Section §ion, } else if (symbolIndex < numLocal+numGlobal+numUndef) { sym = &normalizedFile.undefinedSymbols[symbolIndex-numLocal-numGlobal]; } else { - return make_dynamic_error_code(Twine("symbol index (") + 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 make_dynamic_error_code(Twine("symbol section index (") + 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; @@ -566,19 +572,19 @@ std::error_code convertRelocs(const Section §ion, targetOffsetInSect); if (target) { *result = target; - return std::error_code(); + return llvm::Error(); } - return make_dynamic_error_code("no atom found for defined symbol"); + 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 std::error_code(); + return llvm::Error(); } - return make_dynamic_error_code("no undefined atom found for sym"); + return llvm::make_error<GenericError>("no undefined atom found for sym"); } else { // Search undefs - return make_dynamic_error_code("no atom found for symbol"); + return llvm::make_error<GenericError>("no atom found for symbol"); } }; @@ -589,7 +595,8 @@ std::error_code convertRelocs(const Section §ion, const Relocation &reloc = *it; // Find atom this relocation is in. if (reloc.offset > section.content.size()) - return make_dynamic_error_code(Twine("r_address (") + Twine(reloc.offset) + return llvm::make_error<GenericError>( + Twine("r_address (") + Twine(reloc.offset) + ") is larger than section size (" + Twine(section.content.size()) + ")"); uint32_t offsetInAtom; @@ -602,68 +609,74 @@ std::error_code convertRelocs(const Section §ion, const lld::Atom *target = nullptr; Reference::Addend addend = 0; Reference::KindValue kind; - std::error_code relocErr; if (handler.isPairedReloc(reloc)) { // Handle paired relocations together. const Relocation &reloc2 = *++it; - relocErr = handler.getPairReferenceInfo( + auto relocErr = handler.getPairReferenceInfo( reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, atomByAddr, atomBySymbol, &kind, &target, &addend); if (relocErr) { - return make_dynamic_error_code( - Twine("bad relocation (") + relocErr.message() - + ") 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))) - + ")" ); + 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. - relocErr = handler.getReferenceInfo( + auto relocErr = handler.getReferenceInfo( reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr, atomBySymbol, &kind, &target, &addend); if (relocErr) { - return make_dynamic_error_code( - Twine("bad relocation (") + relocErr.message() - + ") 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))) - + ")" ); + 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(offsetInAtom, kind, target, addend, - handler.kindArch()); + inAtom->addReference(Reference::KindNamespace::mach_o, + handler.kindArch(), + kind, offsetInAtom, target, addend); } - return std::error_code(); + return llvm::Error(); } bool isDebugInfoSection(const Section §ion) { @@ -684,44 +697,81 @@ static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { struct CIEInfo { bool _augmentationDataPresent = false; - bool _mayHaveLSDA = 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 std::error_code processAugmentationString(const uint8_t *augStr, - CIEInfo &cieInfo, - unsigned *len = nullptr) { +static llvm::Error processAugmentationString(const uint8_t *augStr, + CIEInfo &cieInfo, + unsigned &len) { if (augStr[0] == '\0') { - if (len) - *len = 1; - return std::error_code(); + len = 1; + return llvm::Error(); } if (augStr[0] != 'z') - return make_dynamic_error_code("expected 'z' at start of augmentation " - "string"); + 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._mayHaveLSDA = true; + 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; - } else + 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; } - if (len) - *len = idx + 1; - return std::error_code(); + cieInfo._augmentationDataLength = offsetInAugmentationData; + + len = idx + 1; + return llvm::Error(); } -static std::error_code processCIE(const NormalizedFile &normalizedFile, - MachODefinedAtom *atom, - CIEInfoMap &cieInfos) { +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(); @@ -734,31 +784,170 @@ static std::error_code processCIE(const NormalizedFile &normalizedFile, uint64_t versionField = cieIDField + sizeof(uint32_t); uint64_t augmentationStringField = versionField + sizeof(uint8_t); + unsigned augmentationStringLength = 0; if (auto err = processAugmentationString(frameData + augmentationStringField, - cieInfo)) + 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 std::error_code(); + return llvm::Error(); } -static std::error_code processFDE(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler, - const Section *ehFrameSection, - MachODefinedAtom *atom, - uint64_t offset, - const CIEInfoMap &cieInfos) { +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()) - return std::error_code(); + 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; - const uint8_t *frameData = atom->rawContent().data(); uint32_t size = read32(frameData, isBig); uint64_t cieFieldInFDE = size == 0xffffffffU ? sizeof(uint32_t) + sizeof(uint64_t) @@ -770,13 +959,11 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; cieAddress -= cieDelta; - Reference::Addend addend; - const MachODefinedAtom *cie = - findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); - atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, - addend, handler.kindArch()); - - assert(cie && cie->contentType() == DefinedAtom::typeCFI && !addend && + 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; @@ -792,10 +979,9 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; rangeStart += functionFromFDE; - const Atom *func = - findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); - atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), - func, addend, handler.kindArch()); + verifyOrAddReference(rangeStart, + handler.unwindRefToFunctionKind(), + rangeFieldInFDE, true); // Handle the augmentation data if there is any. if (cieInfo._augmentationDataPresent) { @@ -807,7 +993,7 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE, &lengthFieldSize); - if (cieInfo._mayHaveLSDA && augmentationDataLength > 0) { + if (cieInfo._offsetOfLSDA != ~0U && augmentationDataLength > 0) { // Look at the augmentation data field. uint64_t augmentationDataFieldInFDE = @@ -818,20 +1004,19 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t lsdaStart = ehFrameSection->address + offset + augmentationDataFieldInFDE + lsdaFromFDE; - const Atom *lsda = - findAtomCoveringAddress(normalizedFile, file, lsdaStart, &addend); - atom->addReference(augmentationDataFieldInFDE, - handler.unwindRefToFunctionKind(), - lsda, addend, handler.kindArch()); + + verifyOrAddReference(lsdaStart, + handler.unwindRefToFunctionKind(), + augmentationDataFieldInFDE, true); } } - return std::error_code(); + return llvm::Error(); } -std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler) { +llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler) { const Section *ehFrameSection = nullptr; for (auto §ion : normalizedFile.sections) @@ -843,9 +1028,9 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, // No __eh_frame so nothing to do. if (!ehFrameSection) - return std::error_code(); + return llvm::Error(); - std::error_code ehFrameErr; + llvm::Error ehFrameErr; CIEInfoMap cieInfos; file.eachAtomInSection(*ehFrameSection, @@ -858,7 +1043,8 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); if (ArchHandler::isDwarfCIE(isBig, atom)) - ehFrameErr = processCIE(normalizedFile, atom, cieInfos); + ehFrameErr = processCIE(normalizedFile, file, handler, ehFrameSection, + atom, offset, cieInfos); else ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection, atom, offset, cieInfos); @@ -867,24 +1053,66 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, 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(); +} + /// Converts normalized mach-o file into an lld::File and lld::Atoms. -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> objectToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { std::unique_ptr<MachOFile> file(new MachOFile(path)); - if (std::error_code ec = normalizedObjectToAtoms( - file.get(), normalizedFile, copyRefs)) - return ec; + if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs)) + return std::move(ec); return std::unique_ptr<File>(std::move(file)); } -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { // Instantiate SharedLibraryFile object. std::unique_ptr<MachODylibFile> file(new MachODylibFile(path)); - normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs); + if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs)) + return std::move(ec); return std::unique_ptr<File>(std::move(file)); } @@ -892,7 +1120,12 @@ dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, namespace normalized { -std::error_code +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) { @@ -905,12 +1138,23 @@ normalizedObjectToAtoms(MachOFile *file, DEBUG(llvm::dbgs() << "Creating atoms: "; sect.dump()); 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 (std::error_code ec = - processSection(atomType, sect, customSectionName, normalizedFile, - *file, scatterable, copyRefs)) + if (auto ec = processSection(atomType, sect, customSectionName, + normalizedFile, *file, scatterable, copyRefs)) return ec; } // Create atoms from undefined symbols. @@ -931,9 +1175,9 @@ normalizedObjectToAtoms(MachOFile *file, for (auto § : normalizedFile.sections) { if (isDebugInfoSection(sect)) continue; - if (std::error_code ec = convertRelocs(sect, normalizedFile, scatterable, - *file, *handler)) - return ec; + if (llvm::Error ec = convertRelocs(sect, normalizedFile, scatterable, + *file, *handler)) + return ec; } // Add additional arch-specific References @@ -945,7 +1189,7 @@ normalizedObjectToAtoms(MachOFile *file, // 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 (std::error_code ec = addEHFrameReferences(normalizedFile, *file, *handler)) + if (auto ec = addEHFrameReferences(normalizedFile, *file, *handler)) return ec; // Process mach-o data-in-code regions array. That information is encoded in @@ -955,25 +1199,26 @@ normalizedObjectToAtoms(MachOFile *file, ++nextIndex; const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset); if (!s) { - return make_dynamic_error_code(Twine("LC_DATA_IN_CODE address (" - + Twine(entry.offset) - + ") is not in any section")); + 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 make_dynamic_error_code(Twine("LC_DATA_IN_CODE entry (offset=" - + Twine(entry.offset) - + ", length=" - + Twine(entry.length) - + ") crosses atom boundary.")); + 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(offsetInAtom, - handler->dataInCodeTransitionStart(*atom), atom, - entry.kind, handler->kindArch()); + 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()) { @@ -987,19 +1232,26 @@ normalizedObjectToAtoms(MachOFile *file, continue; // Add reference that marks end of data-in-code. - atom->addReference(offsetInAtom+entry.length, - handler->dataInCodeTransitionEnd(*atom), atom, 0, - handler->kindArch()); + 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(); } - return std::error_code(); + return llvm::Error(); } -std::error_code +llvm::Error normalizedDylibToAtoms(MachODylibFile *file, const NormalizedFile &normalizedFile, bool copyRefs) { @@ -1027,7 +1279,7 @@ normalizedDylibToAtoms(MachODylibFile *file, if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB) file->addReExportedDylib(dep.path); } - return std::error_code(); + return llvm::Error(); } void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, @@ -1058,7 +1310,7 @@ void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, llvm_unreachable("content type not yet supported"); } -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { switch (normalizedFile.fileType) { diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp index 0b92a68eeae8..66be77173983 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp @@ -237,6 +237,29 @@ struct ScalarBitSetTraits<SectionAttr> { } }; +/// 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 bool mustQuote(StringRef) { return false; } +}; + template <> struct ScalarEnumerationTraits<NListType> { static void enumeration(IO &io, NListType &value) { @@ -276,7 +299,7 @@ struct MappingTraits<Section> { io.mapRequired("section", sect.sectionName); io.mapRequired("type", sect.type); io.mapOptional("attributes", sect.attributes); - io.mapOptional("alignment", sect.alignment, (uint16_t)1); + io.mapOptional("alignment", sect.alignment, (SectionAlignment)1); io.mapRequired("address", sect.address); if (isZeroFillSection(sect.type)) { // S_ZEROFILL sections use "size:" instead of "content:" @@ -311,6 +334,8 @@ struct MappingTraits<Section> { 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); @@ -504,10 +529,11 @@ struct ScalarTraits<VMProtect> { 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("access", seg.access); + 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); } }; @@ -524,6 +550,14 @@ struct ScalarEnumerationTraits<LoadCommandType> { 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); } }; @@ -692,6 +726,7 @@ struct MappingTraits<NormalizedFile> { 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); @@ -731,8 +766,21 @@ bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io, info->_normalizeMachOFile = &nf; MappingTraits<NormalizedFile>::mapping(io, nf); // Step 2: parse normalized mach-o struct into atoms. - ErrorOr<std::unique_ptr<lld::File>> foe = normalizedToAtoms(nf, info->_path, - true); + 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) @@ -742,16 +790,8 @@ bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io, return false; } info->_normalizeMachOFile = nullptr; - - if (foe) { - // Transfer ownership to "out" File parameter. - std::unique_ptr<lld::File> f = std::move(foe.get()); - file = f.release(); - return true; - } else { - io.setError(foe.getError().message()); - return false; - } + file = fileOrError->release(); + return true; } @@ -759,7 +799,7 @@ bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io, namespace normalized { /// Parses a yaml encoded mach-o file to produce an in-memory normalized view. -ErrorOr<std::unique_ptr<NormalizedFile>> +llvm::Expected<std::unique_ptr<NormalizedFile>> readYaml(std::unique_ptr<MemoryBuffer> &mb) { // Make empty NormalizedFile. std::unique_ptr<NormalizedFile> f(new NormalizedFile()); @@ -773,8 +813,9 @@ readYaml(std::unique_ptr<MemoryBuffer> &mb) { yin >> *f; // Return error if there were parsing problems. - if (yin.error()) - return make_error_code(lld::YamlReaderError::illegal_value); + 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); diff --git a/lib/ReaderWriter/MachO/MachOPasses.h b/lib/ReaderWriter/MachO/MachOPasses.h index a73785418d5f..cd01d4aa2c93 100644 --- a/lib/ReaderWriter/MachO/MachOPasses.h +++ b/lib/ReaderWriter/MachO/MachOPasses.h @@ -21,6 +21,7 @@ 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 diff --git a/lib/ReaderWriter/MachO/ObjCPass.cpp b/lib/ReaderWriter/MachO/ObjCPass.cpp new file mode 100644 index 000000000000..ba24b3fecdf4 --- /dev/null +++ b/lib/ReaderWriter/MachO/ObjCPass.cpp @@ -0,0 +1,128 @@ +//===- lib/ReaderWriter/MachO/ObjCPass.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.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, + 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); + } + + ~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(); + } + +private: + + const DefinedAtom* getImageInfo() { + return new (_file.allocator()) ObjCImageInfoAtom(_file, + _ctx.objcConstraint(), + _ctx.swiftVersion()); + } + + const MachOLinkingContext &_ctx; + MachOFile &_file; +}; + + + +void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) { + pm.add(llvm::make_unique<ObjCPass>(ctx)); +} + +} // end namespace mach_o +} // end namespace lld diff --git a/lib/ReaderWriter/MachO/SectCreateFile.h b/lib/ReaderWriter/MachO/SectCreateFile.h index 2e6e97c5433f..49e65f63151d 100644 --- a/lib/ReaderWriter/MachO/SectCreateFile.h +++ b/lib/ReaderWriter/MachO/SectCreateFile.h @@ -31,6 +31,8 @@ public: _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; } @@ -59,7 +61,7 @@ public: std::unique_ptr<MemoryBuffer> _content; }; - SectCreateFile() : File("sectcreate", kindObject) {} + SectCreateFile() : File("sectcreate", kindSectCreateObject) {} void addSection(StringRef seg, StringRef sect, std::unique_ptr<MemoryBuffer> content) { @@ -67,22 +69,29 @@ public: new (allocator()) SectCreateAtom(*this, seg, sect, std::move(content))); } - const AtomVector<DefinedAtom> &defined() const override { + const AtomRange<DefinedAtom> defined() const override { return _definedAtoms; } - const AtomVector<UndefinedAtom> &undefined() const override { + const AtomRange<UndefinedAtom> undefined() const override { return _noUndefinedAtoms; } - const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + const AtomRange<SharedLibraryAtom> sharedLibrary() const override { return _noSharedLibraryAtoms; } - const AtomVector<AbsoluteAtom> &absolute() const override { + const AtomRange<AbsoluteAtom> absolute() const override { return _noAbsoluteAtoms; } + void clearAtoms() override { + _definedAtoms.clear(); + _noUndefinedAtoms.clear(); + _noSharedLibraryAtoms.clear(); + _noAbsoluteAtoms.clear(); + } + private: AtomVector<DefinedAtom> _definedAtoms; }; diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp index df29e37c183b..cd5367146658 100644 --- a/lib/ReaderWriter/MachO/ShimPass.cpp +++ b/lib/ReaderWriter/MachO/ShimPass.cpp @@ -42,9 +42,12 @@ class ShimPass : public Pass { public: ShimPass(const MachOLinkingContext &context) : _ctx(context), _archHandler(_ctx.archHandler()), - _stubInfo(_archHandler.stubInfo()), _file("<mach-o shim pass>") {} + _stubInfo(_archHandler.stubInfo()), + _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } - std::error_code perform(SimpleFile &mergedFile) override { + llvm::Error perform(SimpleFile &mergedFile) override { // Scan all references in all atoms. for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { @@ -63,7 +66,7 @@ public: } // Exit early if no shims needed. if (_targetToShim.empty()) - return std::error_code(); + return llvm::Error(); // Sort shim atoms so the layout order is stable. std::vector<const DefinedAtom *> shims; @@ -80,7 +83,7 @@ public: for (const DefinedAtom *shim : shims) mergedFile.addAtom(*shim); - return std::error_code(); + return llvm::Error(); } private: @@ -112,7 +115,7 @@ private: const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; const ArchHandler::StubInfo &_stubInfo; - MachOFile _file; + MachOFile &_file; llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim; }; diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp index 1f61256a5b79..d53b78b24d14 100644 --- a/lib/ReaderWriter/MachO/StubsPass.cpp +++ b/lib/ReaderWriter/MachO/StubsPass.cpp @@ -37,6 +37,8 @@ public: LazyPointerAtom(const File &file, bool is64) : SimpleDefinedAtom(file), _is64(is64) { } + ~LazyPointerAtom() override = default; + ContentType contentType() const override { return DefinedAtom::typeLazyPointer; } @@ -68,11 +70,13 @@ private: // class NonLazyPointerAtom : public SimpleDefinedAtom { public: - NonLazyPointerAtom(const File &file, bool is64) - : SimpleDefinedAtom(file), _is64(is64) { } + NonLazyPointerAtom(const File &file, bool is64, ContentType contentType) + : SimpleDefinedAtom(file), _is64(is64), _contentType(contentType) { } + + ~NonLazyPointerAtom() override = default; ContentType contentType() const override { - return DefinedAtom::typeGOT; + return _contentType; } Alignment alignment() const override { @@ -95,6 +99,7 @@ public: private: const bool _is64; + const ContentType _contentType; }; // @@ -105,6 +110,8 @@ public: StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo) : SimpleDefinedAtom(file), _stubInfo(stubInfo){ } + ~StubAtom() override = default; + ContentType contentType() const override { return DefinedAtom::typeStub; } @@ -137,6 +144,8 @@ public: StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo) : SimpleDefinedAtom(file), _stubInfo(stubInfo) { } + ~StubHelperAtom() override = default; + ContentType contentType() const override { return DefinedAtom::typeStubHelper; } @@ -170,12 +179,14 @@ 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.codeAlignment; + return 1 << _stubInfo.stubHelperCommonAlignment; } uint64_t size() const override { @@ -199,12 +210,15 @@ class StubsPass : public Pass { public: StubsPass(const MachOLinkingContext &context) : _ctx(context), _archHandler(_ctx.archHandler()), - _stubInfo(_archHandler.stubInfo()), _file("<mach-o Stubs pass>") {} + _stubInfo(_archHandler.stubInfo()), + _file(*_ctx.make_file<MachOFile>("<mach-o Stubs pass>")) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } - std::error_code perform(SimpleFile &mergedFile) override { + llvm::Error perform(SimpleFile &mergedFile) override { // Skip this pass if output format uses text relocations instead of stubs. if (!this->noTextRelocs()) - return std::error_code(); + return llvm::Error(); // Scan all references in all atoms. for (const DefinedAtom *atom : mergedFile.defined()) { @@ -231,15 +245,17 @@ public: // Exit early if no stubs needed. if (_targetToUses.empty()) - return std::error_code(); + return llvm::Error(); // 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()); + new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(), + _stubInfo.stubHelperImageCacheContentType); SimpleDefinedAtom *helperBinderNLPAtom = - new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit()); + new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(), + _stubInfo.stubHelperImageCacheContentType); addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, helperCacheNLPAtom); addOptReference( @@ -307,7 +323,7 @@ public: lazyOffset += target->name().size() + 12; } - return std::error_code(); + return llvm::Error(); } private: @@ -351,7 +367,7 @@ private: const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; const ArchHandler::StubInfo &_stubInfo; - MachOFile _file; + MachOFile &_file; TargetToUses _targetToUses; }; diff --git a/lib/ReaderWriter/MachO/TLVPass.cpp b/lib/ReaderWriter/MachO/TLVPass.cpp index aba222edcd27..7a8496c20a4e 100644 --- a/lib/ReaderWriter/MachO/TLVPass.cpp +++ b/lib/ReaderWriter/MachO/TLVPass.cpp @@ -30,6 +30,8 @@ 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; } @@ -65,10 +67,12 @@ class TLVPass : public Pass { public: TLVPass(const MachOLinkingContext &context) : _ctx(context), _archHandler(_ctx.archHandler()), - _file("<mach-o TLV Pass>") {} + _file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) { + _file.setOrdinal(_ctx.getNextOrdinalAndIncrement()); + } private: - std::error_code perform(SimpleFile &mergedFile) override { + llvm::Error perform(SimpleFile &mergedFile) override { bool allowTLV = _ctx.minOS("10.7", "1.0"); for (const DefinedAtom *atom : mergedFile.defined()) { @@ -77,7 +81,7 @@ private: continue; if (!allowTLV) - return make_dynamic_error_code( + return llvm::make_error<GenericError>( "targeted OS version does not support use of thread local " "variables in " + atom->name() + " for architecture " + _ctx.archName()); @@ -103,7 +107,7 @@ private: for (const TLVPEntryAtom *slot : entries) mergedFile.addAtom(*slot); - return std::error_code(); + return llvm::Error(); } const DefinedAtom *makeTLVPEntry(const Atom *target) { @@ -124,7 +128,7 @@ private: const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; - MachOFile _file; + MachOFile &_file; llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP; }; diff --git a/lib/ReaderWriter/MachO/WriterMachO.cpp b/lib/ReaderWriter/MachO/WriterMachO.cpp index cce0a179608c..f08487f21ac1 100644 --- a/lib/ReaderWriter/MachO/WriterMachO.cpp +++ b/lib/ReaderWriter/MachO/WriterMachO.cpp @@ -28,17 +28,18 @@ class MachOWriter : public Writer { public: MachOWriter(const MachOLinkingContext &ctxt) : _ctx(ctxt) {} - std::error_code writeFile(const lld::File &file, StringRef path) override { + llvm::Error writeFile(const lld::File &file, StringRef path) override { // Construct empty normalized file from atoms. - ErrorOr<std::unique_ptr<NormalizedFile>> nFile = + llvm::Expected<std::unique_ptr<NormalizedFile>> nFile = normalized::normalizedFromAtoms(file, _ctx); - if (std::error_code ec = nFile.getError()) + 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); - yamlWriter->writeFile(file, "-"); + if (auto ec = yamlWriter->writeFile(file, "-")) + return ec; } // Write normalized file as mach-o binary. |