From 2d432353ba7ae9ab1ab8826037ed31d2627d6b1d Mon Sep 17 00:00:00 2001 From: Nick Kledzik Date: Thu, 17 Jul 2014 23:16:21 +0000 Subject: [PATCH] [mach-o] implement more x86 and x86_64 relocation support Add support for adding section relocations in -r mode. Enhance the test cases which validate the parsing of .o files to also round trip. They now write out the .o file and then parse that, verifying all relocations survived the round trip. llvm-svn: 213333 --- lld/lib/ReaderWriter/MachO/ArchHandler.cpp | 38 ++++ lld/lib/ReaderWriter/MachO/ArchHandler.h | 44 +++- lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp | 88 ++++++-- lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp | 209 +++++++++++++++--- lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp | 234 ++++++++++++++++++--- .../MachO/MachONormalizedFileFromAtoms.cpp | 101 ++++++--- lld/test/mach-o/parse-data-relocs-x86_64.yaml | 10 +- lld/test/mach-o/parse-relocs-x86.yaml | 11 +- lld/test/mach-o/parse-text-relocs-x86_64.yaml | 10 +- 9 files changed, 638 insertions(+), 107 deletions(-) diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler.cpp index 8dfafc6..f5fdb1b 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler.cpp +++ b/lld/lib/ReaderWriter/MachO/ArchHandler.cpp @@ -86,6 +86,44 @@ ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) { return result; } +normalized::Relocation +ArchHandler::relocFromPattern(ArchHandler::RelocPattern pattern) { + normalized::Relocation result; + result.offset = 0; + result.scattered = (pattern & rScattered); + result.type = (RelocationInfoType)(pattern & 0xF); + result.pcRel = (pattern & rPcRel); + result.isExtern = (pattern & rExtern); + result.value = 0; + result.symbol = 0; + switch (pattern & 0x300) { + case rLength1: + result.length = 0; + break; + case rLength2: + result.length = 1; + break; + case rLength4: + result.length = 2; + break; + case rLength8: + result.length = 3; + break; + } + return result; +} + +void ArchHandler::appendReloc(normalized::Relocations &relocs, uint32_t offset, + uint32_t symbol, uint32_t value, + RelocPattern pattern) { + normalized::Relocation reloc = relocFromPattern(pattern); + reloc.offset = offset; + reloc.symbol = symbol; + reloc.value = value; + relocs.push_back(reloc); +} + + int16_t ArchHandler::readS16(bool swap, const uint8_t *addr) { return read16(swap, *reinterpret_cast(addr)); } diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler.h b/lld/lib/ReaderWriter/MachO/ArchHandler.h index 8a097a0..cf479c8 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler.h +++ b/lld/lib/ReaderWriter/MachO/ArchHandler.h @@ -105,11 +105,37 @@ public: const lld::Atom **target, Reference::Addend *addend) = 0; - /// Fixup an atom when generating a final linked binary. - virtual void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch, - Reference::KindValue kindValue, uint64_t addend, - uint8_t *location, uint64_t fixupAddress, - uint64_t targetAddress, uint64_t inAtomAddress) = 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, + uint8_t *atomContentBuffer) = 0; + + /// Used in -r mode to convert a Reference to a mach-o relocation. + virtual void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom, + FindSectionIndexForAtom, + FindAddressForAtom, + normalized::Relocations&) = 0; + struct ReferenceInfo { Reference::KindArch arch; @@ -161,7 +187,15 @@ protected: rLength4 = 0x0200, rLength8 = 0x0300 }; + /// 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(bool swap, const uint8_t *addr); static int32_t readS32(bool swap, const uint8_t *addr); diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp index a1e6b83..1608858 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ b/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -57,11 +57,18 @@ public: const lld::Atom **target, Reference::Addend *addend) override; - void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch, - Reference::KindValue kindValue, uint64_t addend, - uint8_t *location, uint64_t fixupAddress, - uint64_t targetAddress, uint64_t inAtomAddress) - override; + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom, + FindSectionIndexForAtom, + FindAddressForAtom, + normalized::Relocations &) override; + private: static const Registry::KindStrings _sKindStrings[]; @@ -96,6 +103,14 @@ private: uint32_t clearThumbBit(uint32_t value, const Atom *target); uint32_t setDisplacementInArmBranch(uint32_t instruction, int32_t disp); + 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); const bool _swap; }; @@ -594,19 +609,17 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, return std::error_code(); } -void ArchHandler_arm::applyFixup(Reference::KindNamespace ns, - Reference::KindArch arch, - Reference::KindValue kindValue, - uint64_t addend, uint8_t *location, - uint64_t fixupAddress, uint64_t targetAddress, - uint64_t inAtomAddress) { - if (ns != Reference::KindNamespace::mach_o) +void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; - assert(arch == Reference::KindArch::ARM); + assert(ref.kindArch() == Reference::KindArch::ARM); int32_t *loc32 = reinterpret_cast(location); int32_t displacement; // FIXME: these writes may need a swap. - switch (kindValue) { + switch (ref.kindValue()) { case thumb_b22: // FIXME break; @@ -623,7 +636,7 @@ void ArchHandler_arm::applyFixup(Reference::KindNamespace ns, // FIXME break; case arm_b24: - displacement = (targetAddress - (fixupAddress + 8)) + addend; + displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); *loc32 = setDisplacementInArmBranch(*loc32, displacement); break; case arm_movw: @@ -654,6 +667,51 @@ void ArchHandler_arm::applyFixup(Reference::KindNamespace ns, } } +void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, + bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // 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_arm::applyFixupRelocatable(const Reference &ref, + uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + // FIXME: to do +} + +void ArchHandler_arm::appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom, + FindSectionIndexForAtom, + FindAddressForAtom, + normalized::Relocations &) { + // FIXME: to do +} + std::unique_ptr ArchHandler::create_arm() { return std::unique_ptr(new ArchHandler_arm()); } diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp index db7d036..4cb043e 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ b/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -57,11 +57,17 @@ public: const lld::Atom **target, Reference::Addend *addend) override; - void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch, - Reference::KindValue kindValue, uint64_t addend, - uint8_t *location, uint64_t fixupAddress, - uint64_t targetAddress, uint64_t inAtomAddress) - override; + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) override; private: static const Registry::KindStrings _sKindStrings[]; @@ -83,6 +89,17 @@ private: 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); + const bool _swap; }; @@ -246,12 +263,10 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, 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) - : + 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 = readS32(swap, fixupContent); @@ -285,33 +300,58 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, } } -void ArchHandler_x86::applyFixup(Reference::KindNamespace ns, - Reference::KindArch arch, - Reference::KindValue kindValue, - uint64_t addend, uint8_t *location, - uint64_t fixupAddress, uint64_t targetAddress, - uint64_t inAtomAddress) { - if (ns != Reference::KindNamespace::mach_o) +void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom, + bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // 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 *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; - assert(arch == Reference::KindArch::x86); + assert(ref.kindArch() == Reference::KindArch::x86); int32_t *loc32 = reinterpret_cast(location); int16_t *loc16 = reinterpret_cast(location); - switch (kindValue) { + switch (ref.kindValue()) { case branch32: - write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + ref.addend()); break; case branch16: - write16(*loc16, _swap, (targetAddress - (fixupAddress + 4)) + addend); + write16(*loc16, _swap, (targetAddress - (fixupAddress + 2)) + ref.addend()); break; case pointer32: case abs32: - write32(*loc32, _swap, targetAddress + addend); + write32(*loc32, _swap, targetAddress + ref.addend()); break; case funcRel32: - write32(*loc32, _swap, targetAddress - inAtomAddress + addend); // FIXME + write32(*loc32, _swap, targetAddress - inAtomAddress + ref.addend()); break; case delta32: - write32(*loc32, _swap, targetAddress - fixupAddress + addend); + write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend()); break; case lazyPointer: case lazyImmediateLocation: @@ -323,6 +363,125 @@ void ArchHandler_x86::applyFixup(Reference::KindNamespace ns, } } +void ArchHandler_x86::applyFixupRelocatable(const Reference &ref, + uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + int32_t *loc32 = reinterpret_cast(location); + int16_t *loc16 = reinterpret_cast(location); + switch (ref.kindValue()) { + case branch32: + write32(*loc32, _swap, ref.addend() - (fixupAddress + 4)); + break; + case branch16: + write16(*loc16, _swap, ref.addend() - (fixupAddress + 2)); + break; + case pointer32: + case abs32: + write32(*loc32, _swap, targetAddress + ref.addend()); + break; + case funcRel32: + write32(*loc32, _swap, targetAddress - inAtomAddress + ref.addend()); // FIXME + break; + case delta32: + write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend()); + break; + case lazyPointer: + case lazyImmediateLocation: + // do nothing + break; + default: + 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 (ref.kindValue()) { + case branch32: + if (useExternalReloc) + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + GENERIC_RELOC_VANILLA | rPcRel | rExtern | 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 | rPcRel | rExtern | 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 + 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 lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + break; + default: + llvm_unreachable("unknown x86 Reference Kind"); + break; + + } +} + + std::unique_ptr ArchHandler::create_x86() { return std::unique_ptr(new ArchHandler_x86()); } diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index 5f1cddb..4fc8000 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -84,12 +84,22 @@ public: Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; - - virtual void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch, - Reference::KindValue kindValue, uint64_t addend, - uint8_t *location, uint64_t fixupAddress, - uint64_t targetAddress, uint64_t inAtomAddress) - override; + + virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) { + return (atom->contentType() == DefinedAtom::typeCString); + } + + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) override; private: static const Registry::KindStrings _sKindStrings[]; @@ -126,6 +136,15 @@ private: Reference::KindValue kindFromRelocPair(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2); + void applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress); + + void applyFixupRelocatable(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress); + const bool _swap; }; @@ -357,51 +376,76 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, } } -void ArchHandler_x86_64::applyFixup(Reference::KindNamespace ns, - Reference::KindArch arch, - Reference::KindValue kindValue, - uint64_t addend, uint8_t *location, - uint64_t fixupAddress, - uint64_t targetAddress, - uint64_t inAtomAddress) { - if (ns != Reference::KindNamespace::mach_o) +void ArchHandler_x86_64::generateAtomContent(const DefinedAtom &atom, + bool relocatable, + FindAddressForAtom findAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // 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_64::applyFixupFinal(const Reference &ref, + uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; - assert(arch == Reference::KindArch::x86_64); + assert(ref.kindArch() == Reference::KindArch::x86_64); int32_t *loc32 = reinterpret_cast(location); uint64_t *loc64 = reinterpret_cast(location); - switch (kindValue) { + switch (ref.kindValue()) { case branch32: case ripRel32: case ripRel32Got: case ripRel32GotLoad: - write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + ref.addend()); break; case pointer64: case pointer64Anon: - write64(*loc64, _swap, targetAddress + addend); + write64(*loc64, _swap, targetAddress + ref.addend()); break; case ripRel32Minus1: - write32(*loc32, _swap, (targetAddress - (fixupAddress + 5)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 5)) + ref.addend()); break; case ripRel32Minus2: - write32(*loc32, _swap, (targetAddress - (fixupAddress + 6)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 6)) + ref.addend()); break; case ripRel32Minus4: - write32(*loc32, _swap, (targetAddress - (fixupAddress + 8)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 8)) + ref.addend()); break; case delta32: case delta32Anon: - write32(*loc32, _swap, (targetAddress - fixupAddress) + addend); + write32(*loc32, _swap, (targetAddress - fixupAddress) + ref.addend()); break; case delta64: case delta64Anon: - write64(*loc64, _swap, (targetAddress - fixupAddress) + addend); + write64(*loc64, _swap, (targetAddress - fixupAddress) + ref.addend()); break; case ripRel32GotLoadNowLea: // Change MOVQ to LEA assert(location[-2] == 0x8B); location[-2] = 0x8D; - write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend); + write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + ref.addend()); break; case lazyPointer: case lazyImmediateLocation: @@ -413,6 +457,148 @@ void ArchHandler_x86_64::applyFixup(Reference::KindNamespace ns, } } + +void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, + uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + int32_t *loc32 = reinterpret_cast(location); + uint64_t *loc64 = reinterpret_cast(location); + switch (ref.kindValue()) { + case branch32: + case ripRel32: + case ripRel32Got: + case ripRel32GotLoad: + write32(*loc32, _swap, ref.addend()); + break; + case pointer64: + write64(*loc64, _swap, ref.addend()); + break; + case pointer64Anon: + write64(*loc64, _swap, targetAddress + ref.addend()); + break; + case ripRel32Minus1: + write32(*loc32, _swap, ref.addend() - 1); + break; + case ripRel32Minus2: + write32(*loc32, _swap, ref.addend() - 2); + break; + case ripRel32Minus4: + write32(*loc32, _swap, ref.addend() - 4); + break; + case delta32: + write32(*loc32, _swap, ref.addend() + inAtomAddress - fixupAddress); + break; + case delta32Anon: + write32(*loc32, _swap, (targetAddress - fixupAddress) + ref.addend()); + break; + case delta64: + write64(*loc64, _swap, ref.addend() + inAtomAddress - fixupAddress); + break; + case delta64Anon: + write64(*loc64, _swap, (targetAddress - fixupAddress) + ref.addend()); + break; + case ripRel32GotLoadNowLea: + llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); + break; + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + break; + default: + llvm_unreachable("unknown x86_64 Reference Kind"); + break; + } +} + +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 (ref.kindValue()) { + case branch32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4); + break; + case ripRel32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 ); + break; + case ripRel32Got: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 ); + break; + case ripRel32GotLoad: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); + break; + case pointer64: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rExtern | rLength8); + break; + case pointer64Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rLength8); + break; + case ripRel32Minus1: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 ); + break; + case ripRel32Minus2: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 ); + break; + case ripRel32Minus4: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 ); + break; + 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 ); + break; + 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 ); + break; + 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 ); + break; + 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 ); + break; + case ripRel32GotLoadNowLea: + llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); + break; + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + break; + default: + llvm_unreachable("unknown x86_64 Reference Kind"); + break; + } +} + + std::unique_ptr ArchHandler::create_x86_64() { return std::unique_ptr(new ArchHandler_x86_64()); } diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index 0a30680..17dc470 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -88,7 +88,8 @@ SegmentInfo::SegmentInfo(StringRef n) class Util { public: - Util(const MachOLinkingContext &ctxt) : _context(ctxt), _entryAtom(nullptr) {} + Util(const MachOLinkingContext &ctxt) : _context(ctxt), + _archHandler(ctxt.archHandler()), _entryAtom(nullptr) {} void assignAtomsToSections(const lld::File &atomFile); void organizeSections(); @@ -128,8 +129,7 @@ private: const Atom *targetOfStub(const DefinedAtom *stubAtom); bool belongsInGlobalSymbolsSection(const DefinedAtom* atom); void appendSection(SectionInfo *si, NormalizedFile &file); - void appendReloc(const DefinedAtom *atom, const Reference *ref, - Relocations &relocations); + uint32_t sectionIndexForAtom(const Atom *atom); static uint64_t alignTo(uint64_t value, uint8_t align2); typedef llvm::DenseMap AtomToIndex; @@ -147,6 +147,7 @@ private: }; const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; llvm::BumpPtrAllocator _allocator; std::vector _sectionInfos; std::vector _segmentInfos; @@ -509,6 +510,14 @@ void Util::copySegmentInfo(NormalizedFile &file) { void Util::appendSection(SectionInfo *si, NormalizedFile &file) { const bool rMode = (_context.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; + }; + // Add new empty section to end of file.sections. Section temp; file.sections.push_back(std::move(temp)); @@ -528,28 +537,9 @@ void Util::appendSection(SectionInfo *si, NormalizedFile &file) { uint8_t *sectionContent = file.ownedAllocations.Allocate(si->size); normSect->content = llvm::makeArrayRef(sectionContent, si->size); for (AtomInfo &ai : si->atomsAndOffsets) { - // Copy raw bytes. uint8_t *atomContent = reinterpret_cast (§ionContent[ai.offsetInSection]); - memcpy(atomContent, ai.atom->rawContent().data(), ai.atom->size()); - // Apply fix-ups. - for (const Reference *ref : *ai.atom) { - uint32_t offset = ref->offsetInAtom(); - uint64_t targetAddress = 0; - if ( ref->target() != nullptr ) - targetAddress = _atomToAddress[ref->target()]; - uint64_t atomAddress = _atomToAddress[ai.atom]; - uint64_t fixupAddress = atomAddress + offset; - if ( rMode ) { - // FIXME: Need a handler method to update content for .o file - // output and any needed section relocations. - } else { - _context.archHandler().applyFixup( - ref->kindNamespace(), ref->kindArch(), ref->kindValue(), - ref->addend(), &atomContent[offset], fixupAddress, targetAddress, - atomAddress); - } - } + _archHandler.generateAtomContent(*ai.atom, rMode, addrForAtom, atomContent); } } @@ -647,6 +637,7 @@ bool Util::belongsInGlobalSymbolsSection(const DefinedAtom* atom) { } void Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { + bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); // Mach-O symbol table has three regions: locals, globals, undefs. // Add all local (non-global) symbols in address order @@ -667,14 +658,31 @@ void Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { sym.sect = sect->finalSectionIndex; sym.desc = 0; 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(); @@ -683,6 +691,7 @@ void Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { 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); } @@ -715,7 +724,7 @@ void Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) { for (const Reference *ref : *lpAtom) { - if (_context.archHandler().isLazyPointer(*ref)) { + if (_archHandler.isLazyPointer(*ref)) { return ref->target(); } } @@ -838,21 +847,51 @@ void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, } -void Util::appendReloc(const DefinedAtom *atom, const Reference *ref, - Relocations &relocations) { - // TODO: convert Reference to normalized relocation +uint32_t Util::sectionIndexForAtom(const Atom *atom) { + uint64_t address = _atomToAddress[atom]; + uint32_t index = 1; + for (const SectionInfo *si : _sectionInfos) { + if ((si->address <= address) && (address < si->address+si->size)) + return index; + ++index; + } + llvm_unreachable("atom not in any section"); } void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { if (_context.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) { - appendReloc(atom, ref, normSect.relocations); + _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref, + symIndexForAtom, + sectIndexForAtom, + addressForAtom, + normSect.relocations); } } } @@ -873,7 +912,7 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom() - segmentStartAddr; const Atom* targ = ref->target(); - if (_context.archHandler().isPointer(*ref)) { + if (_archHandler.isPointer(*ref)) { // A pointer to a DefinedAtom requires rebasing. if (dyn_cast(targ)) { RebaseLocation rebase; @@ -895,13 +934,13 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, nFile.bindingInfo.push_back(bind); } } - if (_context.archHandler().isLazyPointer(*ref)) { + if (_archHandler.isLazyPointer(*ref)) { BindLocation bind; bind.segIndex = segmentIndex; bind.segOffset = segmentOffset; bind.kind = llvm::MachO::BIND_TYPE_POINTER; bind.canBeNull = false; //sa->canBeNullAtRuntime(); - bind.ordinal = 1; + bind.ordinal = 1; // FIXME bind.symbolName = targ->name(); bind.addend = ref->addend(); nFile.lazyBindingInfo.push_back(bind); diff --git a/lld/test/mach-o/parse-data-relocs-x86_64.yaml b/lld/test/mach-o/parse-data-relocs-x86_64.yaml index ffc9e477..6ba2e48 100644 --- a/lld/test/mach-o/parse-data-relocs-x86_64.yaml +++ b/lld/test/mach-o/parse-data-relocs-x86_64.yaml @@ -1,6 +1,12 @@ -# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s \ +# RUN: && lld -flavor darwin -arch x86_64 %t -r -print_atoms -o %t2 | FileCheck %s # -# Test parsing of x86_64 data relocations. +# Test parsing and writing of x86_64 text relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. # #_foo: # ret diff --git a/lld/test/mach-o/parse-relocs-x86.yaml b/lld/test/mach-o/parse-relocs-x86.yaml index c9feb24..546a9e9 100644 --- a/lld/test/mach-o/parse-relocs-x86.yaml +++ b/lld/test/mach-o/parse-relocs-x86.yaml @@ -1,9 +1,14 @@ -# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch i386 -r -print_atoms %t -o %t2 | FileCheck %s # -# Test parsing of x86 relocations. +# Test parsing and writing of x86 relocations. # -# .text +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. # +# .text #_test: # call _undef # call _undef+2 diff --git a/lld/test/mach-o/parse-text-relocs-x86_64.yaml b/lld/test/mach-o/parse-text-relocs-x86_64.yaml index e3b9cf2..b2a3d5d 100644 --- a/lld/test/mach-o/parse-text-relocs-x86_64.yaml +++ b/lld/test/mach-o/parse-text-relocs-x86_64.yaml @@ -1,6 +1,12 @@ -# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s # -# Test parsing of x86_64 text relocations. +# Test parsing and writing of x86_64 text relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. # #_test: # call _foo -- 2.7.4