From 3e582c8855b44ef6d61d863a7db8ae5189c68682 Mon Sep 17 00:00:00 2001 From: Eugene Leviant Date: Mon, 6 Feb 2017 15:31:28 +0000 Subject: [PATCH] RuntimeDyldELF/AArch64: Implement basic GOT support This patch implements two GOT relocations: R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC Differential revision: https://reviews.llvm.org/D28571 llvm-svn: 294191 --- .../RuntimeDyld/RuntimeDyld.cpp | 26 +++ .../RuntimeDyld/RuntimeDyldELF.cpp | 187 ++++++++++++------ .../RuntimeDyld/RuntimeDyldELF.h | 21 +- .../RuntimeDyld/RuntimeDyldImpl.h | 13 +- .../AArch64/ELF_ARM64_PIC_relocations.s | 46 +++++ 5 files changed, 221 insertions(+), 72 deletions(-) create mode 100644 llvm/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_PIC_relocations.s diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index 63b56f725209..9660e1acad15 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -484,6 +484,14 @@ Error RuntimeDyldImpl::computeTotalAllocSize(const ObjectFile &Obj, } } + // Compute Global Offset Table size. If it is not zero we + // also update alignment, which is equal to a size of a + // single GOT entry. + if (unsigned GotSize = computeGOTSize(Obj)) { + RWSectionSizes.push_back(GotSize); + RWDataAlign = std::max(RWDataAlign, getGOTEntrySize()); + } + // Compute the size of all common symbols uint64_t CommonSize = 0; uint32_t CommonAlign = 1; @@ -518,6 +526,24 @@ Error RuntimeDyldImpl::computeTotalAllocSize(const ObjectFile &Obj, return Error::success(); } +// compute GOT size +unsigned RuntimeDyldImpl::computeGOTSize(const ObjectFile &Obj) { + size_t GotEntrySize = getGOTEntrySize(); + if (!GotEntrySize) + return 0; + + size_t GotSize = 0; + for (section_iterator SI = Obj.section_begin(), SE = Obj.section_end(); + SI != SE; ++SI) { + + for (const RelocationRef &Reloc : SI->relocations()) + if (relocationNeedsGot(Reloc)) + GotSize += GotEntrySize; + } + + return GotSize; +} + // compute stub buffer size for the given section unsigned RuntimeDyldImpl::computeSectionStubBufSize(const ObjectFile &Obj, const SectionRef &Section) { diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index 8e7437ac4ffb..f780137d0874 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -961,6 +961,61 @@ bool RuntimeDyldELF::resolveAArch64ShortBranch( return true; } +void RuntimeDyldELF::resolveAArch64Branch(unsigned SectionID, + const RelocationValueRef &Value, + relocation_iterator RelI, + StubMap &Stubs) { + + DEBUG(dbgs() << "\t\tThis is an AArch64 branch relocation."); + SectionEntry &Section = Sections[SectionID]; + + uint64_t Offset = RelI->getOffset(); + unsigned RelType = RelI->getType(); + // Look for an existing stub. + StubMap::const_iterator i = Stubs.find(Value); + if (i != Stubs.end()) { + resolveRelocation(Section, Offset, + (uint64_t)Section.getAddressWithOffset(i->second), + RelType, 0); + DEBUG(dbgs() << " Stub function found\n"); + } else if (!resolveAArch64ShortBranch(SectionID, RelI, Value)) { + // Create a new stub function. + DEBUG(dbgs() << " Create a new stub function\n"); + Stubs[Value] = Section.getStubOffset(); + uint8_t *StubTargetAddr = createStubFunction( + Section.getAddressWithOffset(Section.getStubOffset())); + + RelocationEntry REmovz_g3(SectionID, StubTargetAddr - Section.getAddress(), + ELF::R_AARCH64_MOVW_UABS_G3, Value.Addend); + RelocationEntry REmovk_g2(SectionID, + StubTargetAddr - Section.getAddress() + 4, + ELF::R_AARCH64_MOVW_UABS_G2_NC, Value.Addend); + RelocationEntry REmovk_g1(SectionID, + StubTargetAddr - Section.getAddress() + 8, + ELF::R_AARCH64_MOVW_UABS_G1_NC, Value.Addend); + RelocationEntry REmovk_g0(SectionID, + StubTargetAddr - Section.getAddress() + 12, + ELF::R_AARCH64_MOVW_UABS_G0_NC, Value.Addend); + + if (Value.SymbolName) { + addRelocationForSymbol(REmovz_g3, Value.SymbolName); + addRelocationForSymbol(REmovk_g2, Value.SymbolName); + addRelocationForSymbol(REmovk_g1, Value.SymbolName); + addRelocationForSymbol(REmovk_g0, Value.SymbolName); + } else { + addRelocationForSection(REmovz_g3, Value.SectionID); + addRelocationForSection(REmovk_g2, Value.SectionID); + addRelocationForSection(REmovk_g1, Value.SectionID); + addRelocationForSection(REmovk_g0, Value.SectionID); + } + resolveRelocation(Section, Offset, + reinterpret_cast(Section.getAddressWithOffset( + Section.getStubOffset())), + RelType, 0); + Section.advanceStubOffset(getMaxStubSize()); + } +} + Expected RuntimeDyldELF::processRelocationRef( unsigned SectionID, relocation_iterator RelI, const ObjectFile &O, @@ -1055,55 +1110,22 @@ RuntimeDyldELF::processRelocationRef( DEBUG(dbgs() << "\t\tSectionID: " << SectionID << " Offset: " << Offset << "\n"); - if ((Arch == Triple::aarch64 || Arch == Triple::aarch64_be) && - (RelType == ELF::R_AARCH64_CALL26 || RelType == ELF::R_AARCH64_JUMP26)) { - // This is an AArch64 branch relocation, need to use a stub function. - DEBUG(dbgs() << "\t\tThis is an AArch64 branch relocation."); - SectionEntry &Section = Sections[SectionID]; + if ((Arch == Triple::aarch64 || Arch == Triple::aarch64_be)) { + if (RelType == ELF::R_AARCH64_CALL26 || RelType == ELF::R_AARCH64_JUMP26) { + resolveAArch64Branch(SectionID, Value, RelI, Stubs); + } else if (RelType == ELF::R_AARCH64_ADR_GOT_PAGE) { + // Craete new GOT entry or find existing one. If GOT entry is + // to be created, then we also emit ABS64 relocation for it. + uint64_t GOTOffset = findOrAllocGOTEntry(Value, ELF::R_AARCH64_ABS64); + resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend, + ELF::R_AARCH64_ADR_PREL_PG_HI21); - // Look for an existing stub. - StubMap::const_iterator i = Stubs.find(Value); - if (i != Stubs.end()) { - resolveRelocation(Section, Offset, - (uint64_t)Section.getAddressWithOffset(i->second), - RelType, 0); - DEBUG(dbgs() << " Stub function found\n"); - } else if (!resolveAArch64ShortBranch(SectionID, RelI, Value)) { - // Create a new stub function. - DEBUG(dbgs() << " Create a new stub function\n"); - Stubs[Value] = Section.getStubOffset(); - uint8_t *StubTargetAddr = createStubFunction( - Section.getAddressWithOffset(Section.getStubOffset())); - - RelocationEntry REmovz_g3(SectionID, - StubTargetAddr - Section.getAddress(), - ELF::R_AARCH64_MOVW_UABS_G3, Value.Addend); - RelocationEntry REmovk_g2(SectionID, StubTargetAddr - - Section.getAddress() + 4, - ELF::R_AARCH64_MOVW_UABS_G2_NC, Value.Addend); - RelocationEntry REmovk_g1(SectionID, StubTargetAddr - - Section.getAddress() + 8, - ELF::R_AARCH64_MOVW_UABS_G1_NC, Value.Addend); - RelocationEntry REmovk_g0(SectionID, StubTargetAddr - - Section.getAddress() + 12, - ELF::R_AARCH64_MOVW_UABS_G0_NC, Value.Addend); - - if (Value.SymbolName) { - addRelocationForSymbol(REmovz_g3, Value.SymbolName); - addRelocationForSymbol(REmovk_g2, Value.SymbolName); - addRelocationForSymbol(REmovk_g1, Value.SymbolName); - addRelocationForSymbol(REmovk_g0, Value.SymbolName); - } else { - addRelocationForSection(REmovz_g3, Value.SectionID); - addRelocationForSection(REmovk_g2, Value.SectionID); - addRelocationForSection(REmovk_g1, Value.SectionID); - addRelocationForSection(REmovk_g0, Value.SectionID); - } - resolveRelocation(Section, Offset, - reinterpret_cast(Section.getAddressWithOffset( - Section.getStubOffset())), - RelType, 0); - Section.advanceStubOffset(getMaxStubSize()); + } else if (RelType == ELF::R_AARCH64_LD64_GOT_LO12_NC) { + uint64_t GOTOffset = findOrAllocGOTEntry(Value, ELF::R_AARCH64_ABS64); + resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend, + ELF::R_AARCH64_LDST64_ABS_LO12_NC); + } else { + processSimpleRelocation(SectionID, Offset, RelType, Value); } } else if (Arch == Triple::arm) { if (RelType == ELF::R_ARM_PC24 || RelType == ELF::R_ARM_CALL || @@ -1252,7 +1274,7 @@ RuntimeDyldELF::processRelocationRef( if (i != GOTSymbolOffsets.end()) RE.SymOffset = i->second; else { - RE.SymOffset = allocateGOTEntries(SectionID, 1); + RE.SymOffset = allocateGOTEntries(1); GOTSymbolOffsets[TargetName] = RE.SymOffset; } } @@ -1509,14 +1531,15 @@ RuntimeDyldELF::processRelocationRef( Section.advanceStubOffset(getMaxStubSize()); // Allocate a GOT Entry - uint64_t GOTOffset = allocateGOTEntries(SectionID, 1); + uint64_t GOTOffset = allocateGOTEntries(1); // The load of the GOT address has an addend of -4 - resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4); + resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4, + ELF::R_X86_64_PC32); // Fill in the value of the symbol we're targeting into the GOT addRelocationForSymbol( - computeGOTOffsetRE(SectionID, GOTOffset, 0, ELF::R_X86_64_64), + computeGOTOffsetRE(GOTOffset, 0, ELF::R_X86_64_64), Value.SymbolName); } @@ -1531,11 +1554,13 @@ RuntimeDyldELF::processRelocationRef( } else if (RelType == ELF::R_X86_64_GOTPCREL || RelType == ELF::R_X86_64_GOTPCRELX || RelType == ELF::R_X86_64_REX_GOTPCRELX) { - uint64_t GOTOffset = allocateGOTEntries(SectionID, 1); - resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend); + uint64_t GOTOffset = allocateGOTEntries(1); + resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend, + ELF::R_X86_64_PC32); // Fill in the value of the symbol we're targeting into the GOT - RelocationEntry RE = computeGOTOffsetRE(SectionID, GOTOffset, Value.Offset, ELF::R_X86_64_64); + RelocationEntry RE = + computeGOTOffsetRE(GOTOffset, Value.Offset, ELF::R_X86_64_64); if (Value.SymbolName) addRelocationForSymbol(RE, Value.SymbolName); else @@ -1593,9 +1618,7 @@ size_t RuntimeDyldELF::getGOTEntrySize() { return Result; } -uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no) -{ - (void)SectionID; // The GOT Section is the same for all section in the object file +uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned no) { if (GOTSectionID == 0) { GOTSectionID = Sections.size(); // Reserve a section id. We'll allocate the section later @@ -1607,17 +1630,38 @@ uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no) return StartOffset; } -void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset, uint64_t GOTOffset) -{ +uint64_t RuntimeDyldELF::findOrAllocGOTEntry(const RelocationValueRef &Value, + unsigned GOTRelType) { + auto E = GOTOffsetMap.insert({Value, 0}); + if (E.second) { + uint64_t GOTOffset = allocateGOTEntries(1); + + // Create relocation for newly created GOT entry + RelocationEntry RE = + computeGOTOffsetRE(GOTOffset, Value.Offset, GOTRelType); + if (Value.SymbolName) + addRelocationForSymbol(RE, Value.SymbolName); + else + addRelocationForSection(RE, Value.SectionID); + + E.first->second = GOTOffset; + } + + return E.first->second; +} + +void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID, + uint64_t Offset, + uint64_t GOTOffset, + uint32_t Type) { // Fill in the relative address of the GOT Entry into the stub - RelocationEntry GOTRE(SectionID, Offset, ELF::R_X86_64_PC32, GOTOffset); + RelocationEntry GOTRE(SectionID, Offset, Type, GOTOffset); addRelocationForSection(GOTRE, GOTSectionID); } -RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(unsigned SectionID, uint64_t GOTOffset, uint64_t SymbolOffset, - uint32_t Type) -{ - (void)SectionID; // The GOT Section is the same for all section in the object file +RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(uint64_t GOTOffset, + uint64_t SymbolOffset, + uint32_t Type) { return RelocationEntry(GOTSectionID, GOTOffset, Type, SymbolOffset); } @@ -1683,6 +1727,19 @@ bool RuntimeDyldELF::isCompatibleFile(const object::ObjectFile &Obj) const { return Obj.isELF(); } +bool RuntimeDyldELF::relocationNeedsGot(const RelocationRef &R) const { + unsigned RelTy = R.getType(); + if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be) + return RelTy == ELF::R_AARCH64_ADR_GOT_PAGE || + RelTy == ELF::R_AARCH64_LD64_GOT_LO12_NC; + + if (Arch == Triple::x86_64) + return RelTy == ELF::R_X86_64_GOTPCREL || + RelTy == ELF::R_X86_64_GOTPCRELX || + RelTy == ELF::R_X86_64_REX_GOTPCRELX; + return false; +} + bool RuntimeDyldELF::relocationNeedsStub(const RelocationRef &R) const { if (Arch != Triple::x86_64) return true; // Conservative answer diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index d1867d091fe2..498979705b77 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -43,6 +43,9 @@ class RuntimeDyldELF : public RuntimeDyldImpl { bool resolveAArch64ShortBranch(unsigned SectionID, relocation_iterator RelI, const RelocationValueRef &Value); + void resolveAArch64Branch(unsigned SectionID, const RelocationValueRef &Value, + relocation_iterator RelI, StubMap &Stubs); + void resolveARMRelocation(const SectionEntry &Section, uint64_t Offset, uint32_t Value, uint32_t Type, int32_t Addend); @@ -88,24 +91,26 @@ class RuntimeDyldELF : public RuntimeDyldImpl { ObjSectionToIDMap &LocalSections, RelocationValueRef &Rel); protected: - size_t getGOTEntrySize(); + size_t getGOTEntrySize() override; private: SectionEntry &getSection(unsigned SectionID) { return Sections[SectionID]; } // Allocate no GOT entries for use in the given section. - uint64_t allocateGOTEntries(unsigned SectionID, unsigned no); + uint64_t allocateGOTEntries(unsigned no); + + // Find GOT entry corresponding to relocation or create new one. + uint64_t findOrAllocGOTEntry(const RelocationValueRef &Value, + unsigned GOTRelType); // Resolve the relvative address of GOTOffset in Section ID and place // it at the given Offset void resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset, - uint64_t GOTOffset); + uint64_t GOTOffset, uint32_t Type); // For a GOT entry referenced from SectionID, compute a relocation entry // that will place the final resolved value in the GOT slot - RelocationEntry computeGOTOffsetRE(unsigned SectionID, - uint64_t GOTOffset, - uint64_t SymbolOffset, + RelocationEntry computeGOTOffsetRE(uint64_t GOTOffset, uint64_t SymbolOffset, unsigned Type); // Compute the address in memory where we can find the placeholder @@ -146,6 +151,10 @@ private: SmallVector UnregisteredEHFrameSections; SmallVector RegisteredEHFrameSections; + // Map between GOT relocation value and corresponding GOT offset + std::map GOTOffsetMap; + + bool relocationNeedsGot(const RelocationRef &R) const override; bool relocationNeedsStub(const RelocationRef &R) const override; public: diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index 279d0de2da76..f5cc883d98fd 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -213,7 +213,7 @@ public: } }; -/// @brief Symbol info for RuntimeDyld. +/// @brief Symbol info for RuntimeDyld. class SymbolTableEntry { public: SymbolTableEntry() @@ -426,6 +426,9 @@ protected: uint64_t &RODataSize, uint32_t &RODataAlign, uint64_t &RWDataSize, uint32_t &RWDataAlign); + // \brief Compute GOT size + unsigned computeGOTSize(const ObjectFile &Obj); + // \brief Compute the stub buffer size required for a section unsigned computeSectionStubBufSize(const ObjectFile &Obj, const SectionRef &Section); @@ -433,6 +436,14 @@ protected: // \brief Implementation of the generic part of the loadObject algorithm. Expected loadObjectImpl(const object::ObjectFile &Obj); + // \brief Return size of Global Offset Table (GOT) entry + virtual size_t getGOTEntrySize() { return 0; } + + // \brief Return true if the relocation R may require allocating a GOT entry. + virtual bool relocationNeedsGot(const RelocationRef &R) const { + return false; + } + // \brief Return true if the relocation R may require allocating a stub. virtual bool relocationNeedsStub(const RelocationRef &R) const { return true; // Conservative answer diff --git a/llvm/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_PIC_relocations.s b/llvm/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_PIC_relocations.s new file mode 100644 index 000000000000..ba00afc7ad99 --- /dev/null +++ b/llvm/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_PIC_relocations.s @@ -0,0 +1,46 @@ +# RUN: llvm-mc -triple=arm64-none-linux-gnu -filetype=obj -o %T/pic-reloc.o %s +# RUN: llvm-rtdyld -triple=arm64-none-linux-gnu -verify -check=%s %T/pic-reloc.o \ +# RUN: -map-section pic-reloc.o,.got=0x20000 -dummy-extern f=0x1234 -dummy-extern g=0x5678 + +_s: + nop +_a1: + adrp x8, :got:f +_a2: + adrp x9, :got:g +_a3: + adrp x10, :got:_s +_l1: + ldr x8, [x8, :got_lo12:f] +_l2: + ldr x9, [x9, :got_lo12:g] +_l3: + ldr x10, [x10, :got_lo12:_s] + + +## We'll end up having two sections .text and .got, +## each is located on the start of a memory page + +## Test that .got section has three entries pointing to f, g and _s +# *{8}section_addr(pic-reloc.o, .got) = f +# *{8}(section_addr(pic-reloc.o, .got) + 8) = g +# *{8}(section_addr(pic-reloc.o, .got) + 16) = _s + +## Test that first adrp instruction really takes address of +## the .got section (_s label is on the start of a page) +# rtdyld-check: _s + (((*{4}_a1)[30:29] + ((*{4}_a1)[23:5] << 2)) << 12) = section_addr(pic-reloc.o, .got) + +## Test that second adrp takes address of .got +# rtdyld-check: _s + (((*{4}_a2)[30:29] + ((*{4}_a2)[23:5] << 2)) << 12) = section_addr(pic-reloc.o, .got) + +## Test that third adrp takes address of .got +# rtdyld-check: _s + (((*{4}_a3)[30:29] + ((*{4}_a3)[23:5] << 2)) << 12) = section_addr(pic-reloc.o, .got) + +## Test that first ldr immediate value is 0 >> 3 = 0 (1st .got entry) +# rtdyld-check: (*{4}_l1)[21:10] = 0 + +## Test that second ldr immediate value is 8 >> 3 = 1 (2nd .got entry) +# rtdyld-check: (*{4}_l2)[21:10] = 1 + +## Test that third ldr immediate value is 16 >> 3 = 2 (3rd .got entry, addend is 0) +# rtdyld-check: (*{4}_l3)[21:10] = 2