From 87c4268329072153f178d554e138166d579f18b9 Mon Sep 17 00:00:00 2001 From: Sunho Kim Date: Fri, 10 Jun 2022 13:46:31 +0900 Subject: [PATCH] [JITLink][ELF][AArch64] Implement Procedure Linkage Table. Implements Procedure Linkage Table (PLT) for ELF/AARCH64. The aarch64 linux calling convention also uses r16 as the intra-procedure-call scratch register same as MachO/ARM64. We can use the same stub sequence for this reason. Also, BR regiseter doesn't touch X30 register. External function call by BL instruction (touched by CALL26 relocation) will set X30 to the original PC + 4, which is the intended behavior. External function call by B instruction (touched by JUMP26 relocation) doesn't requite to set X30, so the patch will be correct in this case too. Reference: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#611general-purpose-registers Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D127061 --- llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp | 35 +++++++++++++++++++--- .../JITLink/AArch64/ELF_aarch64_relocations.s | 28 +++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp index ae99637..0f6f731 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp @@ -321,14 +321,24 @@ public: llvm_unreachable("Not a GOT edge?"); } - bool isExternalBranchEdge(Edge &E) { return false; } + bool isExternalBranchEdge(Edge &E) { + return E.getKind() == aarch64::Branch26 && !E.getTarget().isDefined(); + } Symbol &createPLTStub(Symbol &Target) { - assert(false && "unimplemetned"); - return Target; + auto &StubContentBlock = G.createContentBlock( + getStubsSection(), getStubBlockContent(), orc::ExecutorAddr(), 1, 0); + // Re-use GOT entries for stub targets. + auto &GOTEntrySymbol = getGOTEntry(Target); + StubContentBlock.addEdge(aarch64::LDRLiteral19, 0, GOTEntrySymbol, 0); + return G.addAnonymousSymbol(StubContentBlock, 0, 8, true, false); } - void fixPLTEdge(Edge &E, Symbol &Stub) { assert(false && "unimplemetned"); } + void fixPLTEdge(Edge &E, Symbol &Stub) { + assert(E.getKind() == aarch64::Branch26 && "Not a Branch26 edge?"); + assert(E.getAddend() == 0 && "Branch26 edge has non-zero addend?"); + E.setTarget(Stub); + } private: Section &getGOTSection() { @@ -337,17 +347,34 @@ private: return *GOTSection; } + Section &getStubsSection() { + if (!StubsSection) + StubsSection = + &G.createSection("$__STUBS", MemProt::Read | MemProt::Exec); + return *StubsSection; + } + ArrayRef getGOTEntryBlockContent() { return {reinterpret_cast(NullGOTEntryContent), sizeof(NullGOTEntryContent)}; } + ArrayRef getStubBlockContent() { + return {reinterpret_cast(StubContent), sizeof(StubContent)}; + } + static const uint8_t NullGOTEntryContent[8]; + static const uint8_t StubContent[8]; Section *GOTSection = nullptr; + Section *StubsSection = nullptr; }; const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_arm64::NullGOTEntryContent[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_arm64::StubContent[8] = { + 0x10, 0x00, 0x00, 0x58, // LDR x16, + 0x00, 0x02, 0x1f, 0xd6 // BR x16 +}; Expected> createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer) { diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_relocations.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_relocations.s index 8604878..8714f09 100644 --- a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_relocations.s +++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_aarch64_relocations.s @@ -3,6 +3,7 @@ # RUN: -position-independent -filetype=obj -o %t/elf_reloc.o %s # RUN: llvm-jitlink -noexec \ # RUN: -abs external_data=0xdeadbeef \ +# RUN: -abs external_func=0xcafef00d \ # RUN: -check %s %t/elf_reloc.o .text @@ -57,6 +58,24 @@ test_add_abs_lo12: add x0, x0, :lo12:named_data .size test_add_abs_lo12, .-test_add_abs_lo12 +# Check that calls/jumps to external functions trigger the generation of stubs and GOT +# entries. +# +# jitlink-check: decode_operand(test_external_call, 0) = (stub_addr(elf_reloc.o, external_func) - test_external_call)[27:2] +# jitlink-check: decode_operand(test_external_jump, 0) = (stub_addr(elf_reloc.o, external_func) - test_external_jump)[27:2] +# jitlink-check: *{8}(got_addr(elf_reloc.o, external_func)) = external_func + .globl test_external_call + .p2align 2 +test_external_call: + bl external_func + .size test_external_call, .-test_external_call + + .globl test_external_jump + .p2align 2 +test_external_jump: + b external_func + .size test_external_jump, .-test_external_jump + # Check R_AARCH64_LDST*_ABS_LO12_NC relocation of a local symbol # # The immediate value should be the symbol address right shifted according to its instruction bitwidth. @@ -131,6 +150,15 @@ local_func_addr_quad: .xword named_func .size local_func_addr_quad, 8 +# Check R_AARCH64_ABS64 relocation of a function pointer to external symbol +# +# jitlink-check: *{8}external_func_addr_quad = external_func + .globl external_func_addr_quad + .p2align 3 +external_func_addr_quad: + .xword external_func + .size external_func_addr_quad, 8 + # Check R_AARCH64_ADR_GOT_PAGE / R_AARCH64_LD64_GOT_LO12_NC handling with a # reference to an external symbol. Validate both the reference to the GOT entry, # and also the content of the GOT entry. -- 2.7.4