Index: lld/trunk/lib/ReaderWriter/ELF/ARM/ARMELFFile.h =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ARM/ARMELFFile.h +++ lld/trunk/lib/ReaderWriter/ELF/ARM/ARMELFFile.h @@ -54,6 +54,8 @@ }; template class ARMELFFile : public ELFFile { + typedef llvm::object::Elf_Rel_Impl Elf_Rel; + public: ARMELFFile(std::unique_ptr mb, ARMLinkingContext &ctx) : ELFFile(std::move(mb), ctx) {} @@ -64,6 +66,15 @@ new ARMELFFile(std::move(mb), ctx)); } +protected: + /// Returns initial addend; for ARM it is 0, because it is read + /// during the relocations applying + Reference::Addend getInitialAddend(ArrayRef, + uint64_t, + const Elf_Rel&) const override { + return 0; + } + private: typedef llvm::object::Elf_Sym_Impl Elf_Sym; typedef llvm::object::Elf_Shdr_Impl Elf_Shdr; Index: lld/trunk/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h +++ lld/trunk/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,19 @@ return 0x400000; return _baseAddress; } + + 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; + } + } }; // Special methods to check code model of atoms. Index: lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp +++ lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -412,6 +412,75 @@ applyArmReloc(location, result); } +template +static void relocR_ARM_ALU_PC_GN_NC(uint8_t *location, uint32_t result) { + static_assert(lshift < 32 && lshift % 2 == 0, + "lshift must be even and less than word size"); + + const uint32_t rshift = 32 - lshift; + result = ((result >> lshift) & 0xFF) | ((rshift / 2) << 8); + + applyArmReloc(location, result, 0xFFF); +} + +/// \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) { + int32_t result = (int32_t)((S + A) - P); + + if (result < 0) + llvm_unreachable( + "Negative offsets for group relocations has not been implemented"); + + 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((uint32_t)result) << "\n"); + + relocR_ARM_ALU_PC_GN_NC<20>(location, (uint32_t)result); +} + +/// \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) { + int32_t result = (int32_t)((S + A) - P); + + if (result < 0) + llvm_unreachable( + "Negative offsets for group relocations has not been implemented"); + + 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((uint32_t)result) << "\n"); + + relocR_ARM_ALU_PC_GN_NC<12>(location, (uint32_t)result); +} + +/// \brief R_ARM_LDR_PC_G2 - S + A - P +static void relocR_ARM_LDR_PC_G2(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int32_t result = (int32_t)((S + A) - P); + + if (result < 0) + llvm_unreachable( + "Negative offsets for group relocations has not been implemented"); + + 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((uint32_t)result) << "\n"); + + const uint32_t mask = 0xFFF; + applyArmReloc(location, (uint32_t)result & mask, mask); +} + std::error_code ARMTargetRelocationHandler::applyRelocation( ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, const Reference &ref) const { @@ -426,7 +495,7 @@ // Calculate proper initial addend for the relocation const Reference::Addend addend = - readAddend(location, ref.kindValue()); + readAddend(location, ref.kindValue()) + ref.addend(); // Flags that the relocation addresses Thumb instruction bool addressesThumb = false; @@ -492,6 +561,18 @@ relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend, _armLayout.getTPOffset()); 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: lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp +++ lld/trunk/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -28,6 +28,7 @@ using namespace lld::elf; using namespace llvm::ELF; +namespace { // ARM B/BL instructions of static relocation veneer. // TODO: consider different instruction set for archs below ARMv5 // (one as for Thumb may be used though it's less optimal). @@ -48,7 +49,17 @@ // .got values static const uint8_t ARMGotAtomContent[4] = {0}; -namespace { +// .plt values (other entries) +static 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; @@ -119,6 +130,15 @@ Alignment alignment() const override { return 4; } }; +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") { @@ -140,8 +160,16 @@ return; assert(ref.kindArch() == Reference::KindArch::ARM); switch (ref.kindValue()) { + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_CALL: + case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_THM_JUMP24: + static_cast(this)->handleIFUNC(ref); static_cast(this)->handleVeneer(atom, ref); break; case R_ARM_TLS_IE32: @@ -152,6 +180,15 @@ protected: std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) { + const auto kindValue = ref.kindValue(); + switch (kindValue) { + case R_ARM_JUMP24: + case R_ARM_THM_JUMP24: + break; + default: + return std::error_code(); + } + // Target symbol and relocated place should have different // instruction sets in order a veneer to be generated in between. const auto *target = dyn_cast(ref.target()); @@ -163,7 +200,6 @@ // Veneers may only be generated for STT_FUNC target symbols // or for symbols located in sections different to the place of relocation. - const auto kindValue = ref.kindValue(); StringRef secName = atom.customSectionName(); if (DefinedAtom::typeCode != target->contentType() && !target->customSectionName().equals(secName)) { @@ -227,6 +263,65 @@ return g; } + /// \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); + +#ifndef NDEBUG + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); +#endif + + _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(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) {} @@ -278,6 +373,10 @@ got->setOrdinal(ordinal++); mf->addAtom(*got); } + for (auto &plt : _pltVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } for (auto &veneer : _veneerVector) { veneer->setOrdinal(ordinal++); mf->addAtom(*veneer); @@ -292,11 +391,15 @@ /// \brief Map Atoms to their GOT entries. llvm::DenseMap _gotMap; + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap _pltMap; + /// \brief Map Atoms to their veneers. llvm::DenseMap _veneerMap; /// \brief the list of GOT/PLT atoms std::vector _gotVector; + std::vector _pltVector; /// \brief the list of veneer atoms. std::vector _veneerVector; Index: lld/trunk/lib/ReaderWriter/ELF/ELFFile.h =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ELFFile.h +++ lld/trunk/lib/ReaderWriter/ELF/ELFFile.h @@ -316,6 +316,13 @@ return symbol->st_value; } + /// Returns initial addend + virtual Reference::Addend getInitialAddend(ArrayRef symContent, + uint64_t symbolValue, + const Elf_Rel& reference) const { + return *(symContent.data() + reference.r_offset - symbolValue); + } + /// Process the common symbol and create an atom for it. virtual ErrorOr *> handleCommonSymbol(StringRef symName, const Elf_Sym *sym) { @@ -1030,7 +1037,7 @@ auto elfRelocation = new (_readerStorage) ELFReference(rel.r_offset - symValue, kindArch(), rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); - int32_t addend = *(symContent.data() + rel.r_offset - symValue); + Reference::Addend addend = getInitialAddend(symContent, symValue, rel); elfRelocation->setAddend(addend); addReferenceToSymbol(elfRelocation, symbol); _references.push_back(elfRelocation); Index: lld/trunk/test/elf/ARM/rel-group-relocs.test =================================================================== --- lld/trunk/test/elf/ARM/rel-group-relocs.test +++ lld/trunk/test/elf/ARM/rel-group-relocs.test @@ -0,0 +1,69 @@ +# Check handling of group relocations (R_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G1_NC, +# R_ARM_LDR_PC_G2). +# +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: -e plt_func %t-o.o -o %t +# RUN: llvm-objdump -s -t %t -disassemble | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK: plt_func: +# CHECK: 400074: 00 c6 8f e2 add r12, pc, #0, #12 +# ^ after execution: r12=0x40007c +# CHECK: 400078: 00 ca 8c e2 add r12, r12, #0, #20 +# ^ after execution: r12=0x40007C +# CHECK: 40007c: 84 ff bc e5 ldr pc, [r12, #3972]! +# ^ referenced address is 0x401000, after execution pc=0x400074 +# CHECK: Contents of section .data: +# CHECK: 401000 74004000 +# ^ this value is written to pc after execution of comand 0x40007c +# ^ this address is referenced by command at 0x40007c +# 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: 00C08FE200C08CE200F0BCE5 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + Info: .text + AddressAlign: 0x4 + Relocations: + - Offset: 0 + Symbol: got_func + Type: R_ARM_ALU_PC_G0_NC + Addend: -8 + - Offset: 0x4 + Symbol: got_func + Type: R_ARM_ALU_PC_G1_NC + Addend: -4 + - Offset: 0x8 + Symbol: got_func + Type: R_ARM_LDR_PC_G2 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x4 + Content: 74004000 +Symbols: + Local: + Global: + - Name: plt_func + Type: STT_FUNC + Section: .text + Size: 0xC + - Name: got_func + Section: .data + Size: 0x4 +... Index: lld/trunk/test/elf/ARM/rel-ifunc.test =================================================================== --- lld/trunk/test/elf/ARM/rel-ifunc.test +++ lld/trunk/test/elf/ARM/rel-ifunc.test @@ -0,0 +1,102 @@ +# 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 .rel.plt: +# CHECK: 400074 00104000 +# CHECK: Contents of section .plt: +# CHECK: 400080 00c68fe2 00ca8ce2 78ffbce5 +# CHECK: Contents of section .text: +# CHECK: 4001ac 00482de9 04b08de2 d4ffffeb b0ffffeb +# ^ bl #-320 (to address 0x400080=__plt_ifunc_myfunc) +# CHECK: Contents of section .got.plt: +# CHECK: 401000 c4004000 +# CHECK: SYMBOL TABLE: +# CHECK: 00400080 l F .plt 0000000c __plt_ifunc_myfunc +# CHECK: 004000c4 g .text 00000020 myfunc +# 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: 04B02DE500B08DE20130A0E30300A0E100D04BE204B09DE41EFF2FE104B02DE500B08DE20230A0E30300A0E100D04BE204B09DE41EFF2FE104B02DE500B08DE2003000E3003040E30300A0E100D04BE204B09DE41EFF2FE100482DE904B08DE208D04DE208000BE508301BE533FF2FE10030A0E10300A0E104D04BE20088BDE800482DE904B08DE210D04DE2003000E3003040E308300BE5190000EA08301BE50C300BE50C301BE5003093E510300BE50C301BE5043093E57330EFE614300BE514301BE5A00053E30130A0030030A0137330EFE6000053E30600000A10301BE5003093E50300A0E1DAFFFFEB0020A0E110301BE5002083E508301BE5083083E208300BE508201BE5003000E3003040E3030052E1E0FFFF3A04D04BE20088BDE800482DE904B08DE2D4FFFFEBFEFFFFEB0030A0E30300A0E10088BDE8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000040 + Symbol: myfunc1 + Type: R_ARM_MOVW_ABS_NC + - Offset: 0x0000000000000044 + Symbol: myfunc1 + Type: R_ARM_MOVT_ABS + - Offset: 0x000000000000008C + Symbol: __rel_iplt_start + Type: R_ARM_MOVW_ABS_NC + - Offset: 0x0000000000000090 + Symbol: __rel_iplt_start + Type: R_ARM_MOVT_ABS + - Offset: 0x0000000000000108 + Symbol: __rel_iplt_end + Type: R_ARM_MOVW_ABS_NC + - Offset: 0x000000000000010C + Symbol: __rel_iplt_end + Type: R_ARM_MOVT_ABS + - Offset: 0x000000000000012C + Symbol: myfunc + Type: R_ARM_CALL +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: '$a' + Section: .text + - Name: elf_ifunc_invoke + Type: STT_FUNC + Section: .text + Value: 0x0000000000000058 + Size: 0x0000000000000028 + - Name: apply_irel + Type: STT_FUNC + Section: .text + Value: 0x0000000000000080 + Size: 0x00000000000000A0 + Global: + - Name: myfunc1 + Type: STT_FUNC + Section: .text + Size: 0x000000000000001C + - Name: myfunc2 + Type: STT_FUNC + Section: .text + Value: 0x000000000000001C + Size: 0x000000000000001C + - Name: myfunc + Type: STT_GNU_IFUNC + Section: .text + Value: 0x0000000000000038 + Size: 0x0000000000000020 + - Name: _start + Type: STT_FUNC + Section: .text + Value: 0x0000000000000120 + Size: 0x000000000000001C + Weak: + - Name: __rel_iplt_start + - Name: __rel_iplt_end +... Index: lld/trunk/test/elf/ARM/undef-lazy-symbol.test =================================================================== --- lld/trunk/test/elf/ARM/undef-lazy-symbol.test +++ lld/trunk/test/elf/ARM/undef-lazy-symbol.test @@ -5,7 +5,7 @@ # RUN: --noinhibit-exec %t-got.o -o %t # RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=GOT %s -# GOT: Name: _GLOBAL_OFFSET_TABLE_ (185) +# GOT: Name: _GLOBAL_OFFSET_TABLE_ (183) # GOT-NEXT: Value: {{[0-9]+}} # GOT-NEXT: Size: 0 # GOT-NEXT: Binding: Global (0x1) @@ -20,7 +20,7 @@ # RUN: --defsym=main=fn --noinhibit-exec %t-exidx.o -o %t # RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=EXIDX %s -# EXIDX: Name: __exidx_start (188) +# EXIDX: Name: __exidx_start (186) # EXIDX-NEXT: Value: {{[0-9]+}} # EXIDX-NEXT: Size: 0 # EXIDX-NEXT: Binding: Global (0x1) @@ -28,7 +28,7 @@ # EXIDX-NEXT: Other: 0 # EXIDX-NEXT: Section: Absolute (0xFFF1) # -# EXIDX: Name: __exidx_end (202) +# EXIDX: Name: __exidx_end (200) # EXIDX-NEXT: Value: {{[0-9]+}} # EXIDX-NEXT: Size: 0 # EXIDX-NEXT: Binding: Global (0x1) @@ -44,7 +44,7 @@ # RUN: --noinhibit-exec %t-got.o %t-exidx.o -o %t # RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=SYMS %s -# SYMS: Name: _GLOBAL_OFFSET_TABLE_ (188) +# SYMS: Name: _GLOBAL_OFFSET_TABLE_ (186) # SYMS-NEXT: Value: {{[0-9]+}} # SYMS-NEXT: Size: 0 # SYMS-NEXT: Binding: Global (0x1) @@ -52,7 +52,7 @@ # SYMS-NEXT: Other: 0 # SYMS-NEXT: Section: Absolute (0xFFF1) # -# SYMS: Name: __exidx_start (210) +# SYMS: Name: __exidx_start (208) # SYMS-NEXT: Value: {{[0-9]+}} # SYMS-NEXT: Size: 0 # SYMS-NEXT: Binding: Global (0x1) @@ -60,7 +60,7 @@ # SYMS-NEXT: Other: 0 # SYMS-NEXT: Section: Absolute (0xFFF1) # -# SYMS: Name: __exidx_end (224) +# SYMS: Name: __exidx_end (222) # SYMS-NEXT: Value: {{[0-9]+}} # SYMS-NEXT: Size: 0 # SYMS-NEXT: Binding: Global (0x1)