Index: lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h =================================================================== --- lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h +++ lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h @@ -22,6 +22,8 @@ static std::unique_ptr create(llvm::Triple); ARMLinkingContext(llvm::Triple); + bool isRelaOutputFormat() const override { return false; } + void addPasses(PassManager &) override; uint64_t getBaseAddress() const override { @@ -29,6 +31,49 @@ return 0x400000; return _baseAddress; } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_TLS_DTPMOD32: + case llvm::ELF::R_ARM_TLS_DTPOFF32: + case llvm::ELF::R_ARM_TLS_TPOFF32: + case llvm::ELF::R_ARM_COPY: + case llvm::ELF::R_ARM_GLOB_DAT: + case llvm::ELF::R_ARM_JUMP_SLOT: + case llvm::ELF::R_ARM_RELATIVE: + return true; + default: + return false; + } + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_JUMP_SLOT: + case llvm::ELF::R_ARM_IRELATIVE: + return true; + default: + return false; + } + } + + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_RELATIVE: + return true; + default: + return false; + } + } }; } // end namespace elf } // end namespace lld Index: lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp =================================================================== --- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp +++ lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -309,6 +309,57 @@ return relocR_ARM_THM_MOV(location, arg); } +static uint32_t formALU_PC_GN(uint32_t val, uint32_t lshift) { + assert(lshift < 32 && lshift % 2 == 0); + const uint32_t rshift = 32 - lshift; + return (val & 0xFF) | ((rshift / 2) << 8); +} + +/// \brief R_ARM_ALU_PC_G0_NC - ((S + A) | T) - P => S + A - P +static void relocR_ARM_ALU_PC_G0_NC(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + result = result >> 20; + result = formALU_PC_GN(result, 20); + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result, 0xFFF); +} + +/// \brief R_ARM_ALU_PC_G1_NC - ((S + A) | T) - P => S + A - P +static void relocR_ARM_ALU_PC_G1_NC(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + result = result >> 12; + result = formALU_PC_GN(result, 12); + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result, 0xFFF); +} + +/// \brief R_ARM_LDR_PC_G2 - ((S + A) | T) - P => S + A - P +static void relocR_ARM_LDR_PC_G2(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + const uint32_t mask = 0xFFF; + result = result & mask; + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result, mask); +} + std::error_code ARMTargetRelocationHandler::applyRelocation( ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, const Reference &ref) const { @@ -371,6 +422,18 @@ case R_ARM_THM_MOVT_ABS: relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); break; + case R_ARM_ALU_PC_G0_NC: + relocR_ARM_ALU_PC_G0_NC(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_ALU_PC_G1_NC: + relocR_ARM_ALU_PC_G1_NC(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_LDR_PC_G2: + relocR_ARM_LDR_PC_G2(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_IRELATIVE: + // Runtime only relocations. Ignore here. + break; default: return make_unhandled_reloc_error(); } Index: lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp =================================================================== --- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp +++ lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -46,6 +46,20 @@ 0xfe, 0xff, 0xff, 0xea // b }; +// .got values +const uint8_t ARMGotAtomContent[4] = {0, 0, 0, 0}; + +// .plt values (other entries) +const uint8_t ARMPltAtomContent[12] = { + 0x00, 0xc0, 0x8f, + 0xe2, // add ip, pc, #offset[G0] + 0x00, 0xc0, 0x8c, + 0xe2, // add ip, ip, #offset[G1] + + 0x00, 0xf0, 0xbc, + 0xe5, // ldr pc, [ip, #offset[G2]]! +}; + /// \brief Atoms that hold veneer code. class VeneerAtom : public SimpleELFDefinedAtom { StringRef _section; @@ -104,6 +118,28 @@ } }; +/// \brief Atoms that are used by ARM dynamic linking +class ARMGOTAtom : public GOTAtom { +public: + ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef rawContent() const override { + return llvm::makeArrayRef(ARMGotAtomContent); + } + + Alignment alignment() const override { return Alignment(2); } +}; + +class ARMPLTAtom : public PLTAtom { +public: + ARMPLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + + ArrayRef rawContent() const override { + return llvm::makeArrayRef(ARMPltAtomContent); + } +}; + class ELFPassFile : public SimpleFile { public: ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { @@ -129,6 +165,10 @@ case R_ARM_THM_JUMP24: static_cast(this)->handleVeneer(atom, ref); break; + case R_ARM_CALL: + case R_ARM_THM_CALL: + static_cast(this)->handleIFUNC(ref); + break; } } @@ -183,6 +223,62 @@ return std::error_code(); } + /// \brief Create a PLT entry referencing PLTGOT entry. + /// + /// The function creates the PLT entry object and passes ownership + /// over it to the caller. + PLTAtom *createPLTforGOT(const GOTAtom *ga) { + auto pa = new (_file._alloc) ARMPLTAtom(_file, ".plt"); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G0_NC, 0, ga, -8); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G1_NC, 4, ga, -4); + pa->addReferenceELF_ARM(R_ARM_LDR_PC_G2, 8, ga, 0); + return pa; + } + + /// \brief get the PLT entry for a given IFUNC Atom. + /// + /// If the entry does not exist. Both the GOT and PLT entry is created. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da) { + auto plt = _pltMap.find(da); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) ARMGOTAtom(_file, ".got.plt"); + ga->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); + ga->addReferenceELF_ARM(R_ARM_IRELATIVE, 0, da, 0); + auto pa = createPLTforGOT(ga); + + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); + + _gotMap[da] = ga; + _pltMap[da] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + /// \brief Handle adding of PLT entry by marking the reference + /// as requiring veneer generation. + std::error_code handlePLTEntry(Reference &ref, const PLTAtom *pa) { + ref.setTarget(pa); + + return std::error_code(); + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(const Reference &ref) { + auto target = dyn_cast_or_null(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) { + return handlePLTEntry(const_cast(ref), + getIFUNCPLTEntry(target)); + } + return std::error_code(); + } public: ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} @@ -234,6 +330,14 @@ veneer->setOrdinal(ordinal++); mf->addAtom(*veneer); } + for (auto &plt : _pltVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } } protected: @@ -246,6 +350,15 @@ /// \brief the list of veneer atoms. std::vector _veneerVector; + + /// \brief Map Atoms to their GOT entries. + llvm::DenseMap _gotMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap _pltMap; + /// \brief the list of GOT/PLT atoms + std::vector _gotVector; + std::vector _pltVector; }; /// This implements the static relocation model. Meaning GOT and PLT entries are Index: test/elf/ARM/rel-arm-alu-pc-g0-nc.test =================================================================== --- /dev/null +++ test/elf/ARM/rel-arm-alu-pc-g0-nc.test @@ -0,0 +1,51 @@ +# Check handling of R_ARM_ALU_PC_G0_NC relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400074 00c68fe2 00ca8ce2 68ffbce5 +# CHECK: Contents of section .data: +# CHECK: 401000 a5004000 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text 0000000c plt_func +# CHECK: 00401000 g .data 00000004 got_func + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 00C08FE200CA8CE268FFBCE5 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x4 + Relocations: + - Offset: 0 + Symbol: got_func + Type: R_ARM_ALU_PC_G0_NC + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x4 + Content: A5004000 +Symbols: + Local: + Global: + - Name: plt_func + Type: STT_FUNC + Section: .text + Size: 0xC + - Name: got_func + Section: .data + Size: 0x4 +... Index: test/elf/ARM/rel-arm-alu-pc-g1-nc.test =================================================================== --- /dev/null +++ test/elf/ARM/rel-arm-alu-pc-g1-nc.test @@ -0,0 +1,51 @@ +# Check handling of R_ARM_ALU_PC_G1_NC relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400074 00c68fe2 00ca8ce2 68ffbce5 +# CHECK: Contents of section .data: +# CHECK: 401000 a5004000 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text 0000000c plt_func +# CHECK: 00401000 g .data 00000004 got_func + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 00C68FE200C08CE268FFBCE5 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x4 + Relocations: + - Offset: 0x4 + Symbol: got_func + Type: R_ARM_ALU_PC_G1_NC + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x4 + Content: A5004000 +Symbols: + Local: + Global: + - Name: plt_func + Type: STT_FUNC + Section: .text + Size: 0xC + - Name: got_func + Section: .data + Size: 0x4 +... Index: test/elf/ARM/rel-arm-ldr-pc-g2.test =================================================================== --- /dev/null +++ test/elf/ARM/rel-arm-ldr-pc-g2.test @@ -0,0 +1,50 @@ +# Check handling of R_ARM_LDR_PC_G2 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400074 00c68fe2 00ca8ce2 84ffbce5 +# CHECK: Contents of section .data: +# CHECK: 401000 a5004000 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text 0000000c plt_func +# CHECK: 00401000 g .data 00000004 got_func + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 00C68FE200CA8CE200F0BCE5 + - Name: .rel.text + Link: .symtab + Info: .text + Type: SHT_REL + Relocations: + - Offset: 0x8 + Symbol: got_func + Type: R_ARM_LDR_PC_G2 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x4 + Content: A5004000 +Symbols: + Local: + Global: + - Name: plt_func + Type: STT_FUNC + Section: .text + Size: 0xC + - Name: got_func + Section: .data + Size: 0x4 +... Index: test/elf/ARM/rel-ifunc.test =================================================================== --- /dev/null +++ test/elf/ARM/rel-ifunc.test @@ -0,0 +1,136 @@ +# Check handling of IFUNC (gnu_indirect_function). +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .plt: +# CHECK: 400080 00c68fe2 00ca8ce2 78ffbce5 +# CHECK-NEXT: Contents of section .text: +# CHECK: 40013c 80b500af fff7ccff fff79cef 00231846 +# CHECK: Contents of section .got.plt: +# CHECK: 401000 ad004000 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g *ABS* 00000000 __rel_iplt_start +# CHECK: 0040007c g *ABS* 00000000 __rel_iplt_end + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF01231846BD465DF8047B704780B400AF02231846BD465DF8047B704780B400AF40F20003C0F200031846BD465DF8047B704700BF80B582B000AF78607B689847034618460837BD4680BD00BF80B584B000AF40F20003C0F20003FB601BE0FB68BB60BB681B687B60BB685B68DBB23B603B68A02B0CBF01230023DBB2002B07D07B681B681846FFF7D5FF02467B681A60FB680833FB60FA6840F20003C0F200039A42DCD31037BD4680BD00BF80B500AFFFF7CCFFFFF7FEFF0023184680BD00BF + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000024 + Symbol: myfunc1 + Type: R_ARM_THM_MOVW_ABS_NC + - Offset: 0x0000000000000028 + Symbol: myfunc1 + Type: R_ARM_THM_MOVT_ABS + - Offset: 0x0000000000000056 + Symbol: __rel_iplt_start + Type: R_ARM_THM_MOVW_ABS_NC + - Offset: 0x000000000000005A + Symbol: __rel_iplt_start + Type: R_ARM_THM_MOVT_ABS + - Offset: 0x000000000000009C + Symbol: __rel_iplt_end + Type: R_ARM_THM_MOVW_ABS_NC + - Offset: 0x00000000000000A0 + Symbol: __rel_iplt_end + Type: R_ARM_THM_MOVT_ABS + - Offset: 0x00000000000000B8 + Symbol: myfunc + Type: R_ARM_THM_CALL + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 004743433A202863726F7373746F6F6C2D4E47206C696E61726F2D312E31332E312D342E392D323031342E3039202D204C696E61726F2047434320342E392D323031342E30392920342E392E32203230313430393034202870726572656C656173652900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .ARM.attributes + Type: SHT_ARM_ATTRIBUTES + AddressAlign: 0x0000000000000001 + Content: 4134000000616561626900012A00000005372D4100060A0741080109020A041204140115011703180119011A021B031C011E062201 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: '$t' + Section: .text + - Name: elf_ifunc_invoke + Type: STT_FUNC + Section: .text + Value: 0x0000000000000039 + Size: 0x0000000000000016 + - Name: apply_irel + Type: STT_FUNC + Section: .text + Value: 0x0000000000000051 + Size: 0x000000000000005E + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .ARM.attributes + Type: STT_SECTION + Section: .ARM.attributes + Global: + - Name: myfunc1 + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + Size: 0x0000000000000010 + - Name: myfunc2 + Type: STT_FUNC + Section: .text + Value: 0x0000000000000011 + Size: 0x0000000000000010 + - Name: myfunc + Type: STT_GNU_IFUNC + Section: .text + Value: 0x0000000000000021 + Size: 0x0000000000000016 + - Name: _start + Type: STT_FUNC + Section: .text + Value: 0x00000000000000B1 + Size: 0x0000000000000012 + Weak: + - Name: __rel_iplt_start + - Name: __rel_iplt_end +...