diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp --- a/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp @@ -73,7 +73,6 @@ void fixExternalBranchEdge(Edge &E, Symbol &Stub) { assert(E.getKind() == Branch32 && "Not a Branch32 edge?"); - assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?"); // Set the edge kind to Branch32ToStub. We will use this to check for stub // optimization opportunities in the optimize ELF_x86_64_GOTAndStubs pass @@ -160,11 +159,6 @@ }); } } else if (E.getKind() == Branch32ToStub) { - - // Switch the edge kind to PCRel32: Whether we change the edge target - // or not this will be the desired kind. - E.setKind(Branch32); - auto &StubBlock = E.getTarget().getBlock(); assert(StubBlock.getSize() == sizeof(ELF_x86_64_GOTAndStubsBuilder::StubContent) && @@ -185,6 +179,7 @@ int64_t Displacement = TargetAddr - EdgeAddr + 4; if (Displacement >= std::numeric_limits::min() && Displacement <= std::numeric_limits::max()) { + E.setKind(Branch32); E.setTarget(GOTTarget); LLVM_DEBUG({ dbgs() << " Replaced stub branch with direct branch:\n "; @@ -232,6 +227,8 @@ case ELF::R_X86_64_GOTPCRELX: case ELF::R_X86_64_REX_GOTPCRELX: return ELF_x86_64_Edges::ELFX86RelocationKind::PCRel32GOTLoad; + case ELF::R_X86_64_PLT32: + return ELF_x86_64_Edges::ELFX86RelocationKind::Branch32; } return make_error("Unsupported x86-64 relocation:" + formatv("{0:d}", Type)); @@ -639,6 +636,8 @@ char *FixupPtr = BlockWorkingMem + E.getOffset(); JITTargetAddress FixupAddress = B.getAddress() + E.getOffset(); switch (E.getKind()) { + case ELFX86RelocationKind::Branch32: + case ELFX86RelocationKind::Branch32ToStub: case ELFX86RelocationKind::PCRel32: case ELFX86RelocationKind::PCRel32GOTLoad: { int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress; @@ -687,6 +686,10 @@ return "Pointer64"; case PCRel32GOTLoad: return "PCRel32GOTLoad"; + case Branch32: + return "Branch32"; + case Branch32ToStub: + return "Branch32ToStub"; } return getGenericEdgeKindName(static_cast(R)); } diff --git a/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s b/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s --- a/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s +++ b/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s @@ -1,7 +1,10 @@ # RUN: rm -rf %t && mkdir -p %t # RUN: llvm-mc -triple=x86_64-unknown-linux -position-independent -filetype=obj -o %t/elf_reloc.o %s # RUN: llvm-jitlink -noexec -slab-allocate 100Kb -slab-address 0xfff00000 \ -# RUN: -define-abs external_data=0x1 -check %s %t/elf_reloc.o +# RUN: -define-abs external_data=0x1 \ +# RUN: -define-abs extern_in_range32=0xffe00000 \ +# RUN: -define-abs extern_out_of_range32=0x7fff00000000 \ +# RUN: -check %s %t/elf_reloc.o # # Test standard ELF relocations. @@ -27,6 +30,56 @@ .Lend_test_pcrel32: .size test_pcrel32, .Lend_test_pcrel32-test_pcrel32 + .globl named_func + .p2align 4, 0x90 + .type named_func,@function +named_func: + xorq %rax, %rax +.Lend_named_func: + .size named_func, .Lend_named_func-named_func + +# Check R_X86_64_PLT32 handling with a call to a local function. This produces a +# Branch32 edge that is resolved like a regular PCRel32 (no PLT entry created). +# +# jitlink-check: decode_operand(test_call_local, 0) = named_func - next_pc(test_call_local) + .globl test_call_local + .p2align 4, 0x90 + .type test_call_local,@function +test_call_local: + callq named_func +.Lend_test_call_local: + .size test_call_local, .Lend_test_call_local-test_call_local + +# Check R_X86_64_PLT32 handling with a call to an external. This produces a +# Branch32ToStub edge, because externals are not defined locally. During +# resolution, the target turns out to be in-range from the callsite and so the +# edge is relaxed in post-allocation optimization. +# +# jitlink-check: decode_operand(test_call_extern, 0) = extern_in_range32 - next_pc(test_call_extern) + .globl test_call_extern + .p2align 4, 0x90 + .type test_call_extern,@function +test_call_extern: + callq extern_in_range32@plt +.Lend_test_call_extern: + .size test_call_extern, .Lend_test_call_extern-test_call_extern + +# Check R_X86_64_PLT32 handling with a call to an external via PLT. This +# produces a Branch32ToStub edge, because externals are not defined locally. +# As the target is out-of-range from the callsite, the edge keeps using its PLT +# entry. +# +# jitlink-check: decode_operand(test_call_extern_plt, 0) = \ +# jitlink-check: stub_addr(elf_reloc.o, extern_out_of_range32) - next_pc(test_call_extern_plt) +# jitlink-check: *{8}(got_addr(elf_reloc.o, extern_out_of_range32)) = extern_out_of_range32 + .globl test_call_extern_plt + .p2align 4, 0x90 + .type test_call_extern_plt,@function +test_call_extern_plt: + callq extern_out_of_range32@plt +.Lend_test_call_extern_plt: + .size test_call_extern_plt, .Lend_test_call_extern_plt-test_call_extern_plt + # Test GOTPCREL handling. We want to check both the offset to the GOT entry and its # contents. # jitlink-check: decode_operand(test_gotpcrel, 4) = got_addr(elf_reloc.o, named_data) - next_pc(test_gotpcrel)