Index: vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler.h =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler.h (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler.h (revision 326905) @@ -1,319 +1,323 @@ //===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H #define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H #include "Atoms.h" #include "File.h" #include "MachONormalizedFile.h" #include "lld/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" namespace lld { namespace mach_o { /// /// The ArchHandler class handles all architecture specific aspects of /// mach-o linking. /// class ArchHandler { public: virtual ~ArchHandler(); /// There is no public interface to subclasses of ArchHandler, so this /// is the only way to instantiate an ArchHandler. static std::unique_ptr create(MachOLinkingContext::Arch arch); /// Get (arch specific) kind strings used by Registry. virtual const Registry::KindStrings *kindStrings() = 0; /// Convert mach-o Arch to Reference::KindArch. virtual Reference::KindArch kindArch() = 0; /// Used by StubPass to update References to shared library functions /// to be references to a stub. virtual bool isCallSite(const Reference &) = 0; /// Used by GOTPass to locate GOT References virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) { return false; } /// Used by TLVPass to locate TLV References. virtual bool isTLVAccess(const Reference &) const { return false; } /// Used by the TLVPass to update TLV References. virtual void updateReferenceToTLV(const Reference *) {} /// Used by ShimPass to insert shims in branches that switch mode. virtual bool isNonCallBranch(const Reference &) = 0; /// Used by GOTPass to update GOT References virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {} /// Does this architecture make use of __unwind_info sections for exception /// handling? If so, it will need a separate pass to create them. virtual bool needsCompactUnwind() = 0; /// Returns the kind of reference to use to synthesize a 32-bit image-offset /// value, used in the __unwind_info section. virtual Reference::KindValue imageOffsetKind() = 0; /// Returns the kind of reference to use to synthesize a 32-bit image-offset /// indirect value. Used for personality functions in the __unwind_info /// section. virtual Reference::KindValue imageOffsetKindIndirect() = 0; /// Architecture specific compact unwind type that signals __eh_frame should /// actually be used. virtual uint32_t dwarfCompactUnwindType() = 0; /// Reference from an __eh_frame CIE atom to its personality function it's /// describing. Usually pointer-sized and PC-relative, but differs in whether /// it needs to be in relocatable objects. virtual Reference::KindValue unwindRefToPersonalityFunctionKind() = 0; /// Reference from an __eh_frame FDE to the CIE it's based on. virtual Reference::KindValue unwindRefToCIEKind() = 0; /// Reference from an __eh_frame FDE atom to the function it's /// describing. Usually pointer-sized and PC-relative, but differs in whether /// it needs to be in relocatable objects. virtual Reference::KindValue unwindRefToFunctionKind() = 0; /// Reference from an __unwind_info entry of dwarfCompactUnwindType to the /// required __eh_frame entry. On current architectures, the low 24 bits /// represent the offset of the function's FDE entry from the start of /// __eh_frame. virtual Reference::KindValue unwindRefToEhFrameKind() = 0; /// Returns a pointer sized reference kind. On 64-bit targets this will /// likely be something like pointer64, and pointer32 on 32-bit targets. virtual Reference::KindValue pointerKind() = 0; virtual const Atom *fdeTargetFunction(const DefinedAtom *fde); /// Used by normalizedFromAtoms() to know where to generated rebasing and /// binding info in final executables. virtual bool isPointer(const Reference &) = 0; /// Used by normalizedFromAtoms() to know where to generated lazy binding /// info in final executables. virtual bool isLazyPointer(const Reference &); + /// Reference from an __stub_helper entry to the required offset of the + /// lazy bind commands. + virtual Reference::KindValue lazyImmediateLocationKind() = 0; + /// Returns true if the specified relocation is paired to the next relocation. virtual bool isPairedReloc(const normalized::Relocation &) = 0; /// Prototype for a helper function. Given a sectionIndex and address, /// finds the atom and offset with that atom of that address. typedef std::function FindAtomBySectionAndAddress; /// Prototype for a helper function. Given a symbolIndex, finds the atom /// representing that symbol. typedef std::function FindAtomBySymbolIndex; /// Analyzes a relocation from a .o file and returns the info /// (kind, target, addend) needed to instantiate a Reference. /// Two helper functions are passed as parameters to find the target atom /// given a symbol index or address. virtual llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBigEndian, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) = 0; /// Analyzes a pair of relocations from a .o file and returns the info /// (kind, target, addend) needed to instantiate a Reference. /// Two helper functions are passed as parameters to find the target atom /// given a symbol index or address. virtual llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) = 0; /// Prototype for a helper function. Given an atom, finds the symbol table /// index for it in the output file. typedef std::function FindSymbolIndexForAtom; /// Prototype for a helper function. Given an atom, finds the index /// of the section that will contain the atom. typedef std::function FindSectionIndexForAtom; /// Prototype for a helper function. Given an atom, finds the address /// assigned to it in the output file. typedef std::function FindAddressForAtom; /// Some architectures require local symbols on anonymous atoms. virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) { return false; } /// Copy raw content then apply all fixup References on an Atom. virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) = 0; /// Used in -r mode to convert a Reference to a mach-o relocation. virtual void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom, FindSectionIndexForAtom, FindAddressForAtom, normalized::Relocations&) = 0; /// Add arch-specific References. virtual void addAdditionalReferences(MachODefinedAtom &atom) { } // Add Reference for data-in-code marker. virtual void addDataInCodeReference(MachODefinedAtom &atom, uint32_t atomOff, uint16_t length, uint16_t kind) { } /// Returns true if the specificed Reference value marks the start or end /// of a data-in-code range in an atom. virtual bool isDataInCodeTransition(Reference::KindValue refKind) { return false; } /// Returns the Reference value for a Reference that marks that start of /// a data-in-code range. virtual Reference::KindValue dataInCodeTransitionStart( const MachODefinedAtom &atom) { return 0; } /// Returns the Reference value for a Reference that marks that end of /// a data-in-code range. virtual Reference::KindValue dataInCodeTransitionEnd( const MachODefinedAtom &atom) { return 0; } /// Only relevant for 32-bit arm archs. virtual bool isThumbFunction(const DefinedAtom &atom) { return false; } /// Only relevant for 32-bit arm archs. virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, const DefinedAtom &) { llvm_unreachable("shims only support on arm"); } /// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE). static bool isDwarfCIE(bool isBig, const DefinedAtom *atom); struct ReferenceInfo { Reference::KindArch arch; uint16_t kind; uint32_t offset; int32_t addend; }; struct OptionalRefInfo { bool used; uint16_t kind; uint32_t offset; int32_t addend; }; /// Table of architecture specific information for creating stubs. struct StubInfo { const char* binderSymbolName; ReferenceInfo lazyPointerReferenceToHelper; ReferenceInfo lazyPointerReferenceToFinal; ReferenceInfo nonLazyPointerReferenceToBinder; uint8_t codeAlignment; uint32_t stubSize; uint8_t stubBytes[16]; ReferenceInfo stubReferenceToLP; OptionalRefInfo optStubReferenceToLP; uint32_t stubHelperSize; uint8_t stubHelperBytes[16]; ReferenceInfo stubHelperReferenceToImm; ReferenceInfo stubHelperReferenceToHelperCommon; DefinedAtom::ContentType stubHelperImageCacheContentType; uint32_t stubHelperCommonSize; uint8_t stubHelperCommonAlignment; uint8_t stubHelperCommonBytes[36]; ReferenceInfo stubHelperCommonReferenceToCache; OptionalRefInfo optStubHelperCommonReferenceToCache; ReferenceInfo stubHelperCommonReferenceToBinder; OptionalRefInfo optStubHelperCommonReferenceToBinder; }; virtual const StubInfo &stubInfo() = 0; protected: ArchHandler(); static std::unique_ptr create_x86_64(); static std::unique_ptr create_x86(); static std::unique_ptr create_arm(); static std::unique_ptr create_arm64(); // Handy way to pack mach-o r_type and other bit fields into one 16-bit value. typedef uint16_t RelocPattern; enum { rScattered = 0x8000, rPcRel = 0x4000, rExtern = 0x2000, rLength1 = 0x0000, rLength2 = 0x0100, rLength4 = 0x0200, rLength8 = 0x0300, rLenArmLo = rLength1, rLenArmHi = rLength2, rLenThmbLo = rLength4, rLenThmbHi = rLength8 }; /// Extract RelocPattern from normalized mach-o relocation. static RelocPattern relocPattern(const normalized::Relocation &reloc); /// Create normalized Relocation initialized from pattern. static normalized::Relocation relocFromPattern(RelocPattern pattern); /// One liner to add a relocation. static void appendReloc(normalized::Relocations &relocs, uint32_t offset, uint32_t symbol, uint32_t value, RelocPattern pattern); static int16_t readS16(const uint8_t *addr, bool isBig); static int32_t readS32(const uint8_t *addr, bool isBig); static uint32_t readU32(const uint8_t *addr, bool isBig); static int64_t readS64(const uint8_t *addr, bool isBig); }; } // namespace mach_o } // namespace lld #endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H Index: vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm.cpp =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm.cpp (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm.cpp (revision 326905) @@ -1,1519 +1,1523 @@ //===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ArchHandler.h" #include "Atoms.h" #include "MachONormalizedFileBinaryUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; namespace lld { namespace mach_o { using llvm::support::ulittle32_t; using llvm::support::little32_t; class ArchHandler_arm : public ArchHandler { public: ArchHandler_arm() = default; ~ArchHandler_arm() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } const ArchHandler::StubInfo &stubInfo() override; bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; bool isNonCallBranch(const Reference &) override; bool needsCompactUnwind() override { return false; } Reference::KindValue imageOffsetKind() override { return invalid; } Reference::KindValue imageOffsetKindIndirect() override { return invalid; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return invalid; } Reference::KindValue unwindRefToCIEKind() override { return invalid; } Reference::KindValue unwindRefToFunctionKind() override { return invalid; } Reference::KindValue unwindRefToEhFrameKind() override { return invalid; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue pointerKind() override { return invalid; } uint32_t dwarfCompactUnwindType() override { // FIXME return -1; } llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom, FindSectionIndexForAtom, FindAddressForAtom, normalized::Relocations &) override; void addAdditionalReferences(MachODefinedAtom &atom) override; bool isDataInCodeTransition(Reference::KindValue refKind) override { switch (refKind) { case modeThumbCode: case modeArmCode: case modeData: return true; default: return false; break; } } Reference::KindValue dataInCodeTransitionStart( const MachODefinedAtom &atom) override { return modeData; } Reference::KindValue dataInCodeTransitionEnd( const MachODefinedAtom &atom) override { return atom.isThumb() ? modeThumbCode : modeArmCode; } bool isThumbFunction(const DefinedAtom &atom) override; const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, const DefinedAtom &) override; private: friend class Thumb2ToArmShimAtom; friend class ArmToThumbShimAtom; static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfoArmPIC; enum ArmKind : Reference::KindValue { invalid, /// for error condition modeThumbCode, /// Content starting at this offset is thumb. modeArmCode, /// Content starting at this offset is arm. modeData, /// Content starting at this offset is data. // Kinds found in mach-o .o files: thumb_bl22, /// ex: bl _foo thumb_b22, /// ex: b _foo thumb_movw, /// ex: movw r1, :lower16:_foo thumb_movt, /// ex: movt r1, :lower16:_foo thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) arm_bl24, /// ex: bl _foo arm_b24, /// ex: b _foo arm_movw, /// ex: movw r1, :lower16:_foo arm_movt, /// ex: movt r1, :lower16:_foo arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) pointer32, /// ex: .long _foo delta32, /// ex: .long _foo - . // Kinds introduced by Passes: lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. }; // Utility functions for inspecting/updating instructions. static bool isThumbMovw(uint32_t instruction); static bool isThumbMovt(uint32_t instruction); static bool isArmMovw(uint32_t instruction); static bool isArmMovt(uint32_t instruction); static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); static int32_t getDisplacementFromArmBranch(uint32_t instruction); static uint16_t getWordFromThumbMov(uint32_t instruction); static uint16_t getWordFromArmMov(uint32_t instruction); static uint32_t clearThumbBit(uint32_t value, const Atom *target); static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, bool targetIsThumb); static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, int32_t disp, bool targetThumb); static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); StringRef stubName(const DefinedAtom &); bool useExternalRelocationTo(const Atom &target); void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb); }; //===----------------------------------------------------------------------===// // ArchHandler_arm //===----------------------------------------------------------------------===// const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(modeThumbCode), LLD_KIND_STRING_ENTRY(modeArmCode), LLD_KIND_STRING_ENTRY(modeData), LLD_KIND_STRING_ENTRY(thumb_bl22), LLD_KIND_STRING_ENTRY(thumb_b22), LLD_KIND_STRING_ENTRY(thumb_movw), LLD_KIND_STRING_ENTRY(thumb_movt), LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), LLD_KIND_STRING_ENTRY(arm_bl24), LLD_KIND_STRING_ENTRY(arm_b24), LLD_KIND_STRING_ENTRY(arm_movw), LLD_KIND_STRING_ENTRY(arm_movt), LLD_KIND_STRING_ENTRY(arm_movw_funcRel), LLD_KIND_STRING_ENTRY(arm_movt_funcRel), LLD_KIND_STRING_ENTRY(pointer32), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { "dyld_stub_binder", // References in lazy pointer { Reference::KindArch::ARM, pointer32, 0, 0 }, { Reference::KindArch::ARM, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::ARM, pointer32, 0, 0 }, // arm code alignment 2^2 2, // Stub size and code 16, { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) { Reference::KindArch::ARM, delta32, 12, 0 }, { false, 0, 0, 0 }, // Stub Helper size and code 12, { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] 0x00, 0x00, 0x00, 0xEA, // b _helperhelper 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, { Reference::KindArch::ARM, arm_b24, 4, 0 }, // Stub helper image cache content type DefinedAtom::typeGOT, // Stub Helper-Common size and code 36, // Stub helper alignment 2, { // push lazy-info-offset 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! // push address of dyld_mageLoaderCache 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! // jump through dyld_stub_binder 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) { Reference::KindArch::ARM, delta32, 28, 0xC }, { false, 0, 0, 0 }, { Reference::KindArch::ARM, delta32, 32, 0x04 }, { false, 0, 0, 0 } }; const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { // If multiple kinds of stubs are supported, select which StubInfo here. return _sStubInfoArmPIC; } bool ArchHandler_arm::isCallSite(const Reference &ref) { switch (ref.kindValue()) { case thumb_b22: case thumb_bl22: case arm_b24: case arm_bl24: return true; default: return false; } } bool ArchHandler_arm::isPointer(const Reference &ref) { return (ref.kindValue() == pointer32); } bool ArchHandler_arm::isNonCallBranch(const Reference &ref) { switch (ref.kindValue()) { case thumb_b22: case arm_b24: return true; default: return false; } } bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { switch (reloc.type) { case ARM_RELOC_SECTDIFF: case ARM_RELOC_LOCAL_SECTDIFF: case ARM_RELOC_HALF_SECTDIFF: case ARM_RELOC_HALF: return true; default: return false; } } /// Trace references from stub atom to lazy pointer to target and get its name. StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { assert(stubAtom.contentType() == DefinedAtom::typeStub); for (const Reference *ref : stubAtom) { if (const DefinedAtom* lp = dyn_cast(ref->target())) { if (lp->contentType() != DefinedAtom::typeLazyPointer) continue; for (const Reference *ref2 : *lp) { if (ref2->kindValue() != lazyPointer) continue; return ref2->target()->name(); } } } return "stub"; } /// Extract displacement from an ARM b/bl/blx instruction. int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { // Sign-extend imm24 int32_t displacement = (instruction & 0x00FFFFFF) << 2; if ((displacement & 0x02000000) != 0) displacement |= 0xFC000000; // If this is BLX and H bit set, add 2. if ((instruction & 0xFF000000) == 0xFB000000) displacement += 2; return displacement; } /// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, int32_t displacement, bool targetIsThumb) { assert((displacement <= 33554428) && (displacement > (-33554432)) && "arm branch out of range"); bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); uint32_t newInstruction = (instruction & 0xFF000000); uint32_t h = 0; if (targetIsThumb) { // Force use of BLX. newInstruction = 0xFA000000; if (!is_blx) { assert(((instruction & 0xF0000000) == 0xE0000000) && "no conditional arm blx"); assert(((instruction & 0xFF000000) == 0xEB000000) && "no arm pc-rel BX instruction"); } if (displacement & 2) h = 1; } else { // Force use of B/BL. if (is_blx) newInstruction = 0xEB000000; } newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); return newInstruction; } /// Extract displacement from a thumb b/bl/blx instruction. int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr) { bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); uint32_t s = (instruction >> 10) & 0x1; uint32_t j1 = (instruction >> 29) & 0x1; uint32_t j2 = (instruction >> 27) & 0x1; uint32_t imm10 = instruction & 0x3FF; uint32_t imm11 = (instruction >> 16) & 0x7FF; uint32_t i1 = (j1 == s); uint32_t i2 = (j2 == s); uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); int32_t sdis = dis; int32_t result = s ? (sdis | 0xFE000000) : sdis; if (is_blx && (instrAddr & 0x2)) { // The thumb blx instruction always has low bit of imm11 as zero. The way // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that // the blx instruction always 4-byte aligns the pc before adding the // displacement from the blx. We must emulate that when decoding this. result -= 2; } return result; } /// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, int32_t displacement, bool targetIsThumb) { assert((displacement <= 16777214) && (displacement > (-16777216)) && "thumb branch out of range"); bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); bool is_b = ((instruction & 0xD000F800) == 0x9000F000); uint32_t newInstruction = (instruction & 0xD000F800); if (is_bl || is_blx) { if (targetIsThumb) { newInstruction = 0xD000F000; // Use bl } else { newInstruction = 0xC000F000; // Use blx // See note in getDisplacementFromThumbBranch() about blx. if (instrAddr & 0x2) displacement += 2; } } else if (is_b) { assert(targetIsThumb && "no pc-rel thumb branch instruction that " "switches to arm mode"); } else { llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); } uint32_t s = (uint32_t)(displacement >> 24) & 0x1; uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; uint32_t j1 = (i1 == s); uint32_t j2 = (i2 == s); uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; uint32_t firstDisp = (s << 10) | imm10; newInstruction |= (nextDisp << 16) | firstDisp; return newInstruction; } bool ArchHandler_arm::isThumbMovw(uint32_t instruction) { return (instruction & 0x8000FBF0) == 0x0000F240; } bool ArchHandler_arm::isThumbMovt(uint32_t instruction) { return (instruction & 0x8000FBF0) == 0x0000F2C0; } bool ArchHandler_arm::isArmMovw(uint32_t instruction) { return (instruction & 0x0FF00000) == 0x03000000; } bool ArchHandler_arm::isArmMovt(uint32_t instruction) { return (instruction & 0x0FF00000) == 0x03400000; } uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { assert(isThumbMovw(instruction) || isThumbMovt(instruction)); uint32_t i = ((instruction & 0x00000400) >> 10); uint32_t imm4 = (instruction & 0x0000000F); uint32_t imm3 = ((instruction & 0x70000000) >> 28); uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; } uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { assert(isArmMovw(instruction) || isArmMovt(instruction)); uint32_t imm4 = ((instruction & 0x000F0000) >> 16); uint32_t imm12 = (instruction & 0x00000FFF); return (imm4 << 12) | imm12; } uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { assert(isThumbMovw(instr) || isThumbMovt(instr)); uint32_t imm4 = (word & 0xF000) >> 12; uint32_t i = (word & 0x0800) >> 11; uint32_t imm3 = (word & 0x0700) >> 8; uint32_t imm8 = word & 0x00FF; return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); } uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { assert(isArmMovw(instr) || isArmMovt(instr)); uint32_t imm4 = (word & 0xF000) >> 12; uint32_t imm12 = word & 0x0FFF; return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; } uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { // The assembler often adds one to the address of a thumb function. // We need to undo that so it does not look like an addend. if (value & 1) { if (isa(target)) { const MachODefinedAtom *machoTarget = reinterpret_cast(target); if (machoTarget->isThumb()) value &= -2; // mask off thumb-bit } } return value; } llvm::Error ArchHandler_arm::getReferenceInfo( const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; uint32_t instruction = *(const ulittle32_t *)fixupContent; int32_t displacement; switch (relocPattern(reloc)) { case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: // ex: bl _foo (and _foo is undefined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); *addend = fixupAddress + 4 + displacement; return llvm::Error::success(); case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); targetAddress = fixupAddress + 4 + displacement; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: // ex: bl _foo+4 (and _foo is defined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); targetAddress = fixupAddress + 4 + displacement; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; // reloc.value is target atom's address. Instruction contains branch // to atom+addend. *addend += (targetAddress - reloc.value); return llvm::Error::success(); case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: // ex: bl _foo (and _foo is undefined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromArmBranch(instruction); *addend = fixupAddress + 8 + displacement; return llvm::Error::success(); case ARM_RELOC_BR24 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; displacement = getDisplacementFromArmBranch(instruction); targetAddress = fixupAddress + 8 + displacement; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: // ex: bl _foo+4 (and _foo is defined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; displacement = getDisplacementFromArmBranch(instruction); targetAddress = fixupAddress + 8 + displacement; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; // reloc.value is target atom's address. Instruction contains branch // to atom+addend. *addend += (targetAddress - reloc.value); return llvm::Error::success(); case ARM_RELOC_VANILLA | rExtern | rLength4: // ex: .long _foo (and _foo is undefined) *kind = pointer32; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = instruction; return llvm::Error::success(); case ARM_RELOC_VANILLA | rLength4: // ex: .long _foo (and _foo is defined) *kind = pointer32; if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend)) return ec; *addend = clearThumbBit((uint32_t) * addend, *target); return llvm::Error::success(); case ARM_RELOC_VANILLA | rScattered | rLength4: // ex: .long _foo+a (and _foo is defined) *kind = pointer32; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend += (clearThumbBit(instruction, *target) - reloc.value); return llvm::Error::success(); default: return llvm::make_error("unsupported arm relocation type"); } return llvm::Error::success(); } llvm::Error ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, bool scatterable, FindAtomBySectionAndAddress atomFromAddr, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { bool pointerDiff = false; bool funcRel; bool top; bool thumbReloc; switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rScattered | rLenThmbLo): // ex: movw r1, :lower16:(_x-L1) [thumb mode] *kind = thumb_movw_funcRel; funcRel = true; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rScattered | rLenThmbHi): // ex: movt r1, :upper16:(_x-L1) [thumb mode] *kind = thumb_movt_funcRel; funcRel = true; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | ARM_RELOC_PAIR | rScattered | rLenArmLo): // ex: movw r1, :lower16:(_x-L1) [arm mode] *kind = arm_movw_funcRel; funcRel = true; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | ARM_RELOC_PAIR | rScattered | rLenArmHi): // ex: movt r1, :upper16:(_x-L1) [arm mode] *kind = arm_movt_funcRel; funcRel = true; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_x [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_x [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_x [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_x [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_x+a [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_x+a [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_x+a [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_x+a [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_undef [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_undef [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_undef [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_undef [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | ARM_RELOC_PAIR | rScattered | rLength4): case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | ARM_RELOC_PAIR | rScattered | rLength4): // ex: .long _foo - . pointerDiff = true; break; default: return llvm::make_error("unsupported arm relocation pair"); } const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint32_t instruction = *(const ulittle32_t *)fixupContent; uint32_t value; uint32_t fromAddress; uint32_t toAddress; uint16_t instruction16; uint16_t other16; const lld::Atom *fromTarget; Reference::Addend offsetInTo; Reference::Addend offsetInFrom; if (pointerDiff) { toAddress = reloc1.value; fromAddress = reloc2.value; if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (scatterable && (fromTarget != inAtom)) return llvm::make_error( "SECTDIFF relocation where subtrahend label is not in atom"); *kind = delta32; value = clearThumbBit(instruction, *target); *addend = (int32_t)(value - (toAddress - fixupAddress)); } else if (funcRel) { toAddress = reloc1.value; fromAddress = reloc2.value; if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (fromTarget != inAtom) return llvm::make_error("ARM_RELOC_HALF_SECTDIFF relocation" " where subtrahend label is not in atom"); other16 = (reloc2.offset & 0xFFFF); if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isThumbMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isArmMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } if (top) value = (instruction16 << 16) | other16; else value = (other16 << 16) | instruction16; value = clearThumbBit(value, *target); int64_t ta = (int64_t) value - (toAddress - fromAddress); *addend = ta - offsetInFrom; return llvm::Error::success(); } else { uint32_t sectIndex; if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isThumbMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isArmMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } other16 = (reloc2.offset & 0xFFFF); if (top) value = (instruction16 << 16) | other16; else value = (other16 << 16) | instruction16; if (reloc1.isExtern) { if (auto ec = atomFromSymbolIndex(reloc1.symbol, target)) return ec; *addend = value; } else { if (reloc1.scattered) { toAddress = reloc1.value; sectIndex = 0; } else { toAddress = value; sectIndex = reloc1.symbol; } if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo)) return ec; *addend = value - toAddress; } } return llvm::Error::success(); } void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::ARM); ulittle32_t *loc32 = reinterpret_cast(loc); int32_t displacement; uint16_t value16; uint32_t value32; switch (static_cast(ref.kindValue())) { case modeThumbCode: thumbMode = true; break; case modeArmCode: thumbMode = false; break; case modeData: break; case thumb_b22: case thumb_bl22: assert(thumbMode); displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement, targetIsThumb); *loc32 = value32; break; case thumb_movw: assert(thumbMode); value16 = (targetAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt: assert(thumbMode); value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movw_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case arm_b24: case arm_bl24: assert(!thumbMode); displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); *loc32 = value32; break; case arm_movw: assert(!thumbMode); value16 = (targetAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt: assert(!thumbMode); value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movw_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case pointer32: if (targetIsThumb) *loc32 = targetAddress + ref.addend() + 1; else *loc32 = targetAddress + ref.addend(); break; case delta32: if (targetIsThumb) *loc32 = targetAddress - fixupAddress + ref.addend() + 1; else *loc32 = targetAddress - fixupAddress + ref.addend(); break; case lazyPointer: // do nothing break; case lazyImmediateLocation: *loc32 = ref.addend(); break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) { // Copy raw bytes. std::copy(atom.rawContent().begin(), atom.rawContent().end(), atomContentBuffer.begin()); // Apply fix-ups. bool thumbMode = false; for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); uint64_t targetAddress = 0; bool targetIsThumb = false; if (const DefinedAtom *defTarg = dyn_cast(target)) { targetAddress = findAddress(*target); targetIsThumb = isThumbFunction(*defTarg); } uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, thumbMode, targetIsThumb); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, thumbMode, targetIsThumb); } } } bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) { // Undefined symbols are referenced via external relocations. if (isa(&target)) return true; if (const DefinedAtom *defAtom = dyn_cast(&target)) { switch (defAtom->merge()) { case DefinedAtom::mergeAsTentative: // Tentative definitions are referenced via external relocations. return true; case DefinedAtom::mergeAsWeak: case DefinedAtom::mergeAsWeakAndAddressUsed: // Global weak-defs are referenced via external relocations. return (defAtom->scope() == DefinedAtom::scopeGlobal); default: break; } } // Everything else is reference via an internal relocation. return false; } void ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::ARM); bool useExternalReloc = useExternalRelocationTo(*ref.target()); ulittle32_t *loc32 = reinterpret_cast(loc); int32_t displacement; uint16_t value16; uint32_t value32; bool targetIsUndef = isa(ref.target()); switch (static_cast(ref.kindValue())) { case modeThumbCode: thumbMode = true; break; case modeArmCode: thumbMode = false; break; case modeData: break; case thumb_b22: case thumb_bl22: assert(thumbMode); if (useExternalReloc) displacement = (ref.addend() - (fixupAddress + 4)); else displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement, targetIsUndef || targetIsThumb); *loc32 = value32; break; case thumb_movw: assert(thumbMode); if (useExternalReloc) value16 = ref.addend() & 0xFFFF; else value16 = (targetAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt: assert(thumbMode); if (useExternalReloc) value16 = ref.addend() >> 16; else value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movw_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case arm_b24: case arm_bl24: assert(!thumbMode); if (useExternalReloc) displacement = (ref.addend() - (fixupAddress + 8)); else displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); *loc32 = value32; break; case arm_movw: assert(!thumbMode); if (useExternalReloc) value16 = ref.addend() & 0xFFFF; else value16 = (targetAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt: assert(!thumbMode); if (useExternalReloc) value16 = ref.addend() >> 16; else value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movw_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case pointer32: *loc32 = targetAddress + ref.addend(); break; case delta32: *loc32 = targetAddress - fixupAddress + ref.addend(); break; case lazyPointer: case lazyImmediateLocation: // do nothing break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::ARM); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); bool useExternalReloc = useExternalRelocationTo(*ref.target()); uint32_t targetAtomAddress; uint32_t fromAtomAddress; uint16_t other16; switch (static_cast(ref.kindValue())) { case modeThumbCode: case modeArmCode: case modeData: // Do nothing. break; case thumb_b22: case thumb_bl22: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_THUMB_RELOC_BR22 | rPcRel | rLength4); } break; case thumb_movw: if (useExternalReloc) { other16 = ref.addend() >> 16; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } else { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } } break; case thumb_movt: if (useExternalReloc) { other16 = ref.addend() & 0xFFFF; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } else { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } } break; case thumb_movw_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenThmbLo); break; case thumb_movt_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenThmbHi); break; case arm_b24: case arm_bl24: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_BR24 | rExtern | rPcRel | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_BR24 | rScattered | rPcRel | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_BR24 | rPcRel | rLength4); } break; case arm_movw: if (useExternalReloc) { other16 = ref.addend() >> 16; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } else { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } } break; case arm_movt: if (useExternalReloc) { other16 = ref.addend() & 0xFFFF; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } else { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } } break; case arm_movw_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenArmLo); break; case arm_movt_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenArmHi); break; case pointer32: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_VANILLA | rExtern | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_VANILLA | rScattered | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_VANILLA | rLength4); } break; case delta32: appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_SECTDIFF | rScattered | rLength4); appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + ref.offsetInAtom(), ARM_RELOC_PAIR | rScattered | rLength4); break; case lazyPointer: case lazyImmediateLocation: // do nothing break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { if (atom.isThumb()) { atom.addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0); } } bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { for (const Reference *ref : atom) { if (ref->offsetInAtom() != 0) return false; if (ref->kindNamespace() != Reference::KindNamespace::mach_o) continue; assert(ref->kindArch() == Reference::KindArch::ARM); if (ref->kindValue() == modeThumbCode) return true; } return false; } class Thumb2ToArmShimAtom : public SimpleDefinedAtom { public: Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, const DefinedAtom &target) : SimpleDefinedAtom(file) { addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::modeThumbCode, 0, this, 0); addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::delta32, 8, &target, 0); std::string name = std::string(targetName) + "$shim"; StringRef tmp(name); _name = tmp.copy(file.allocator()); } ~Thumb2ToArmShimAtom() override = default; StringRef name() const override { return _name; } ContentType contentType() const override { return DefinedAtom::typeCode; } Alignment alignment() const override { return 4; } uint64_t size() const override { return 12; } ContentPermissions permissions() const override { return DefinedAtom::permR_X; } ArrayRef rawContent() const override { static const uint8_t bytes[] = { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4 0xFF, 0x44, // add ip, pc, ip 0x60, 0x47, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }; // .long target - this assert(sizeof(bytes) == size()); return llvm::makeArrayRef(bytes, sizeof(bytes)); } private: StringRef _name; }; class ArmToThumbShimAtom : public SimpleDefinedAtom { public: ArmToThumbShimAtom(MachOFile &file, StringRef targetName, const DefinedAtom &target) : SimpleDefinedAtom(file) { addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::delta32, 12, &target, 0); std::string name = std::string(targetName) + "$shim"; StringRef tmp(name); _name = tmp.copy(file.allocator()); } ~ArmToThumbShimAtom() override = default; StringRef name() const override { return _name; } ContentType contentType() const override { return DefinedAtom::typeCode; } Alignment alignment() const override { return 4; } uint64_t size() const override { return 16; } ContentPermissions permissions() const override { return DefinedAtom::permR_X; } ArrayRef rawContent() const override { static const uint8_t bytes[] = { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }; // .long target - this assert(sizeof(bytes) == size()); return llvm::makeArrayRef(bytes, sizeof(bytes)); } private: StringRef _name; }; const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, bool thumbToArm, const DefinedAtom &target) { bool isStub = (target.contentType() == DefinedAtom::typeStub); StringRef targetName = isStub ? stubName(target) : target.name(); if (thumbToArm) return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); else return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); } std::unique_ptr ArchHandler::create_arm() { return std::unique_ptr(new ArchHandler_arm()); } } // namespace mach_o } // namespace lld Index: vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp (revision 326905) @@ -1,894 +1,898 @@ //===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ArchHandler.h" #include "Atoms.h" #include "MachONormalizedFileBinaryUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; namespace lld { namespace mach_o { using llvm::support::ulittle32_t; using llvm::support::ulittle64_t; using llvm::support::little32_t; using llvm::support::little64_t; class ArchHandler_arm64 : public ArchHandler { public: ArchHandler_arm64() = default; ~ArchHandler_arm64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::AArch64; } /// Used by GOTPass to locate GOT References bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); switch (ref.kindValue()) { case gotPage21: case gotOffset12: canBypassGOT = true; return true; case delta32ToGOT: case unwindCIEToPersonalityFunction: case imageOffsetGot: canBypassGOT = false; return true; default: return false; } } /// Used by GOTPass to update GOT References. void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { // If GOT slot was instanciated, transform: // gotPage21/gotOffset12 -> page21/offset12scale8 // If GOT slot optimized away, transform: // gotPage21/gotOffset12 -> page21/addOffset12 assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::AArch64); switch (ref->kindValue()) { case gotPage21: const_cast(ref)->setKindValue(page21); break; case gotOffset12: const_cast(ref)->setKindValue(targetNowGOT ? offset12scale8 : addOffset12); break; case delta32ToGOT: const_cast(ref)->setKindValue(delta32); break; case imageOffsetGot: const_cast(ref)->setKindValue(imageOffset); break; default: llvm_unreachable("Not a GOT reference"); } } const StubInfo &stubInfo() override { return _sStubInfo; } bool isCallSite(const Reference &) override; bool isNonCallBranch(const Reference &) override { return false; } bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; bool needsCompactUnwind() override { return true; } Reference::KindValue imageOffsetKind() override { return imageOffset; } Reference::KindValue imageOffsetKindIndirect() override { return imageOffsetGot; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return unwindCIEToPersonalityFunction; } Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } Reference::KindValue unwindRefToFunctionKind() override { return unwindFDEToFunction; } Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } Reference::KindValue pointerKind() override { return pointer64; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + uint32_t dwarfCompactUnwindType() override { return 0x03000000; } llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override { return (atom->contentType() == DefinedAtom::typeCString); } void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) override; private: static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfo; enum Arm64Kind : Reference::KindValue { invalid, /// for error condition // Kinds found in mach-o .o files: branch26, /// ex: bl _foo page21, /// ex: adrp x1, _foo@PAGE offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF] offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF] offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF] offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF] offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF] gotPage21, /// ex: adrp x1, _foo@GOTPAGE gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF] tlvPage21, /// ex: adrp x1, _foo@TLVPAGE tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF] pointer64, /// ex: .quad _foo delta64, /// ex: .quad _foo - . delta32, /// ex: .long _foo - . negDelta32, /// ex: .long . - _foo pointer64ToGOT, /// ex: .quad _foo@GOT delta32ToGOT, /// ex: .long _foo@GOT - . // Kinds introduced by Passes: addOffset12, /// Location contains LDR to change into ADD. lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. imageOffset, /// Location contains offset of atom in final image imageOffsetGot, /// Location contains offset of GOT entry for atom in /// final image (typically personality function). unwindCIEToPersonalityFunction, /// Nearly delta32ToGOT, but cannot be /// rematerialized in relocatable object /// (yay for implicit contracts!). unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in /// relocatable object (yay for implicit contracts!). unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to /// refer to __eh_frame entry. }; void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool targetUnnamed); // Utility functions for inspecting/updating instructions. static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp); static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp); static Arm64Kind offset12KindFromInstruction(uint32_t instr); static uint32_t setImm12(uint32_t instr, uint32_t offset); }; const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch26), LLD_KIND_STRING_ENTRY(page21), LLD_KIND_STRING_ENTRY(offset12), LLD_KIND_STRING_ENTRY(offset12scale2), LLD_KIND_STRING_ENTRY(offset12scale4), LLD_KIND_STRING_ENTRY(offset12scale8), LLD_KIND_STRING_ENTRY(offset12scale16), LLD_KIND_STRING_ENTRY(gotPage21), LLD_KIND_STRING_ENTRY(gotOffset12), LLD_KIND_STRING_ENTRY(tlvPage21), LLD_KIND_STRING_ENTRY(tlvOffset12), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(pointer64ToGOT), LLD_KIND_STRING_ENTRY(delta32ToGOT), LLD_KIND_STRING_ENTRY(addOffset12), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindCIEToPersonalityFunction), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = { "dyld_stub_binder", // Lazy pointer references { Reference::KindArch::AArch64, pointer64, 0, 0 }, { Reference::KindArch::AArch64, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::AArch64, pointer64, 0, 0 }, // arm64 code alignment 2^1 1, // Stub size and code 12, { 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff] 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 { Reference::KindArch::AArch64, page21, 0, 0 }, { true, offset12scale8, 4, 0 }, // Stub Helper size and code 12, { 0x50, 0x00, 0x00, 0x18, // LDR W16, L0 0x00, 0x00, 0x00, 0x14, // LDR B helperhelper 0x00, 0x00, 0x00, 0x00 }, // L0: .long 0 { Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 }, { Reference::KindArch::AArch64, branch26, 4, 0 }, // Stub helper image cache content type DefinedAtom::typeGOT, // Stub Helper-Common size and code 24, // Stub helper alignment 2, { 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page 0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff 0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]! 0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff] 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 { Reference::KindArch::AArch64, page21, 0, 0 }, { true, offset12, 4, 0 }, { Reference::KindArch::AArch64, page21, 12, 0 }, { true, offset12scale8, 16, 0 } }; bool ArchHandler_arm64::isCallSite(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); return (ref.kindValue() == branch26); } bool ArchHandler_arm64::isPointer(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); Reference::KindValue kind = ref.kindValue(); return (kind == pointer64); } bool ArchHandler_arm64::isPairedReloc(const Relocation &r) { return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR)); } uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr, int32_t displacement) { assert((displacement <= 134217727) && (displacement > (-134217728)) && "arm64 branch out of range"); return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF); } uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction, int64_t displacement) { assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) && "arm64 ADRP out of range"); assert(((instruction & 0x9F000000) == 0x90000000) && "reloc not on ADRP instruction"); uint32_t immhi = (displacement >> 9) & (0x00FFFFE0); uint32_t immlo = (displacement << 17) & (0x60000000); return (instruction & 0x9F00001F) | immlo | immhi; } ArchHandler_arm64::Arm64Kind ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) { if (instruction & 0x08000000) { switch ((instruction >> 30) & 0x3) { case 0: if ((instruction & 0x04800000) == 0x04800000) return offset12scale16; return offset12; case 1: return offset12scale2; case 2: return offset12scale4; case 3: return offset12scale8; } } return offset12; } uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) { assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range"); uint32_t imm12 = offset << 10; return (instruction & 0xFFC003FF) | imm12; } llvm::Error ArchHandler_arm64::getReferenceInfo( const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; switch (relocPattern(reloc)) { case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4: // ex: bl _foo *kind = branch26; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@PAGE *kind = page21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@PAGEOFF] *kind = offset12KindFromInstruction(*(const little32_t *)fixupContent); if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@GOTPAGE *kind = gotPage21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@GOTPAGEOFF] *kind = gotOffset12; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@TLVPAGE *kind = tlvPage21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@TLVPAGEOFF] *kind = tlvOffset12; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_UNSIGNED | rExtern | rLength8: // ex: .quad _foo + N *kind = pointer64; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little64_t *)fixupContent; return llvm::Error::success(); case ARM64_RELOC_UNSIGNED | rLength8: // ex: .quad Lfoo + N *kind = pointer64; return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent, target, addend); case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8: // ex: .quad _foo@GOT *kind = pointer64ToGOT; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4: // ex: .long _foo@GOT - . // If we are in an .eh_frame section, then the kind of the relocation should // not be delta32ToGOT. It may instead be unwindCIEToPersonalityFunction. if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindCIEToPersonalityFunction; else *kind = delta32ToGOT; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); default: return llvm::make_error("unsupported arm64 relocation type"); } } llvm::Error ArchHandler_arm64::getPairReferenceInfo( const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4): // ex: bl _foo+8 *kind = branch26; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4): // ex: adrp x1, _foo@PAGE *kind = page21; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): { // ex: ldr w0, [x1, _foo@PAGEOFF] uint32_t cont32 = (int32_t)*(const little32_t *)fixupContent; *kind = offset12KindFromInstruction(cont32); if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); } case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength8): // ex: .quad _foo - . if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; // If we are in an .eh_frame section, then the kind of the relocation should // not be delta64. It may instead be unwindFDEToFunction. if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindFDEToFunction; else *kind = delta64; // The offsets of the 2 relocations must match if (reloc1.offset != reloc2.offset) return llvm::make_error( "paired relocs must have the same offset"); *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; return llvm::Error::success(); case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength4): // ex: .quad _foo - . *kind = delta32; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; return llvm::Error::success(); default: return llvm::make_error("unsupported arm64 relocation pair"); } } void ArchHandler_arm64::generateAtomContent( const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) { // Copy raw bytes. std::copy(atom.rawContent().begin(), atom.rawContent().end(), atomContentBuffer.begin()); // Apply fix-ups. #ifndef NDEBUG if (atom.begin() != atom.end()) { DEBUG_WITH_TYPE("atom-content", llvm::dbgs() << "Applying fixups to atom:\n" << " address=" << llvm::format(" 0x%09lX", &atom) << ", file=#" << atom.file().ordinal() << ", atom=#" << atom.ordinal() << ", name=" << atom.name() << ", type=" << atom.contentType() << "\n"); } #endif for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); bool targetUnnamed = target->name().empty(); uint64_t targetAddress = 0; if (isa(target)) targetAddress = findAddress(*target); uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, targetUnnamed); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, imageBaseAddress, findSectionAddress); } } } void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); int32_t displacement; uint32_t instruction; uint32_t value32; uint32_t value64; switch (static_cast(ref.kindValue())) { case branch26: displacement = (targetAddress - fixupAddress) + ref.addend(); *loc32 = setDisplacementInBranch26(*loc32, displacement); return; case page21: case gotPage21: case tlvPage21: displacement = ((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096)); *loc32 = setDisplacementInADRP(*loc32, displacement); return; case offset12: case gotOffset12: case tlvOffset12: displacement = (targetAddress + ref.addend()) & 0x00000FFF; *loc32 = setImm12(*loc32, displacement); return; case offset12scale2: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x1) == 0) && "scaled imm12 not accessing 2-byte aligneds"); *loc32 = setImm12(*loc32, displacement >> 1); return; case offset12scale4: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x3) == 0) && "scaled imm12 not accessing 4-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 2); return; case offset12scale8: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x7) == 0) && "scaled imm12 not accessing 8-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 3); return; case offset12scale16: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0xF) == 0) && "scaled imm12 not accessing 16-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 4); return; case addOffset12: instruction = *loc32; assert(((instruction & 0xFFC00000) == 0xF9400000) && "GOT reloc is not an LDR instruction"); displacement = (targetAddress + ref.addend()) & 0x00000FFF; value32 = 0x91000000 | (instruction & 0x000003FF); instruction = setImm12(value32, displacement); *loc32 = instruction; return; case pointer64: case pointer64ToGOT: *loc64 = targetAddress + ref.addend(); return; case delta64: case unwindFDEToFunction: *loc64 = (targetAddress - fixupAddress) + ref.addend(); return; case delta32: case delta32ToGOT: case unwindCIEToPersonalityFunction: *loc32 = (targetAddress - fixupAddress) + ref.addend(); return; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); return; case lazyPointer: // Do nothing return; case lazyImmediateLocation: *loc32 = ref.addend(); return; case imageOffset: *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); return; case imageOffsetGot: llvm_unreachable("imageOffsetGot should have been changed to imageOffset"); break; case unwindInfoToEhFrame: value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); assert(value64 < 0xffffffU && "offset in __eh_frame too large"); *loc32 = (*loc32 & 0xff000000U) | value64; return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("invalid arm64 Reference Kind"); } void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool targetUnnamed) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch26: *loc32 = setDisplacementInBranch26(*loc32, 0); return; case page21: case gotPage21: case tlvPage21: *loc32 = setDisplacementInADRP(*loc32, 0); return; case offset12: case offset12scale2: case offset12scale4: case offset12scale8: case offset12scale16: case gotOffset12: case tlvOffset12: *loc32 = setImm12(*loc32, 0); return; case pointer64: if (targetUnnamed) *loc64 = targetAddress + ref.addend(); else *loc64 = ref.addend(); return; case delta64: *loc64 = ref.addend() + inAtomAddress - fixupAddress; return; case unwindFDEToFunction: // We don't emit unwindFDEToFunction in -r mode as they are implicitly // generated from the data in the __eh_frame section. So here we need // to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc64 = targetAddress - fixupAddress; return; case delta32: *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case negDelta32: // We don't emit negDelta32 in -r mode as they are implicitly // generated from the data in the __eh_frame section. So here we need // to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc32 = fixupAddress - targetAddress + ref.addend(); return; case pointer64ToGOT: *loc64 = 0; return; case delta32ToGOT: *loc32 = inAtomAddress - fixupAddress; return; case unwindCIEToPersonalityFunction: // We don't emit unwindCIEToPersonalityFunction in -r mode as they are // implicitly generated from the data in the __eh_frame section. So here we // need to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc32 = targetAddress - fixupAddress; return; case addOffset12: llvm_unreachable("lazy reference kind implies GOT pass was run"); case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); case imageOffset: case imageOffsetGot: case unwindInfoToEhFrame: llvm_unreachable("fixup implies __unwind_info"); return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown arm64 Reference Kind"); } void ArchHandler_arm64::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); switch (static_cast(ref.kindValue())) { case branch26: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); } return; case page21: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); } return; case offset12: case offset12scale2: case offset12scale4: case offset12scale8: case offset12scale16: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); } return; case gotPage21: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4); return; case gotOffset12: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4); return; case tlvPage21: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4); return; case tlvOffset12: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4); return; case pointer64: if (ref.target()->name().empty()) appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rLength8); else appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength8); return; case delta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, ARM64_RELOC_SUBTRACTOR | rExtern | rLength8); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength8); return; case delta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case pointer64ToGOT: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8); return; case delta32ToGOT: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4); return; case addOffset12: llvm_unreachable("lazy reference kind implies GOT pass was run"); case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); case imageOffset: case imageOffsetGot: llvm_unreachable("deltas from mach_header can only be in final images"); case unwindCIEToPersonalityFunction: case unwindFDEToFunction: case unwindInfoToEhFrame: case negDelta32: // Do nothing. return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown arm64 Reference Kind"); } std::unique_ptr ArchHandler::create_arm64() { return std::unique_ptr(new ArchHandler_arm64()); } } // namespace mach_o } // namespace lld Index: vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86.cpp =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86.cpp (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86.cpp (revision 326905) @@ -1,640 +1,644 @@ //===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ArchHandler.h" #include "Atoms.h" #include "MachONormalizedFileBinaryUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; namespace lld { namespace mach_o { using llvm::support::ulittle16_t; using llvm::support::ulittle32_t; using llvm::support::little16_t; using llvm::support::little32_t; class ArchHandler_x86 : public ArchHandler { public: ArchHandler_x86() = default; ~ArchHandler_x86() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::x86; } const StubInfo &stubInfo() override { return _sStubInfo; } bool isCallSite(const Reference &) override; bool isNonCallBranch(const Reference &) override { return false; } bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; bool needsCompactUnwind() override { return false; } Reference::KindValue imageOffsetKind() override { return invalid; } Reference::KindValue imageOffsetKindIndirect() override { return invalid; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return invalid; } Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } Reference::KindValue unwindRefToFunctionKind() override{ return delta32; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue unwindRefToEhFrameKind() override { return invalid; } Reference::KindValue pointerKind() override { return invalid; } uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) override; bool isDataInCodeTransition(Reference::KindValue refKind) override { return refKind == modeCode || refKind == modeData; } Reference::KindValue dataInCodeTransitionStart( const MachODefinedAtom &atom) override { return modeData; } Reference::KindValue dataInCodeTransitionEnd( const MachODefinedAtom &atom) override { return modeCode; } private: static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfo; enum X86Kind : Reference::KindValue { invalid, /// for error condition modeCode, /// Content starting at this offset is code. modeData, /// Content starting at this offset is data. // Kinds found in mach-o .o files: branch32, /// ex: call _foo branch16, /// ex: callw _foo abs32, /// ex: movl _foo, %eax funcRel32, /// ex: movl _foo-L1(%eax), %eax pointer32, /// ex: .long _foo delta32, /// ex: .long _foo - . negDelta32, /// ex: .long . - _foo // Kinds introduced by Passes: lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. }; static bool useExternalRelocationTo(const Atom &target); void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress); }; //===----------------------------------------------------------------------===// // ArchHandler_x86 //===----------------------------------------------------------------------===// const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(modeCode), LLD_KIND_STRING_ENTRY(modeData), LLD_KIND_STRING_ENTRY(branch32), LLD_KIND_STRING_ENTRY(branch16), LLD_KIND_STRING_ENTRY(abs32), LLD_KIND_STRING_ENTRY(funcRel32), LLD_KIND_STRING_ENTRY(pointer32), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = { "dyld_stub_binder", // Lazy pointer references { Reference::KindArch::x86, pointer32, 0, 0 }, { Reference::KindArch::x86, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::x86, pointer32, 0, 0 }, // x86 code alignment 1, // Stub size and code 6, { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer { Reference::KindArch::x86, abs32, 2, 0 }, { false, 0, 0, 0 }, // Stub Helper size and code 10, { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper { Reference::KindArch::x86, lazyImmediateLocation, 1, 0 }, { Reference::KindArch::x86, branch32, 6, 0 }, // Stub helper image cache content type DefinedAtom::typeNonLazyPointer, // Stub Helper-Common size and code 12, // Stub helper alignment 2, { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind 0x90 }, // nop { Reference::KindArch::x86, abs32, 1, 0 }, { false, 0, 0, 0 }, { Reference::KindArch::x86, abs32, 7, 0 }, { false, 0, 0, 0 } }; bool ArchHandler_x86::isCallSite(const Reference &ref) { return (ref.kindValue() == branch32); } bool ArchHandler_x86::isPointer(const Reference &ref) { return (ref.kindValue() == pointer32); } bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) { if (!reloc.scattered) return false; return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) || (reloc.type == GENERIC_RELOC_SECTDIFF); } llvm::Error ArchHandler_x86::getReferenceInfo(const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { DefinedAtom::ContentPermissions perms; const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; switch (relocPattern(reloc)) { case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4: // ex: call _foo (and _foo undefined) *kind = branch32; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent; break; case GENERIC_RELOC_VANILLA | rPcRel | rLength4: // ex: call _foo (and _foo defined) *kind = branch32; targetAddress = fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); break; case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4: // ex: call _foo+n (and _foo defined) *kind = branch32; targetAddress = fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend = targetAddress - reloc.value; break; case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2: // ex: callw _foo (and _foo undefined) *kind = branch16; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent; break; case GENERIC_RELOC_VANILLA | rPcRel | rLength2: // ex: callw _foo (and _foo defined) *kind = branch16; targetAddress = fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); break; case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2: // ex: callw _foo+n (and _foo defined) *kind = branch16; targetAddress = fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend = targetAddress - reloc.value; break; case GENERIC_RELOC_VANILLA | rExtern | rLength4: // ex: movl _foo, %eax (and _foo undefined) // ex: .long _foo (and _foo undefined) perms = inAtom->permissions(); *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 : pointer32; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const ulittle32_t *)fixupContent; break; case GENERIC_RELOC_VANILLA | rLength4: // ex: movl _foo, %eax (and _foo defined) // ex: .long _foo (and _foo defined) perms = inAtom->permissions(); *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 : pointer32; targetAddress = *(const ulittle32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); break; case GENERIC_RELOC_VANILLA | rScattered | rLength4: // ex: .long _foo+n (and _foo defined) perms = inAtom->permissions(); *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 : pointer32; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend = *(const ulittle32_t *)fixupContent - reloc.value; break; default: return llvm::make_error("unsupported i386 relocation type"); } return llvm::Error::success(); } llvm::Error ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddr, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; DefinedAtom::ContentPermissions perms = inAtom->permissions(); uint32_t fromAddress; uint32_t toAddress; uint32_t value; const lld::Atom *fromTarget; Reference::Addend offsetInTo; Reference::Addend offsetInFrom; switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 | GENERIC_RELOC_PAIR | rScattered | rLength4): case ((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | GENERIC_RELOC_PAIR | rScattered | rLength4): toAddress = reloc1.value; fromAddress = reloc2.value; value = *(const little32_t *)fixupContent; if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (fromTarget != inAtom) { if (*target != inAtom) return llvm::make_error( "SECTDIFF relocation where neither target is in atom"); *kind = negDelta32; *addend = toAddress - value - fromAddress; *target = fromTarget; } else { if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) { // SECTDIFF relocations are used in i386 codegen where the function // prolog does a CALL to the next instruction which POPs the return // address into EBX which becomes the pic-base register. The POP // instruction is label the used for the subtrahend in expressions. // The funcRel32 kind represents the 32-bit delta to some symbol from // the start of the function (atom) containing the funcRel32. *kind = funcRel32; uint32_t ta = fromAddress + value - toAddress; *addend = ta - offsetInFrom; } else { *kind = delta32; *addend = fromAddress + value - toAddress; } } return llvm::Error::success(); break; default: return llvm::make_error("unsupported i386 relocation type"); } } void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) { // Copy raw bytes. std::copy(atom.rawContent().begin(), atom.rawContent().end(), atomContentBuffer.begin()); // Apply fix-ups. for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); uint64_t targetAddress = 0; if (isa(target)) targetAddress = findAddress(*target); uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress); } } } void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86); ulittle32_t *loc32 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); break; case branch16: *loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend(); break; case pointer32: case abs32: *loc32 = targetAddress + ref.addend(); break; case funcRel32: *loc32 = targetAddress - inAtomAddress + ref.addend(); break; case delta32: *loc32 = targetAddress - fixupAddress + ref.addend(); break; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); break; case modeCode: case modeData: case lazyPointer: // do nothing break; case lazyImmediateLocation: *loc32 = ref.addend(); break; case invalid: llvm_unreachable("invalid x86 Reference Kind"); break; } } void ArchHandler_x86::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86); bool useExternalReloc = useExternalRelocationTo(*ref.target()); ulittle16_t *loc16 = reinterpret_cast(loc); ulittle32_t *loc32 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: if (useExternalReloc) *loc32 = ref.addend() - (fixupAddress + 4); else *loc32 =(targetAddress - (fixupAddress+4)) + ref.addend(); break; case branch16: if (useExternalReloc) *loc16 = ref.addend() - (fixupAddress + 2); else *loc16 = (targetAddress - (fixupAddress+2)) + ref.addend(); break; case pointer32: case abs32: *loc32 = targetAddress + ref.addend(); break; case funcRel32: *loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME break; case delta32: *loc32 = targetAddress - fixupAddress + ref.addend(); break; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); break; case modeCode: case modeData: case lazyPointer: case lazyImmediateLocation: // do nothing break; case invalid: llvm_unreachable("invalid x86 Reference Kind"); break; } } bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) { // Undefined symbols are referenced via external relocations. if (isa(&target)) return true; if (const DefinedAtom *defAtom = dyn_cast(&target)) { switch (defAtom->merge()) { case DefinedAtom::mergeAsTentative: // Tentative definitions are referenced via external relocations. return true; case DefinedAtom::mergeAsWeak: case DefinedAtom::mergeAsWeakAndAddressUsed: // Global weak-defs are referenced via external relocations. return (defAtom->scope() == DefinedAtom::scopeGlobal); default: break; } } // Everything else is reference via an internal relocation. return false; } void ArchHandler_x86::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); bool useExternalReloc = useExternalRelocationTo(*ref.target()); switch (static_cast(ref.kindValue())) { case modeCode: case modeData: break; case branch32: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, GENERIC_RELOC_VANILLA | rPcRel | rLength4); } break; case branch16: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, GENERIC_RELOC_VANILLA | rPcRel | rLength2); } break; case pointer32: case abs32: if (useExternalReloc) appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, GENERIC_RELOC_VANILLA | rExtern | rLength4); else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_VANILLA | rScattered | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, GENERIC_RELOC_VANILLA | rLength4); } break; case funcRel32: appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_SECTDIFF | rScattered | rLength4); appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(), GENERIC_RELOC_PAIR | rScattered | rLength4); break; case delta32: appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_SECTDIFF | rScattered | rLength4); appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + ref.offsetInAtom(), GENERIC_RELOC_PAIR | rScattered | rLength4); break; case negDelta32: appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + ref.offsetInAtom(), GENERIC_RELOC_SECTDIFF | rScattered | rLength4); appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), GENERIC_RELOC_PAIR | rScattered | rLength4); break; case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); break; case invalid: llvm_unreachable("unknown x86 Reference Kind"); break; } } std::unique_ptr ArchHandler::create_x86() { return std::unique_ptr(new ArchHandler_x86()); } } // namespace mach_o } // namespace lld Index: vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp (revision 326905) @@ -1,861 +1,865 @@ //===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ArchHandler.h" #include "Atoms.h" #include "MachONormalizedFileBinaryUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; namespace lld { namespace mach_o { using llvm::support::ulittle32_t; using llvm::support::ulittle64_t; using llvm::support::little32_t; using llvm::support::little64_t; class ArchHandler_x86_64 : public ArchHandler { public: ArchHandler_x86_64() = default; ~ArchHandler_x86_64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::x86_64; } /// Used by GOTPass to locate GOT References bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::x86_64); switch (ref.kindValue()) { case ripRel32GotLoad: canBypassGOT = true; return true; case ripRel32Got: canBypassGOT = false; return true; case imageOffsetGot: canBypassGOT = false; return true; default: return false; } } bool isTLVAccess(const Reference &ref) const override { assert(ref.kindNamespace() == Reference::KindNamespace::mach_o); assert(ref.kindArch() == Reference::KindArch::x86_64); return ref.kindValue() == ripRel32Tlv; } void updateReferenceToTLV(const Reference *ref) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::x86_64); assert(ref->kindValue() == ripRel32Tlv); const_cast(ref)->setKindValue(ripRel32); } /// Used by GOTPass to update GOT References void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::x86_64); switch (ref->kindValue()) { case ripRel32Got: assert(targetNowGOT && "target must be GOT"); case ripRel32GotLoad: const_cast(ref) ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea); break; case imageOffsetGot: const_cast(ref)->setKindValue(imageOffset); break; default: llvm_unreachable("unknown GOT reference kind"); } } bool needsCompactUnwind() override { return true; } Reference::KindValue imageOffsetKind() override { return imageOffset; } Reference::KindValue imageOffsetKindIndirect() override { return imageOffsetGot; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return ripRel32Got; } Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } Reference::KindValue unwindRefToFunctionKind() override{ return unwindFDEToFunction; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } Reference::KindValue pointerKind() override { return pointer64; } uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } const StubInfo &stubInfo() override { return _sStubInfo; } bool isNonCallBranch(const Reference &) override { return false; } bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override { return (atom->contentType() == DefinedAtom::typeCString); } void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBase, llvm::MutableArrayRef atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) override; private: static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfo; enum X86_64Kind: Reference::KindValue { invalid, /// for error condition // Kinds found in mach-o .o files: branch32, /// ex: call _foo ripRel32, /// ex: movq _foo(%rip), %rax ripRel32Minus1, /// ex: movb $0x12, _foo(%rip) ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip) ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip) ripRel32Anon, /// ex: movq L1(%rip), %rax ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip) ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip) ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip) ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip) ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi pointer64, /// ex: .quad _foo pointer64Anon, /// ex: .quad L1 delta64, /// ex: .quad _foo - . delta32, /// ex: .long _foo - . delta64Anon, /// ex: .quad L1 - . delta32Anon, /// ex: .long L1 - . negDelta64, /// ex: .quad . - _foo negDelta32, /// ex: .long . - _foo // Kinds introduced by Passes: ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so /// "movq _foo@GOTPCREL(%rip), %rax" can be changed /// to "leaq _foo(%rip), %rax lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. imageOffset, /// Location contains offset of atom in final image imageOffsetGot, /// Location contains offset of GOT entry for atom in /// final image (typically personality function). unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in /// relocatable object (yay for implicit contracts!). unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to /// refer to __eh_frame entry. tlvInitSectionOffset /// Location contains offset tlv init-value atom /// within the __thread_data section. }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress); }; const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32), LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1), LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4), LLD_KIND_STRING_ENTRY(ripRel32Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon), LLD_KIND_STRING_ENTRY(ripRel32GotLoad), LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon), LLD_KIND_STRING_ENTRY(negDelta64), LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), LLD_KIND_STRING_ENTRY(tlvInitSectionOffset), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = { "dyld_stub_binder", // Lazy pointer references { Reference::KindArch::x86_64, pointer64, 0, 0 }, { Reference::KindArch::x86_64, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::x86_64, pointer64, 0, 0 }, // x86_64 code alignment 2^1 1, // Stub size and code 6, { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer { Reference::KindArch::x86_64, ripRel32, 2, 0 }, { false, 0, 0, 0 }, // Stub Helper size and code 10, { 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper { Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 }, { Reference::KindArch::x86_64, branch32, 6, 0 }, // Stub helper image cache content type DefinedAtom::typeNonLazyPointer, // Stub Helper-Common size and code 16, // Stub helper alignment 2, { 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11 0x41, 0x53, // push %r11 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip) 0x90 }, // nop { Reference::KindArch::x86_64, ripRel32, 3, 0 }, { false, 0, 0, 0 }, { Reference::KindArch::x86_64, ripRel32, 11, 0 }, { false, 0, 0, 0 } }; bool ArchHandler_x86_64::isCallSite(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::x86_64); return (ref.kindValue() == branch32); } bool ArchHandler_x86_64::isPointer(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::x86_64); Reference::KindValue kind = ref.kindValue(); return (kind == pointer64 || kind == pointer64Anon); } bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) { return (reloc.type == X86_64_RELOC_SUBTRACTOR); } Reference::KindValue ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { switch(relocPattern(reloc)) { case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4: return branch32; case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4: return ripRel32; case X86_64_RELOC_SIGNED | rPcRel | rLength4: return ripRel32Anon; case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4: return ripRel32Minus1; case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4: return ripRel32Minus1Anon; case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4: return ripRel32Minus2; case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4: return ripRel32Minus2Anon; case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4: return ripRel32Minus4; case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4: return ripRel32Minus4Anon; case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4: return ripRel32GotLoad; case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4: return ripRel32Got; case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4: return ripRel32Tlv; case X86_64_RELOC_UNSIGNED | rExtern | rLength8: return pointer64; case X86_64_RELOC_UNSIGNED | rLength8: return pointer64Anon; default: return invalid; } } llvm::Error ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { *kind = kindFromReloc(reloc); if (*kind == invalid) return llvm::make_error("unknown type"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; switch (*kind) { case branch32: case ripRel32: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return llvm::Error::success(); case ripRel32Minus1: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 1; return llvm::Error::success(); case ripRel32Minus2: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 2; return llvm::Error::success(); case ripRel32Minus4: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 4; return llvm::Error::success(); case ripRel32Anon: targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus1Anon: targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus2Anon: targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus4Anon: targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32GotLoad: case ripRel32Got: case ripRel32Tlv: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return llvm::Error::success(); case tlvInitSectionOffset: case pointer64: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's // initial value) we need to handle it specially. if (inAtom->contentType() == DefinedAtom::typeThunkTLV && offsetInAtom == 16) { *kind = tlvInitSectionOffset; assert(*addend == 0 && "TLV-init has non-zero addend?"); } else *addend = *(const little64_t *)fixupContent; return llvm::Error::success(); case pointer64Anon: targetAddress = *(const little64_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); default: llvm_unreachable("bad reloc kind"); } } llvm::Error ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; const lld::Atom *fromTarget; if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) return ec; switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | X86_64_RELOC_UNSIGNED | rExtern | rLength8): { if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent; if (inAtom == fromTarget) { if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindFDEToFunction; else *kind = delta64; *addend = encodedAddend + offsetInAtom; } else if (inAtom == *target) { *kind = negDelta64; *addend = encodedAddend - offsetInAtom; *target = fromTarget; } else return llvm::make_error("Invalid pointer diff"); return llvm::Error::success(); } case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | X86_64_RELOC_UNSIGNED | rExtern | rLength4): { if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent; if (inAtom == fromTarget) { *kind = delta32; *addend = encodedAddend + offsetInAtom; } else if (inAtom == *target) { *kind = negDelta32; *addend = encodedAddend - offsetInAtom; *target = fromTarget; } else return llvm::make_error("Invalid pointer diff"); return llvm::Error::success(); } case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | X86_64_RELOC_UNSIGNED | rLength8): if (fromTarget != inAtom) return llvm::make_error("pointer diff not in base atom"); *kind = delta64Anon; targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent; return atomFromAddress(reloc2.symbol, targetAddress, target, addend); case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | X86_64_RELOC_UNSIGNED | rLength4): if (fromTarget != inAtom) return llvm::make_error("pointer diff not in base atom"); *kind = delta32Anon; targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent; return atomFromAddress(reloc2.symbol, targetAddress, target, addend); default: return llvm::make_error("unknown pair"); } } void ArchHandler_x86_64::generateAtomContent( const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) { // Copy raw bytes. std::copy(atom.rawContent().begin(), atom.rawContent().end(), atomContentBuffer.begin()); // Apply fix-ups. for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); uint64_t targetAddress = 0; if (isa(target)) targetAddress = findAddress(*target); uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, imageBaseAddress, findSectionAddress); } } } void ArchHandler_x86_64::applyFixupFinal( const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86_64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: case ripRel32: case ripRel32Anon: case ripRel32Got: case ripRel32GotLoad: case ripRel32Tlv: *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case pointer64: case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; case tlvInitSectionOffset: *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); return; case ripRel32Minus1: case ripRel32Minus1Anon: *loc32 = targetAddress - (fixupAddress + 5) + ref.addend(); return; case ripRel32Minus2: case ripRel32Minus2Anon: *loc32 = targetAddress - (fixupAddress + 6) + ref.addend(); return; case ripRel32Minus4: case ripRel32Minus4Anon: *loc32 = targetAddress - (fixupAddress + 8) + ref.addend(); return; case delta32: case delta32Anon: *loc32 = targetAddress - fixupAddress + ref.addend(); return; case delta64: case delta64Anon: case unwindFDEToFunction: *loc64 = targetAddress - fixupAddress + ref.addend(); return; case ripRel32GotLoadNowLea: // Change MOVQ to LEA assert(loc[-2] == 0x8B); loc[-2] = 0x8D; *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case negDelta64: *loc64 = fixupAddress - targetAddress + ref.addend(); return; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); return; case lazyPointer: // Do nothing return; case lazyImmediateLocation: *loc32 = ref.addend(); return; case imageOffset: case imageOffsetGot: *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); return; case unwindInfoToEhFrame: { uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); assert(val < 0xffffffU && "offset in __eh_frame too large"); *loc32 = (*loc32 & 0xff000000U) | val; return; } case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("invalid x86_64 Reference Kind"); } void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86_64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: case ripRel32: case ripRel32Got: case ripRel32GotLoad: case ripRel32Tlv: *loc32 = ref.addend(); return; case ripRel32Anon: *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); return; case tlvInitSectionOffset: case pointer64: *loc64 = ref.addend(); return; case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; case ripRel32Minus1: *loc32 = ref.addend() - 1; return; case ripRel32Minus1Anon: *loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend(); return; case ripRel32Minus2: *loc32 = ref.addend() - 2; return; case ripRel32Minus2Anon: *loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend(); return; case ripRel32Minus4: *loc32 = ref.addend() - 4; return; case ripRel32Minus4Anon: *loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend(); return; case delta32: *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case delta32Anon: // The value we write here should be the the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: // _base: ... // Lfixup: .quad Ltarget - . // ... // Ltarget: // // Then we want to encode the value (Ltarget + addend) - (LFixup - _base) *loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress); return; case delta64: *loc64 = ref.addend() + inAtomAddress - fixupAddress; return; case delta64Anon: // The value we write here should be the the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: // _base: ... // Lfixup: .quad Ltarget - . // ... // Ltarget: // // Then we want to encode the value (Ltarget + addend) - (LFixup - _base) *loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress); return; case negDelta64: *loc64 = ref.addend() + fixupAddress - inAtomAddress; return; case negDelta32: *loc32 = ref.addend() + fixupAddress - inAtomAddress; return; case ripRel32GotLoadNowLea: llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); return; case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; case imageOffset: case imageOffsetGot: case unwindInfoToEhFrame: llvm_unreachable("fixup implies __unwind_info"); return; case unwindFDEToFunction: // Do nothing for now return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown x86_64 Reference Kind"); } void ArchHandler_x86_64::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86_64); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); switch (static_cast(ref.kindValue())) { case branch32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4); return; case ripRel32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 ); return; case ripRel32Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED | rPcRel | rLength4 ); return; case ripRel32Got: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 ); return; case ripRel32GotLoad: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); return; case ripRel32Tlv: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 ); return; case tlvInitSectionOffset: case pointer64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8); return; case pointer64Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength8); return; case ripRel32Minus1: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus1Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 ); return; case ripRel32Minus2: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus2Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 ); return; case ripRel32Minus4: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus4Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 ); return; case delta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case delta32Anon: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength4 ); return; case delta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8 ); return; case delta64Anon: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength8 ); return; case unwindFDEToFunction: case unwindInfoToEhFrame: return; case negDelta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case negDelta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8 ); return; case ripRel32GotLoadNowLea: llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); return; case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; case imageOffset: case imageOffsetGot: llvm_unreachable("__unwind_info references should have been resolved"); return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown x86_64 Reference Kind"); } std::unique_ptr ArchHandler::create_x86_64() { return std::unique_ptr(new ArchHandler_x86_64()); } } // namespace mach_o } // namespace lld Index: vendor/lld/dist/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp =================================================================== --- vendor/lld/dist/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp (revision 326904) +++ vendor/lld/dist/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp (revision 326905) @@ -1,1600 +1,1657 @@ //===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp ------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file Converts from in-memory Atoms to in-memory normalized mach-o. /// /// +------------+ /// | normalized | /// +------------+ /// ^ /// | /// | /// +-------+ /// | Atoms | /// +-------+ #include "ArchHandler.h" #include "DebugInfo.h" #include "MachONormalizedFile.h" #include "MachONormalizedFileBinaryUtils.h" #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include #include #include using llvm::StringRef; using llvm::isa; using namespace llvm::MachO; using namespace lld::mach_o::normalized; using namespace lld; namespace { struct AtomInfo { const DefinedAtom *atom; uint64_t offsetInSection; }; struct SectionInfo { SectionInfo(StringRef seg, StringRef sect, SectionType type, const MachOLinkingContext &ctxt, uint32_t attr, bool relocsToDefinedCanBeImplicit); StringRef segmentName; StringRef sectionName; SectionType type; uint32_t attributes; uint64_t address; uint64_t size; uint16_t alignment; /// If this is set, the any relocs in this section which point to defined /// addresses can be implicitly generated. This is the case for the /// __eh_frame section where references to the function can be implicit if the /// function is defined. bool relocsToDefinedCanBeImplicit; std::vector atomsAndOffsets; uint32_t normalizedSectionIndex; uint32_t finalSectionIndex; }; SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t, const MachOLinkingContext &ctxt, uint32_t attrs, bool relocsToDefinedCanBeImplicit) : segmentName(sg), sectionName(sct), type(t), attributes(attrs), address(0), size(0), alignment(1), relocsToDefinedCanBeImplicit(relocsToDefinedCanBeImplicit), normalizedSectionIndex(0), finalSectionIndex(0) { uint16_t align = 1; if (ctxt.sectionAligned(segmentName, sectionName, align)) { alignment = align; } } struct SegmentInfo { SegmentInfo(StringRef name); StringRef name; uint64_t address; uint64_t size; uint32_t init_access; uint32_t max_access; std::vector sections; uint32_t normalizedSegmentIndex; }; SegmentInfo::SegmentInfo(StringRef n) : name(n), address(0), size(0), init_access(0), max_access(0), normalizedSegmentIndex(0) { } class Util { public: Util(const MachOLinkingContext &ctxt) : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr), _hasTLVDescriptors(false), _subsectionsViaSymbols(true) {} ~Util(); void processDefinedAtoms(const lld::File &atomFile); void processAtomAttributes(const DefinedAtom *atom); void assignAtomToSection(const DefinedAtom *atom); void organizeSections(); void assignAddressesToSections(const NormalizedFile &file); uint32_t fileFlags(); void copySegmentInfo(NormalizedFile &file); void copySectionInfo(NormalizedFile &file); void updateSectionInfo(NormalizedFile &file); void buildAtomToAddressMap(); llvm::Error synthesizeDebugNotes(NormalizedFile &file); llvm::Error addSymbols(const lld::File &atomFile, NormalizedFile &file); void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file); void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file); void addExportInfo(const lld::File &, NormalizedFile &file); void addSectionRelocs(const lld::File &, NormalizedFile &file); void addFunctionStarts(const lld::File &, NormalizedFile &file); void buildDataInCodeArray(const lld::File &, NormalizedFile &file); void addDependentDylibs(const lld::File &, NormalizedFile &file); void copyEntryPointAddress(NormalizedFile &file); void copySectionContent(NormalizedFile &file); bool allSourceFilesHaveMinVersions() const { return _allSourceFilesHaveMinVersions; } uint32_t minVersion() const { return _minVersion; } LoadCommandType minVersionCommandType() const { return _minVersionCommandType; } private: typedef std::map TypeToSection; typedef llvm::DenseMap AtomToAddress; struct DylibInfo { int ordinal; bool hasWeak; bool hasNonWeak; }; typedef llvm::StringMap DylibPathToInfo; SectionInfo *sectionForAtom(const DefinedAtom*); SectionInfo *getRelocatableSection(DefinedAtom::ContentType type); SectionInfo *getFinalSection(DefinedAtom::ContentType type); void appendAtom(SectionInfo *sect, const DefinedAtom *atom); SegmentInfo *segmentForName(StringRef segName); void layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr); void layoutSectionsInTextSegment(size_t, SegmentInfo *, uint64_t &); void copySectionContent(SectionInfo *si, ContentBytes &content); uint16_t descBits(const DefinedAtom* atom); int dylibOrdinal(const SharedLibraryAtom *sa); void segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, uint64_t &segmentStartAddr); const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom); const Atom *targetOfStub(const DefinedAtom *stubAtom); llvm::Error getSymbolTableRegion(const DefinedAtom* atom, bool &inGlobalsRegion, SymbolScope &symbolScope); void appendSection(SectionInfo *si, NormalizedFile &file); uint32_t sectionIndexForAtom(const Atom *atom); + void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, + NormalizedFile &file); typedef llvm::DenseMap AtomToIndex; struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; struct AtomSorter { bool operator()(const AtomAndIndex &left, const AtomAndIndex &right); }; struct SegmentSorter { bool operator()(const SegmentInfo *left, const SegmentInfo *right); static unsigned weight(const SegmentInfo *); }; struct TextSectionSorter { bool operator()(const SectionInfo *left, const SectionInfo *right); static unsigned weight(const SectionInfo *); }; const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; llvm::BumpPtrAllocator _allocator; std::vector _sectionInfos; std::vector _segmentInfos; TypeToSection _sectionMap; std::vector _customSections; AtomToAddress _atomToAddress; DylibPathToInfo _dylibInfo; const DefinedAtom *_entryAtom; AtomToIndex _atomToSymbolIndex; std::vector _machHeaderAliasAtoms; bool _hasTLVDescriptors; bool _subsectionsViaSymbols; bool _allSourceFilesHaveMinVersions = true; LoadCommandType _minVersionCommandType = (LoadCommandType)0; uint32_t _minVersion = 0; std::vector _stabs; }; Util::~Util() { // The SectionInfo structs are BumpPtr allocated, but atomsAndOffsets needs // to be deleted. for (SectionInfo *si : _sectionInfos) { // clear() destroys vector elements, but does not deallocate. // Instead use swap() to deallocate vector buffer. std::vector empty; si->atomsAndOffsets.swap(empty); } // The SegmentInfo structs are BumpPtr allocated, but sections needs // to be deleted. for (SegmentInfo *sgi : _segmentInfos) { std::vector empty2; sgi->sections.swap(empty2); } } SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) { StringRef segmentName; StringRef sectionName; SectionType sectionType; SectionAttr sectionAttrs; bool relocsToDefinedCanBeImplicit; // Use same table used by when parsing .o files. relocatableSectionInfoForContentType(type, segmentName, sectionName, sectionType, sectionAttrs, relocsToDefinedCanBeImplicit); // If we already have a SectionInfo with this name, re-use it. // This can happen if two ContentType map to the same mach-o section. for (auto sect : _sectionMap) { if (sect.second->sectionName.equals(sectionName) && sect.second->segmentName.equals(segmentName)) { return sect.second; } } // Otherwise allocate new SectionInfo object. auto *sect = new (_allocator) SectionInfo(segmentName, sectionName, sectionType, _ctx, sectionAttrs, relocsToDefinedCanBeImplicit); _sectionInfos.push_back(sect); _sectionMap[type] = sect; return sect; } #define ENTRY(seg, sect, type, atomType) \ {seg, sect, type, DefinedAtom::atomType } struct MachOFinalSectionFromAtomType { StringRef segmentName; StringRef sectionName; SectionType sectionType; DefinedAtom::ContentType atomType; }; const MachOFinalSectionFromAtomType sectsToAtomType[] = { ENTRY("__TEXT", "__text", S_REGULAR, typeCode), ENTRY("__TEXT", "__text", S_REGULAR, typeMachHeader), ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString), ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String), ENTRY("__TEXT", "__const", S_REGULAR, typeConstant), ENTRY("__TEXT", "__const", S_4BYTE_LITERALS, typeLiteral4), ENTRY("__TEXT", "__const", S_8BYTE_LITERALS, typeLiteral8), ENTRY("__TEXT", "__const", S_16BYTE_LITERALS, typeLiteral16), ENTRY("__TEXT", "__stubs", S_SYMBOL_STUBS, typeStub), ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper), ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo), ENTRY("__DATA", "__data", S_REGULAR, typeData), ENTRY("__DATA", "__const", S_REGULAR, typeConstData), ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), ENTRY("__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS, typeLazyPointer), ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, typeInitializerPtr), ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS, typeTerminatorPtr), ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, typeGOT), ENTRY("__DATA", "__nl_symbol_ptr", S_NON_LAZY_SYMBOL_POINTERS, typeNonLazyPointer), ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, typeThunkTLV), ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData), ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS, typeTLVInitializerPtr), ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, typeTLVInitialZeroFill), ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), }; #undef ENTRY SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { for (auto &p : sectsToAtomType) { if (p.atomType != atomType) continue; SectionAttr sectionAttrs = 0; switch (atomType) { case DefinedAtom::typeMachHeader: case DefinedAtom::typeCode: case DefinedAtom::typeStub: case DefinedAtom::typeStubHelper: sectionAttrs = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS; break; case DefinedAtom::typeThunkTLV: _hasTLVDescriptors = true; break; default: break; } // If we already have a SectionInfo with this name, re-use it. // This can happen if two ContentType map to the same mach-o section. for (auto sect : _sectionMap) { if (sect.second->sectionName.equals(p.sectionName) && sect.second->segmentName.equals(p.segmentName)) { return sect.second; } } // Otherwise allocate new SectionInfo object. auto *sect = new (_allocator) SectionInfo( p.segmentName, p.sectionName, p.sectionType, _ctx, sectionAttrs, /* relocsToDefinedCanBeImplicit */ false); _sectionInfos.push_back(sect); _sectionMap[atomType] = sect; return sect; } llvm_unreachable("content type not yet supported"); } SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { // Section for this atom is derived from content type. DefinedAtom::ContentType type = atom->contentType(); auto pos = _sectionMap.find(type); if ( pos != _sectionMap.end() ) return pos->second; bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); return rMode ? getRelocatableSection(type) : getFinalSection(type); } else { // This atom needs to be in a custom section. StringRef customName = atom->customSectionName(); // Look to see if we have already allocated the needed custom section. for(SectionInfo *sect : _customSections) { const DefinedAtom *firstAtom = sect->atomsAndOffsets.front().atom; if (firstAtom->customSectionName().equals(customName)) { return sect; } } // Not found, so need to create a new custom section. size_t seperatorIndex = customName.find('/'); assert(seperatorIndex != StringRef::npos); StringRef segName = customName.slice(0, seperatorIndex); StringRef sectName = customName.drop_front(seperatorIndex + 1); auto *sect = new (_allocator) SectionInfo(segName, sectName, S_REGULAR, _ctx, 0, /* relocsToDefinedCanBeImplicit */ false); _customSections.push_back(sect); _sectionInfos.push_back(sect); return sect; } } void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) { // Figure out offset for atom in this section given alignment constraints. uint64_t offset = sect->size; DefinedAtom::Alignment atomAlign = atom->alignment(); uint64_t align = atomAlign.value; uint64_t requiredModulus = atomAlign.modulus; uint64_t currentModulus = (offset % align); if ( currentModulus != requiredModulus ) { if ( requiredModulus > currentModulus ) offset += requiredModulus-currentModulus; else offset += align+requiredModulus-currentModulus; } // Record max alignment of any atom in this section. if (align > sect->alignment) sect->alignment = atomAlign.value; // Assign atom to this section with this offset. AtomInfo ai = {atom, offset}; sect->atomsAndOffsets.push_back(ai); // Update section size to include this atom. sect->size = offset + atom->size(); } void Util::processDefinedAtoms(const lld::File &atomFile) { for (const DefinedAtom *atom : atomFile.defined()) { processAtomAttributes(atom); assignAtomToSection(atom); } } void Util::processAtomAttributes(const DefinedAtom *atom) { if (auto *machoFile = dyn_cast(&atom->file())) { // If the file doesn't use subsections via symbols, then make sure we don't // add that flag to the final output file if we have a relocatable file. if (!machoFile->subsectionsViaSymbols()) _subsectionsViaSymbols = false; // All the source files must have min versions for us to output an object // file with a min version. if (auto v = machoFile->minVersion()) _minVersion = std::max(_minVersion, v); else _allSourceFilesHaveMinVersions = false; // If we don't have a platform load command, but one of the source files // does, then take the one from the file. if (!_minVersionCommandType) if (auto v = machoFile->minVersionLoadCommandKind()) _minVersionCommandType = v; } } void Util::assignAtomToSection(const DefinedAtom *atom) { if (atom->contentType() == DefinedAtom::typeMachHeader) { _machHeaderAliasAtoms.push_back(atom); // Assign atom to this section with this offset. AtomInfo ai = {atom, 0}; sectionForAtom(atom)->atomsAndOffsets.push_back(ai); } else if (atom->contentType() == DefinedAtom::typeDSOHandle) _machHeaderAliasAtoms.push_back(atom); else appendAtom(sectionForAtom(atom), atom); } SegmentInfo *Util::segmentForName(StringRef segName) { for (SegmentInfo *si : _segmentInfos) { if ( si->name.equals(segName) ) return si; } auto *info = new (_allocator) SegmentInfo(segName); // Set the initial segment protection. if (segName.equals("__TEXT")) info->init_access = VM_PROT_READ | VM_PROT_EXECUTE; else if (segName.equals("__PAGEZERO")) info->init_access = 0; else if (segName.equals("__LINKEDIT")) info->init_access = VM_PROT_READ; else { // All others default to read-write info->init_access = VM_PROT_READ | VM_PROT_WRITE; } // Set max segment protection // Note, its overkill to use a switch statement here, but makes it so much // easier to use switch coverage to catch new cases. switch (_ctx.os()) { case lld::MachOLinkingContext::OS::unknown: case lld::MachOLinkingContext::OS::macOSX: case lld::MachOLinkingContext::OS::iOS_simulator: if (segName.equals("__PAGEZERO")) { info->max_access = 0; break; } // All others default to all info->max_access = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; break; case lld::MachOLinkingContext::OS::iOS: // iPhoneOS always uses same protection for max and initial info->max_access = info->init_access; break; } _segmentInfos.push_back(info); return info; } unsigned Util::SegmentSorter::weight(const SegmentInfo *seg) { return llvm::StringSwitch(seg->name) .Case("__PAGEZERO", 1) .Case("__TEXT", 2) .Case("__DATA", 3) .Default(100); } bool Util::SegmentSorter::operator()(const SegmentInfo *left, const SegmentInfo *right) { return (weight(left) < weight(right)); } unsigned Util::TextSectionSorter::weight(const SectionInfo *sect) { return llvm::StringSwitch(sect->sectionName) .Case("__text", 1) .Case("__stubs", 2) .Case("__stub_helper", 3) .Case("__const", 4) .Case("__cstring", 5) .Case("__unwind_info", 98) .Case("__eh_frame", 99) .Default(10); } bool Util::TextSectionSorter::operator()(const SectionInfo *left, const SectionInfo *right) { return (weight(left) < weight(right)); } void Util::organizeSections() { // NOTE!: Keep this in sync with assignAddressesToSections. switch (_ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: // Main executables, need a zero-page segment segmentForName("__PAGEZERO"); // Fall into next case. LLVM_FALLTHROUGH; case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: // All dynamic code needs TEXT segment to hold the load commands. segmentForName("__TEXT"); break; default: break; } segmentForName("__LINKEDIT"); // Group sections into segments. for (SectionInfo *si : _sectionInfos) { SegmentInfo *seg = segmentForName(si->segmentName); seg->sections.push_back(si); } // Sort segments. std::sort(_segmentInfos.begin(), _segmentInfos.end(), SegmentSorter()); // Sort sections within segments. for (SegmentInfo *seg : _segmentInfos) { if (seg->name.equals("__TEXT")) { std::sort(seg->sections.begin(), seg->sections.end(), TextSectionSorter()); } } // Record final section indexes. uint32_t segmentIndex = 0; uint32_t sectionIndex = 1; for (SegmentInfo *seg : _segmentInfos) { seg->normalizedSegmentIndex = segmentIndex++; for (SectionInfo *sect : seg->sections) sect->finalSectionIndex = sectionIndex++; } } void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) { seg->address = addr; for (SectionInfo *sect : seg->sections) { sect->address = llvm::alignTo(addr, sect->alignment); addr = sect->address + sect->size; } seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); } // __TEXT segment lays out backwards so padding is at front after load commands. void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, uint64_t &addr) { seg->address = addr; // Walks sections starting at end to calculate padding for start. int64_t taddr = 0; for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) { SectionInfo *sect = *it; taddr -= sect->size; taddr = taddr & (0 - sect->alignment); } int64_t padding = taddr - hlcSize; while (padding < 0) padding += _ctx.pageSize(); // Start assigning section address starting at padded offset. addr += (padding + hlcSize); for (SectionInfo *sect : seg->sections) { sect->address = llvm::alignTo(addr, sect->alignment); addr = sect->address + sect->size; } seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize()); } void Util::assignAddressesToSections(const NormalizedFile &file) { // NOTE!: Keep this in sync with organizeSections. size_t hlcSize = headerAndLoadCommandsSize(file); uint64_t address = 0; for (SegmentInfo *seg : _segmentInfos) { if (seg->name.equals("__PAGEZERO")) { seg->size = _ctx.pageZeroSize(); address += seg->size; } else if (seg->name.equals("__TEXT")) { // _ctx.baseAddress() == 0 implies it was either unspecified or // pageZeroSize is also 0. In either case resetting address is safe. address = _ctx.baseAddress() ? _ctx.baseAddress() : address; layoutSectionsInTextSegment(hlcSize, seg, address); } else layoutSectionsInSegment(seg, address); address = llvm::alignTo(address, _ctx.pageSize()); } DEBUG_WITH_TYPE("WriterMachO-norm", llvm::dbgs() << "assignAddressesToSections()\n"; for (SegmentInfo *sgi : _segmentInfos) { llvm::dbgs() << " address=" << llvm::format("0x%08llX", sgi->address) << ", size=" << llvm::format("0x%08llX", sgi->size) << ", segment-name='" << sgi->name << "'\n"; for (SectionInfo *si : sgi->sections) { llvm::dbgs()<< " addr=" << llvm::format("0x%08llX", si->address) << ", size=" << llvm::format("0x%08llX", si->size) << ", section-name='" << si->sectionName << "\n"; } } ); } void Util::copySegmentInfo(NormalizedFile &file) { for (SegmentInfo *sgi : _segmentInfos) { Segment seg; seg.name = sgi->name; seg.address = sgi->address; seg.size = sgi->size; seg.init_access = sgi->init_access; seg.max_access = sgi->max_access; file.segments.push_back(seg); } } void Util::appendSection(SectionInfo *si, NormalizedFile &file) { // Add new empty section to end of file.sections. Section temp; file.sections.push_back(std::move(temp)); Section* normSect = &file.sections.back(); // Copy fields to normalized section. normSect->segmentName = si->segmentName; normSect->sectionName = si->sectionName; normSect->type = si->type; normSect->attributes = si->attributes; normSect->address = si->address; normSect->alignment = si->alignment; // Record where normalized section is. si->normalizedSectionIndex = file.sections.size()-1; } void Util::copySectionContent(NormalizedFile &file) { const bool r = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); // Utility function for ArchHandler to find address of atom in output file. auto addrForAtom = [&] (const Atom &atom) -> uint64_t { auto pos = _atomToAddress.find(&atom); assert(pos != _atomToAddress.end()); return pos->second; }; auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t { for (const SectionInfo *sectInfo : _sectionInfos) for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) if (atomInfo.atom == &atom) return sectInfo->address; llvm_unreachable("atom not assigned to section"); }; for (SectionInfo *si : _sectionInfos) { Section *normSect = &file.sections[si->normalizedSectionIndex]; if (isZeroFillSection(si->type)) { const uint8_t *empty = nullptr; normSect->content = llvm::makeArrayRef(empty, si->size); continue; } // Copy content from atoms to content buffer for section. llvm::MutableArrayRef sectionContent; if (si->size) { uint8_t *sectContent = file.ownedAllocations.Allocate(si->size); sectionContent = llvm::MutableArrayRef(sectContent, si->size); normSect->content = sectionContent; } for (AtomInfo &ai : si->atomsAndOffsets) { if (!ai.atom->size()) { assert(ai.atom->begin() == ai.atom->end() && "Cannot have references without content"); continue; } auto atomContent = sectionContent.slice(ai.offsetInSection, ai.atom->size()); _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, sectionAddrForAtom, _ctx.baseAddress(), atomContent); } } } void Util::copySectionInfo(NormalizedFile &file) { file.sections.reserve(_sectionInfos.size()); // Write sections grouped by segment. for (SegmentInfo *sgi : _segmentInfos) { for (SectionInfo *si : sgi->sections) { appendSection(si, file); } } } void Util::updateSectionInfo(NormalizedFile &file) { file.sections.reserve(_sectionInfos.size()); // sections grouped by segment. for (SegmentInfo *sgi : _segmentInfos) { Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex]; normSeg->address = sgi->address; normSeg->size = sgi->size; for (SectionInfo *si : sgi->sections) { Section *normSect = &file.sections[si->normalizedSectionIndex]; normSect->address = si->address; } } } void Util::copyEntryPointAddress(NormalizedFile &nFile) { if (!_entryAtom) { nFile.entryAddress = 0; return; } if (_ctx.outputTypeHasEntry()) { if (_archHandler.isThumbFunction(*_entryAtom)) nFile.entryAddress = (_atomToAddress[_entryAtom] | 1); else nFile.entryAddress = _atomToAddress[_entryAtom]; } } void Util::buildAtomToAddressMap() { DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << "assign atom addresses:\n"); const bool lookForEntry = _ctx.outputTypeHasEntry(); for (SectionInfo *sect : _sectionInfos) { for (const AtomInfo &info : sect->atomsAndOffsets) { _atomToAddress[info.atom] = sect->address + info.offsetInSection; if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) && (info.atom->size() != 0) && info.atom->name() == _ctx.entrySymbolName()) { _entryAtom = info.atom; } DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << " address=" << llvm::format("0x%016X", _atomToAddress[info.atom]) << llvm::format(" 0x%09lX", info.atom) << ", file=#" << info.atom->file().ordinal() << ", atom=#" << info.atom->ordinal() << ", name=" << info.atom->name() << ", type=" << info.atom->contentType() << "\n"); } } DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << "assign header alias atom addresses:\n"); for (const Atom *atom : _machHeaderAliasAtoms) { _atomToAddress[atom] = _ctx.baseAddress(); #ifndef NDEBUG if (auto *definedAtom = dyn_cast(atom)) { DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << " address=" << llvm::format("0x%016X", _atomToAddress[atom]) << llvm::format(" 0x%09lX", atom) << ", file=#" << definedAtom->file().ordinal() << ", atom=#" << definedAtom->ordinal() << ", name=" << definedAtom->name() << ", type=" << definedAtom->contentType() << "\n"); } else { DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << " address=" << llvm::format("0x%016X", _atomToAddress[atom]) << " atom=" << atom << " name=" << atom->name() << "\n"); } #endif } } llvm::Error Util::synthesizeDebugNotes(NormalizedFile &file) { // Bail out early if we don't need to generate a debug map. if (_ctx.debugInfoMode() == MachOLinkingContext::DebugInfoMode::noDebugMap) return llvm::Error::success(); std::vector atomsNeedingDebugNotes; std::set filesWithStabs; bool objFileHasDwarf = false; const File *objFile = nullptr; for (SectionInfo *sect : _sectionInfos) { for (const AtomInfo &info : sect->atomsAndOffsets) { if (const DefinedAtom *atom = dyn_cast(info.atom)) { // FIXME: No stabs/debug-notes for symbols that wouldn't be in the // symbol table. // FIXME: No stabs/debug-notes for kernel dtrace probes. if (atom->contentType() == DefinedAtom::typeCFI || atom->contentType() == DefinedAtom::typeCString) continue; // Whenever we encounter a new file, update the 'objfileHasDwarf' flag. if (&info.atom->file() != objFile) { objFileHasDwarf = false; if (const mach_o::MachOFile *atomFile = dyn_cast(&info.atom->file())) { if (atomFile->debugInfo()) { if (isa(atomFile->debugInfo())) objFileHasDwarf = true; else if (isa(atomFile->debugInfo())) filesWithStabs.insert(atomFile); } } } // If this atom is from a file that needs dwarf, add it to the list. if (objFileHasDwarf) atomsNeedingDebugNotes.push_back(info.atom); } } } // Sort atoms needing debug notes by file ordinal, then atom ordinal. std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), [](const DefinedAtom *lhs, const DefinedAtom *rhs) { if (lhs->file().ordinal() != rhs->file().ordinal()) return (lhs->file().ordinal() < rhs->file().ordinal()); return (lhs->ordinal() < rhs->ordinal()); }); // FIXME: Handle : Add -add_ast_path option to \ // linker which add N_AST stab entry to output // See OutputFile::synthesizeDebugNotes in ObjectFile.cpp in ld64. StringRef oldFileName = ""; StringRef oldDirPath = ""; bool wroteStartSO = false; std::unordered_set seenFiles; for (const DefinedAtom *atom : atomsNeedingDebugNotes) { const auto &atomFile = cast(atom->file()); assert(dyn_cast_or_null(atomFile.debugInfo()) && "file for atom needing debug notes does not contain dwarf"); auto &dwarf = cast(*atomFile.debugInfo()); auto &tu = dwarf.translationUnitSource(); StringRef newFileName = tu.name; StringRef newDirPath = tu.path; // Add an SO whenever the TU source file changes. if (newFileName != oldFileName || newDirPath != oldDirPath) { // Translation unit change, emit ending SO if (oldFileName != "") _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, "")); oldFileName = newFileName; oldDirPath = newDirPath; // If newDirPath doesn't end with a '/' we need to add one: if (newDirPath.back() != '/') { char *p = file.ownedAllocations.Allocate(newDirPath.size() + 2); memcpy(p, newDirPath.data(), newDirPath.size()); p[newDirPath.size()] = '/'; p[newDirPath.size() + 1] = '\0'; newDirPath = p; } // New translation unit, emit start SOs: _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newDirPath)); _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newFileName)); // Synthesize OSO for start of file. char *fullPath = nullptr; { SmallString<1024> pathBuf(atomFile.path()); if (auto EC = llvm::sys::fs::make_absolute(pathBuf)) return llvm::errorCodeToError(EC); fullPath = file.ownedAllocations.Allocate(pathBuf.size() + 1); memcpy(fullPath, pathBuf.c_str(), pathBuf.size() + 1); } // Get mod time. uint32_t modTime = 0; llvm::sys::fs::file_status stat; if (!llvm::sys::fs::status(fullPath, stat)) if (llvm::sys::fs::exists(stat)) modTime = llvm::sys::toTimeT(stat.getLastModificationTime()); _stabs.push_back(mach_o::Stab(nullptr, N_OSO, _ctx.getCPUSubType(), 1, modTime, fullPath)); // linker should put cpusubtype in n_sect field // of nlist entry for N_OSO debug note entries. wroteStartSO = true; } if (atom->contentType() == DefinedAtom::typeCode) { // Synthesize BNSYM and start FUN stabs. _stabs.push_back(mach_o::Stab(atom, N_BNSYM, 1, 0, 0, "")); _stabs.push_back(mach_o::Stab(atom, N_FUN, 1, 0, 0, atom->name())); // Synthesize any SOL stabs needed // FIXME: add SOL stabs. _stabs.push_back(mach_o::Stab(nullptr, N_FUN, 0, 0, atom->rawContent().size(), "")); _stabs.push_back(mach_o::Stab(nullptr, N_ENSYM, 1, 0, atom->rawContent().size(), "")); } else { if (atom->scope() == Atom::scopeTranslationUnit) _stabs.push_back(mach_o::Stab(atom, N_STSYM, 1, 0, 0, atom->name())); else _stabs.push_back(mach_o::Stab(nullptr, N_GSYM, 1, 0, 0, atom->name())); } } // Emit ending SO if necessary. if (wroteStartSO) _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, "")); // Copy any stabs from .o file. for (const auto *objFile : filesWithStabs) { const auto &stabsList = cast(objFile->debugInfo())->stabs(); for (auto &stab : stabsList) { // FIXME: Drop stabs whose atoms have been dead-stripped. _stabs.push_back(stab); } } return llvm::Error::success(); } uint16_t Util::descBits(const DefinedAtom* atom) { uint16_t desc = 0; switch (atom->merge()) { case lld::DefinedAtom::mergeNo: case lld::DefinedAtom::mergeAsTentative: break; case lld::DefinedAtom::mergeAsWeak: case lld::DefinedAtom::mergeAsWeakAndAddressUsed: desc |= N_WEAK_DEF; break; case lld::DefinedAtom::mergeSameNameAndSize: case lld::DefinedAtom::mergeByLargestSection: case lld::DefinedAtom::mergeByContent: llvm_unreachable("Unsupported DefinedAtom::merge()"); break; } if (atom->contentType() == lld::DefinedAtom::typeResolver) desc |= N_SYMBOL_RESOLVER; if (atom->contentType() == lld::DefinedAtom::typeMachHeader) desc |= REFERENCED_DYNAMICALLY; if (_archHandler.isThumbFunction(*atom)) desc |= N_ARM_THUMB_DEF; if (atom->deadStrip() == DefinedAtom::deadStripNever && _ctx.outputMachOType() == llvm::MachO::MH_OBJECT) { if ((atom->contentType() != DefinedAtom::typeInitializerPtr) && (atom->contentType() != DefinedAtom::typeTerminatorPtr)) desc |= N_NO_DEAD_STRIP; } return desc; } bool Util::AtomSorter::operator()(const AtomAndIndex &left, const AtomAndIndex &right) { return (left.atom->name().compare(right.atom->name()) < 0); } llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom, bool &inGlobalsRegion, SymbolScope &scope) { bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); switch (atom->scope()) { case Atom::scopeTranslationUnit: scope = 0; inGlobalsRegion = false; return llvm::Error::success(); case Atom::scopeLinkageUnit: if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) && _ctx.exportSymbolNamed(atom->name())) { return llvm::make_error( Twine("cannot export hidden symbol ") + atom->name()); } if (rMode) { if (_ctx.keepPrivateExterns()) { // -keep_private_externs means keep in globals region as N_PEXT. scope = N_PEXT | N_EXT; inGlobalsRegion = true; return llvm::Error::success(); } } // scopeLinkageUnit symbols are no longer global once linked. scope = N_PEXT; inGlobalsRegion = false; return llvm::Error::success(); case Atom::scopeGlobal: if (_ctx.exportRestrictMode()) { if (_ctx.exportSymbolNamed(atom->name())) { scope = N_EXT; inGlobalsRegion = true; return llvm::Error::success(); } else { scope = N_PEXT; inGlobalsRegion = false; return llvm::Error::success(); } } else { scope = N_EXT; inGlobalsRegion = true; return llvm::Error::success(); } break; } llvm_unreachable("atom->scope() unknown enum value"); } llvm::Error Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); // Mach-O symbol table has four regions: stabs, locals, globals, undefs. // Add all stabs. for (auto &stab : _stabs) { Symbol sym; sym.type = static_cast(stab.type); sym.scope = 0; sym.sect = stab.other; sym.desc = stab.desc; if (stab.atom) sym.value = _atomToAddress[stab.atom]; else sym.value = stab.value; sym.name = stab.str; file.stabsSymbols.push_back(sym); } // Add all local (non-global) symbols in address order std::vector globals; globals.reserve(512); for (SectionInfo *sect : _sectionInfos) { for (const AtomInfo &info : sect->atomsAndOffsets) { const DefinedAtom *atom = info.atom; if (!atom->name().empty()) { SymbolScope symbolScope; bool inGlobalsRegion; if (auto ec = getSymbolTableRegion(atom, inGlobalsRegion, symbolScope)){ return ec; } if (inGlobalsRegion) { AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope }; globals.push_back(ai); } else { Symbol sym; sym.name = atom->name(); sym.type = N_SECT; sym.scope = symbolScope; sym.sect = sect->finalSectionIndex; sym.desc = descBits(atom); sym.value = _atomToAddress[atom]; _atomToSymbolIndex[atom] = file.localSymbols.size(); file.localSymbols.push_back(sym); } } else if (rMode && _archHandler.needsLocalSymbolInRelocatableFile(atom)){ // Create 'Lxxx' labels for anonymous atoms if archHandler says so. static unsigned tempNum = 1; char tmpName[16]; sprintf(tmpName, "L%04u", tempNum++); StringRef tempRef(tmpName); Symbol sym; sym.name = tempRef.copy(file.ownedAllocations); sym.type = N_SECT; sym.scope = 0; sym.sect = sect->finalSectionIndex; sym.desc = 0; sym.value = _atomToAddress[atom]; _atomToSymbolIndex[atom] = file.localSymbols.size(); file.localSymbols.push_back(sym); } } } // Sort global symbol alphabetically, then add to symbol table. std::sort(globals.begin(), globals.end(), AtomSorter()); const uint32_t globalStartIndex = file.localSymbols.size(); for (AtomAndIndex &ai : globals) { Symbol sym; sym.name = ai.atom->name(); sym.type = N_SECT; sym.scope = ai.scope; sym.sect = ai.index; sym.desc = descBits(static_cast(ai.atom)); sym.value = _atomToAddress[ai.atom]; _atomToSymbolIndex[ai.atom] = globalStartIndex + file.globalSymbols.size(); file.globalSymbols.push_back(sym); } // Sort undefined symbol alphabetically, then add to symbol table. std::vector undefs; undefs.reserve(128); for (const UndefinedAtom *atom : atomFile.undefined()) { AtomAndIndex ai = { atom, 0, N_EXT }; undefs.push_back(ai); } for (const SharedLibraryAtom *atom : atomFile.sharedLibrary()) { AtomAndIndex ai = { atom, 0, N_EXT }; undefs.push_back(ai); } std::sort(undefs.begin(), undefs.end(), AtomSorter()); const uint32_t start = file.globalSymbols.size() + file.localSymbols.size(); for (AtomAndIndex &ai : undefs) { Symbol sym; uint16_t desc = 0; if (!rMode) { uint8_t ordinal = 0; if (!_ctx.useFlatNamespace()) ordinal = dylibOrdinal(dyn_cast(ai.atom)); llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal); } sym.name = ai.atom->name(); sym.type = N_UNDF; sym.scope = ai.scope; sym.sect = 0; sym.desc = desc; sym.value = 0; _atomToSymbolIndex[ai.atom] = file.undefinedSymbols.size() + start; file.undefinedSymbols.push_back(sym); } return llvm::Error::success(); } const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) { for (const Reference *ref : *lpAtom) { if (_archHandler.isLazyPointer(*ref)) { return ref->target(); } } return nullptr; } const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) { for (const Reference *ref : *stubAtom) { if (const Atom *ta = ref->target()) { if (const DefinedAtom *lpAtom = dyn_cast(ta)) { const Atom *target = targetOfLazyPointer(lpAtom); if (target) return target; } } } return nullptr; } void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) { for (SectionInfo *si : _sectionInfos) { Section &normSect = file.sections[si->normalizedSectionIndex]; switch (si->type) { case llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS: for (const AtomInfo &info : si->atomsAndOffsets) { bool foundTarget = false; for (const Reference *ref : *info.atom) { const Atom *target = ref->target(); if (target) { if (isa(target)) { uint32_t index = _atomToSymbolIndex[target]; normSect.indirectSymbols.push_back(index); foundTarget = true; } else { normSect.indirectSymbols.push_back( llvm::MachO::INDIRECT_SYMBOL_LOCAL); } } } if (!foundTarget) { normSect.indirectSymbols.push_back( llvm::MachO::INDIRECT_SYMBOL_ABS); } } break; case llvm::MachO::S_LAZY_SYMBOL_POINTERS: for (const AtomInfo &info : si->atomsAndOffsets) { const Atom *target = targetOfLazyPointer(info.atom); if (target) { uint32_t index = _atomToSymbolIndex[target]; normSect.indirectSymbols.push_back(index); } } break; case llvm::MachO::S_SYMBOL_STUBS: for (const AtomInfo &info : si->atomsAndOffsets) { const Atom *target = targetOfStub(info.atom); if (target) { uint32_t index = _atomToSymbolIndex[target]; normSect.indirectSymbols.push_back(index); } } break; default: break; } } } void Util::addDependentDylibs(const lld::File &atomFile, NormalizedFile &nFile) { // Scan all imported symbols and build up list of dylibs they are from. int ordinal = 1; for (const auto *dylib : _ctx.allDylibs()) { DylibPathToInfo::iterator pos = _dylibInfo.find(dylib->installName()); if (pos == _dylibInfo.end()) { DylibInfo info; bool flatNamespaceAtom = dylib == _ctx.flatNamespaceFile(); // If we're in -flat_namespace mode (or this atom came from the flat // namespace file under -undefined dynamic_lookup) then use the flat // lookup ordinal. if (flatNamespaceAtom || _ctx.useFlatNamespace()) info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; else info.ordinal = ordinal++; info.hasWeak = false; info.hasNonWeak = !info.hasWeak; _dylibInfo[dylib->installName()] = info; // Unless this was a flat_namespace atom, record the source dylib. if (!flatNamespaceAtom) { DependentDylib depInfo; depInfo.path = dylib->installName(); depInfo.kind = llvm::MachO::LC_LOAD_DYLIB; depInfo.currentVersion = _ctx.dylibCurrentVersion(dylib->path()); depInfo.compatVersion = _ctx.dylibCompatVersion(dylib->path()); nFile.dependentDylibs.push_back(depInfo); } } else { pos->second.hasWeak = false; pos->second.hasNonWeak = !pos->second.hasWeak; } } // Automatically weak link dylib in which all symbols are weak (canBeNull). for (DependentDylib &dep : nFile.dependentDylibs) { DylibInfo &info = _dylibInfo[dep.path]; if (info.hasWeak && !info.hasNonWeak) dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB; else if (_ctx.isUpwardDylib(dep.path)) dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB; } } int Util::dylibOrdinal(const SharedLibraryAtom *sa) { return _dylibInfo[sa->loadName()].ordinal; } void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, uint64_t &segmentStartAddr) { segmentIndex = 0; for (const SegmentInfo *seg : _segmentInfos) { if ((seg->address <= sect->address) && (seg->address+seg->size >= sect->address+sect->size)) { segmentStartAddr = seg->address; return; } ++segmentIndex; } llvm_unreachable("section not in any segment"); } uint32_t Util::sectionIndexForAtom(const Atom *atom) { uint64_t address = _atomToAddress[atom]; for (const SectionInfo *si : _sectionInfos) { if ((si->address <= address) && (address < si->address+si->size)) return si->finalSectionIndex; } llvm_unreachable("atom not in any section"); } void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) return; // Utility function for ArchHandler to find symbol index for an atom. auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t { auto pos = _atomToSymbolIndex.find(&atom); assert(pos != _atomToSymbolIndex.end()); return pos->second; }; // Utility function for ArchHandler to find section index for an atom. auto sectIndexForAtom = [&] (const Atom &atom) -> uint32_t { return sectionIndexForAtom(&atom); }; // Utility function for ArchHandler to find address of atom in output file. auto addressForAtom = [&] (const Atom &atom) -> uint64_t { auto pos = _atomToAddress.find(&atom); assert(pos != _atomToAddress.end()); return pos->second; }; for (SectionInfo *si : _sectionInfos) { Section &normSect = file.sections[si->normalizedSectionIndex]; for (const AtomInfo &info : si->atomsAndOffsets) { const DefinedAtom *atom = info.atom; for (const Reference *ref : *atom) { // Skip emitting relocs for sections which are always able to be // implicitly regenerated and where the relocation targets an address // which is defined. if (si->relocsToDefinedCanBeImplicit && isa(ref->target())) continue; _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref, symIndexForAtom, sectIndexForAtom, addressForAtom, normSect.relocations); } } } } void Util::addFunctionStarts(const lld::File &, NormalizedFile &file) { if (!_ctx.generateFunctionStartsLoadCommand()) return; file.functionStarts.reserve(8192); // Delta compress function starts, starting with the mach header symbol. const uint64_t badAddress = ~0ULL; uint64_t addr = badAddress; for (SectionInfo *si : _sectionInfos) { for (const AtomInfo &info : si->atomsAndOffsets) { auto type = info.atom->contentType(); if (type == DefinedAtom::typeMachHeader) { addr = _atomToAddress[info.atom]; continue; } if (type != DefinedAtom::typeCode) continue; assert(addr != badAddress && "Missing mach header symbol"); // Skip atoms which have 0 size. This is so that LC_FUNCTION_STARTS // can't spill in to the next section. if (!info.atom->size()) continue; uint64_t nextAddr = _atomToAddress[info.atom]; if (_archHandler.isThumbFunction(*info.atom)) nextAddr |= 1; uint64_t delta = nextAddr - addr; if (delta) { ByteBuffer buffer; buffer.append_uleb128(delta); file.functionStarts.insert(file.functionStarts.end(), buffer.bytes(), buffer.bytes() + buffer.size()); } addr = nextAddr; } } // Null terminate, and pad to pointer size for this arch. file.functionStarts.push_back(0); auto size = file.functionStarts.size(); for (unsigned i = size, e = llvm::alignTo(size, _ctx.is64Bit() ? 8 : 4); i != e; ++i) file.functionStarts.push_back(0); } void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) { if (!_ctx.generateDataInCodeLoadCommand()) return; for (SectionInfo *si : _sectionInfos) { for (const AtomInfo &info : si->atomsAndOffsets) { // Atoms that contain data-in-code have "transition" references // which mark a point where the embedded data starts of ends. // This needs to be converted to the mach-o format which is an array // of data-in-code ranges. uint32_t startOffset = 0; DataRegionType mode = DataRegionType(0); for (const Reference *ref : *info.atom) { if (ref->kindNamespace() != Reference::KindNamespace::mach_o) continue; if (_archHandler.isDataInCodeTransition(ref->kindValue())) { DataRegionType nextMode = (DataRegionType)ref->addend(); if (mode != nextMode) { if (mode != 0) { // Found end data range, so make range entry. DataInCode entry; entry.offset = si->address + info.offsetInSection + startOffset; entry.length = ref->offsetInAtom() - startOffset; entry.kind = mode; file.dataInCode.push_back(entry); } } mode = nextMode; startOffset = ref->offsetInAtom(); } } if (mode != 0) { // Function ends with data (no end transition). DataInCode entry; entry.offset = si->address + info.offsetInSection + startOffset; entry.length = info.atom->size() - startOffset; entry.kind = mode; file.dataInCode.push_back(entry); } } } } void Util::addRebaseAndBindingInfo(const lld::File &atomFile, NormalizedFile &nFile) { if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) return; uint8_t segmentIndex; uint64_t segmentStartAddr; + uint32_t offsetInBindInfo = 0; + for (SectionInfo *sect : _sectionInfos) { segIndexForSection(sect, segmentIndex, segmentStartAddr); for (const AtomInfo &info : sect->atomsAndOffsets) { const DefinedAtom *atom = info.atom; for (const Reference *ref : *atom) { uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom() - segmentStartAddr; const Atom* targ = ref->target(); if (_archHandler.isPointer(*ref)) { // A pointer to a DefinedAtom requires rebasing. if (isa(targ)) { RebaseLocation rebase; rebase.segIndex = segmentIndex; rebase.segOffset = segmentOffset; rebase.kind = llvm::MachO::REBASE_TYPE_POINTER; nFile.rebasingInfo.push_back(rebase); } // A pointer to an SharedLibraryAtom requires binding. if (const SharedLibraryAtom *sa = dyn_cast(targ)) { BindLocation bind; bind.segIndex = segmentIndex; bind.segOffset = segmentOffset; bind.kind = llvm::MachO::BIND_TYPE_POINTER; bind.canBeNull = sa->canBeNullAtRuntime(); bind.ordinal = dylibOrdinal(sa); bind.symbolName = targ->name(); bind.addend = ref->addend(); nFile.bindingInfo.push_back(bind); } } else if (_archHandler.isLazyPointer(*ref)) { BindLocation bind; if (const SharedLibraryAtom *sa = dyn_cast(targ)) { bind.ordinal = dylibOrdinal(sa); } else { bind.ordinal = llvm::MachO::BIND_SPECIAL_DYLIB_SELF; } bind.segIndex = segmentIndex; bind.segOffset = segmentOffset; bind.kind = llvm::MachO::BIND_TYPE_POINTER; bind.canBeNull = false; //sa->canBeNullAtRuntime(); bind.symbolName = targ->name(); bind.addend = ref->addend(); nFile.lazyBindingInfo.push_back(bind); + + // Now that we know the segmentOffset and the ordinal attribute, + // we can fix the helper's code + + fixLazyReferenceImm(atom, offsetInBindInfo, nFile); + + // 5 bytes for opcodes + variable sizes (target name + \0 and offset + // encode's size) + offsetInBindInfo += + 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset); + if (bind.ordinal > BIND_IMMEDIATE_MASK) + offsetInBindInfo += llvm::getULEB128Size(bind.ordinal); + } + } + } + } +} + +void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, + NormalizedFile &file) { + for (const auto &ref : *atom) { + const DefinedAtom *da = dyn_cast(ref->target()); + if (da == nullptr) + return; + + const Reference *helperRef = nullptr; + for (const Reference *hr : *da) { + if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) { + helperRef = hr; + break; + } + } + if (helperRef == nullptr) + continue; + + // TODO: maybe get the fixed atom content from _archHandler ? + for (SectionInfo *sectInfo : _sectionInfos) { + for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) { + if (atomInfo.atom == helperRef->target()) { + auto sectionContent = + file.sections[sectInfo->normalizedSectionIndex].content; + uint8_t *rawb = + file.ownedAllocations.Allocate(sectionContent.size()); + llvm::MutableArrayRef newContent{rawb, + sectionContent.size()}; + std::copy(sectionContent.begin(), sectionContent.end(), + newContent.begin()); + llvm::support::ulittle32_t *loc = + reinterpret_cast( + &newContent[atomInfo.offsetInSection + + helperRef->offsetInAtom()]); + *loc = offset; + file.sections[sectInfo->normalizedSectionIndex].content = newContent; } } } } } void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) return; for (SectionInfo *sect : _sectionInfos) { for (const AtomInfo &info : sect->atomsAndOffsets) { const DefinedAtom *atom = info.atom; if (atom->scope() != Atom::scopeGlobal) continue; if (_ctx.exportRestrictMode()) { if (!_ctx.exportSymbolNamed(atom->name())) continue; } Export exprt; exprt.name = atom->name(); exprt.offset = _atomToAddress[atom] - _ctx.baseAddress(); exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR; if (atom->merge() == DefinedAtom::mergeAsWeak) exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; else exprt.flags = 0; exprt.otherOffset = 0; exprt.otherName = StringRef(); nFile.exportInfo.push_back(exprt); } } } uint32_t Util::fileFlags() { // FIXME: these need to determined at runtime. if (_ctx.outputMachOType() == MH_OBJECT) { return _subsectionsViaSymbols ? MH_SUBSECTIONS_VIA_SYMBOLS : 0; } else { uint32_t flags = MH_DYLDLINK; if (!_ctx.useFlatNamespace()) flags |= MH_TWOLEVEL | MH_NOUNDEFS; if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE()) flags |= MH_PIE; if (_hasTLVDescriptors) flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS); return flags; } } } // end anonymous namespace namespace lld { namespace mach_o { namespace normalized { /// Convert a set of Atoms into a normalized mach-o file. llvm::Expected> normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &context) { // The util object buffers info until the normalized file can be made. Util util(context); util.processDefinedAtoms(atomFile); util.organizeSections(); std::unique_ptr f(new NormalizedFile()); NormalizedFile &normFile = *f.get(); normFile.arch = context.arch(); normFile.fileType = context.outputMachOType(); normFile.flags = util.fileFlags(); normFile.stackSize = context.stackSize(); normFile.installName = context.installName(); normFile.currentVersion = context.currentVersion(); normFile.compatVersion = context.compatibilityVersion(); normFile.os = context.os(); // If we are emitting an object file, then the min version is the maximum // of the min's of all the source files and the cmdline. if (normFile.fileType == llvm::MachO::MH_OBJECT) normFile.minOSverson = std::max(context.osMinVersion(), util.minVersion()); else normFile.minOSverson = context.osMinVersion(); normFile.minOSVersionKind = util.minVersionCommandType(); normFile.sdkVersion = context.sdkVersion(); normFile.sourceVersion = context.sourceVersion(); if (context.generateVersionLoadCommand() && context.os() != MachOLinkingContext::OS::unknown) normFile.hasMinVersionLoadCommand = true; else if (normFile.fileType == llvm::MachO::MH_OBJECT && util.allSourceFilesHaveMinVersions() && ((normFile.os != MachOLinkingContext::OS::unknown) || util.minVersionCommandType())) { // If we emit an object file, then it should contain a min version load // command if all of the source files also contained min version commands. // Also, we either need to have a platform, or found a platform from the // source object files. normFile.hasMinVersionLoadCommand = true; } normFile.generateDataInCodeLoadCommand = context.generateDataInCodeLoadCommand(); normFile.pageSize = context.pageSize(); normFile.rpaths = context.rpaths(); util.addDependentDylibs(atomFile, normFile); util.copySegmentInfo(normFile); util.copySectionInfo(normFile); util.assignAddressesToSections(normFile); util.buildAtomToAddressMap(); if (auto err = util.synthesizeDebugNotes(normFile)) return std::move(err); util.updateSectionInfo(normFile); util.copySectionContent(normFile); if (auto ec = util.addSymbols(atomFile, normFile)) { return std::move(ec); } util.addIndirectSymbols(atomFile, normFile); util.addRebaseAndBindingInfo(atomFile, normFile); util.addExportInfo(atomFile, normFile); util.addSectionRelocs(atomFile, normFile); util.addFunctionStarts(atomFile, normFile); util.buildDataInCodeArray(atomFile, normFile); util.copyEntryPointAddress(normFile); return std::move(f); } } // namespace normalized } // namespace mach_o } // namespace lld Index: vendor/lld/dist/test/mach-o/lazy-bind-x86_64.yaml =================================================================== --- vendor/lld/dist/test/mach-o/lazy-bind-x86_64.yaml (revision 326904) +++ vendor/lld/dist/test/mach-o/lazy-bind-x86_64.yaml (revision 326905) @@ -1,111 +1,111 @@ # REQUIRES: x86 # RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \ # RUN: %p/Inputs/lazy-bind-x86_64.yaml %p/Inputs/lazy-bind-x86_64-2.yaml \ # RUN: %p/Inputs/lazy-bind-x86_64-3.yaml -o %t \ # RUN: %p/Inputs/x86_64/libSystem.yaml # RUN: llvm-objdump -lazy-bind %t | FileCheck %s # RUN: llvm-nm -m %t | FileCheck --check-prefix=CHECK-NM %s # RUN: llvm-objdump -disassemble %t | FileCheck --check-prefix=CHECK-HELPERS %s # RUN: llvm-objdump -private-headers %t | FileCheck --check-prefix=CHECK-DYLIBS %s # # Test that correct two-level namespace ordinals are used for lazy bindings. # --- !mach-o arch: x86_64 file-type: MH_OBJECT flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] sections: - segment: __TEXT section: __text type: S_REGULAR attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] address: 0x0000000000000000 content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0x5D, 0xC3 ] relocations: - offset: 0x00000015 type: X86_64_RELOC_BRANCH length: 2 pc-rel: true extern: true symbol: 3 - offset: 0x0000000E type: X86_64_RELOC_BRANCH length: 2 pc-rel: true extern: true symbol: 2 - offset: 0x00000007 type: X86_64_RELOC_BRANCH length: 2 pc-rel: true extern: true symbol: 1 global-symbols: - name: _main type: N_SECT scope: [ N_EXT ] sect: 1 value: 0x0000000000000000 undefined-symbols: - name: _bar type: N_UNDF scope: [ N_EXT ] value: 0x0000000000000000 - name: _baz type: N_UNDF scope: [ N_EXT ] value: 0x0000000000000000 - name: _foo type: N_UNDF scope: [ N_EXT ] value: 0x0000000000000000 ... # CHECK: libbar _bar # CHECK: libbaz _baz # CHECK: libfoo _foo # CHECK-NM: (undefined) external _bar (from libbar) # CHECK-NM: (undefined) external _baz (from libbaz) # CHECK-NM: (undefined) external _foo (from libfoo) # CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper: # CHECK-HELPERS: 68 00 00 00 00 pushq $0 -# CHECK-HELPERS: 68 10 00 00 00 pushq $16 -# CHECK-HELPERS: 68 20 00 00 00 pushq $32 +# CHECK-HELPERS: 68 0b 00 00 00 pushq $11 +# CHECK-HELPERS: 68 16 00 00 00 pushq $22 # Make sure the stub helper is correctly aligned # CHECK-DYLIBS: sectname __stub_helper # CHECK-DYLIBS-NEXT: segname __TEXT # CHECK-DYLIBS-NEXT: addr # CHECK-DYLIBS-NEXT: size # CHECK-DYLIBS-NEXT: offset # CHECK-DYLIBS-NEXT: align 2^2 (4) # Make sure the __nl_symbol_ptr section is used instea of __got as this is x86_64 # CHECK-DYLIBS: sectname __nl_symbol_ptr # CHECK-DYLIBS-NEXT: segname __DATA # CHECK-DYLIBS: cmd LC_LOAD_DYLIB # CHECK-DYLIBS: name /usr/lib/libbar.dylib (offset 24) # CHECK-DYLIBS: current version 2.3.0 # CHECK-DYLIBS: compatibility version 1.0.0 # CHECK-DYLIBS: cmd LC_LOAD_DYLIB # CHECK-DYLIBS: name /usr/lib/libfoo.dylib (offset 24) # CHECK-DYLIBS: current version 3.4.0 # CHECK-DYLIBS: compatibility version 2.0.0 # CHECK-DYLIBS: cmd LC_LOAD_DYLIB # CHECK-DYLIBS: name /usr/lib/libbaz.dylib (offset 24) # CHECK-DYLIBS: current version 4.5.0 # CHECK-DYLIBS: compatibility version 3.0.0