diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -193,6 +193,7 @@ bool ZExecstack; bool ZGlobal; bool ZHazardplt; + bool ZIfuncnoplt; bool ZInitfirst; bool ZInterpose; bool ZKeepTextSectionPrefix; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -357,7 +357,7 @@ static bool isKnownZFlag(StringRef S) { return S == "combreloc" || S == "copyreloc" || S == "defs" || S == "execstack" || S == "global" || S == "hazardplt" || - S == "initfirst" || S == "interpose" || + S == "ifunc-noplt" || S == "initfirst" || S == "interpose" || S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || S == "nodelete" || S == "nodlopen" || S == "noexecstack" || @@ -888,6 +888,7 @@ Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); Config->ZGlobal = hasZOption(Args, "global"); Config->ZHazardplt = hasZOption(Args, "hazardplt"); + Config->ZIfuncnoplt = hasZOption(Args, "ifunc-noplt"); Config->ZInitfirst = hasZOption(Args, "initfirst"); Config->ZInterpose = hasZOption(Args, "interpose"); Config->ZKeepTextSectionPrefix = getZFlag( diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1066,7 +1066,7 @@ // be resolved within the executable will actually be resolved that way at // runtime, because the main exectuable is always at the beginning of a search // list. We can leverage that fact. - if (!Sym.IsPreemptible && !Sym.isGnuIFunc()) { + if (!Sym.IsPreemptible && (!Sym.isGnuIFunc() || Config->ZIfuncnoplt)) { if (Expr == R_GOT_PC && !isAbsoluteValue(Sym)) Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); else @@ -1094,6 +1094,14 @@ return; } + // We were asked not to generate PLT entries for ifuncs. Instead, pass the + // direct relocation on through. + if (Sym.isGnuIFunc() && Config->ZIfuncnoplt) { + Sym.ExportDynamic = true; + In.RelaDyn->addReloc(Type, &Sec, Offset, &Sym, Addend, R_ADDEND, Type); + return; + } + // Non-preemptible ifuncs require special handling. First, handle the usual // case where the symbol isn't one of these. if (!Sym.isGnuIFunc() || Sym.IsPreemptible) { diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -4,7 +4,7 @@ .\" .\" This man page documents only lld's ELF linking support, obtained originally .\" from FreeBSD. -.Dd February 26, 2019 +.Dd May 6, 2019 .Dt LD.LLD 1 .Os .Sh NAME @@ -546,6 +546,11 @@ .Dv DYNAMIC section. Different loaders can decide how to handle this flag on their own. +.It Cm ifunc-noplt +Do not emit PLT entries for ifunc symbols. +Instead, emit text relocations referencing the resolver. +This is an experimental optimization and only suitable for standalone +environments where text relocations are permitted. .It Cm initfirst Sets the .Dv DF_1_INITFIRST diff --git a/lld/test/ELF/gnu-ifunc-noplt-i386.s b/lld/test/ELF/gnu-ifunc-noplt-i386.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/gnu-ifunc-noplt-i386.s @@ -0,0 +1,69 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/shared2-x86-64.s -o %t1.o +// RUN: ld.lld %t1.o --shared -o %t.so +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld -zifunc-noplt --hash-style=sysv %t.so %t.o -o %tout +// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r --dynamic-table %tout | FileCheck %s + +// Check that the IRELATIVE relocations are after the JUMP_SLOT in the plt +// CHECK: Relocations [ +// CHECK-NEXT: Section (4) .rel.dyn { +// CHECK-NEXT: 0x401008 R_386_PLT32 bar +// CHECK-NEXT: 0x401003 R_386_PLT32 foo +// CHECK-NEXT: } +// CHECK-NEXT: Section (5) .rel.plt { +// CHECK-NEXT: 0x40300C R_386_JUMP_SLOT bar2 +// CHECK-NEXT: 0x403010 R_386_JUMP_SLOT zed2 +// CHECK-NEXT: } + +// Check that a PLT header is written and the ifunc entries appear last +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: foo: +// DISASM-NEXT: 401000: c3 retl +// DISASM: bar: +// DISASM-NEXT: 401001: c3 retl +// DISASM: _start: +// DISASM-NEXT: 401002: e8 fc ff ff ff calll -4 +// DISASM-NEXT: 401007: e8 fc ff ff ff calll -4 +// DISASM-NEXT: 40100c: e8 1f 00 00 00 calll 31 +// DISASM-NEXT: 401011: e8 2a 00 00 00 calll 42 +// DISASM-EMPTY: +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-EMPTY: +// DISASM-NEXT: .plt: +// DISASM-NEXT: 401020: ff 35 04 30 40 00 pushl 4206596 +// DISASM-NEXT: 401026: ff 25 08 30 40 00 jmpl *4206600 +// DISASM-NEXT: 40102c: 90 nop +// DISASM-NEXT: 40102d: 90 nop +// DISASM-NEXT: 40102e: 90 nop +// DISASM-NEXT: 40102f: 90 nop +// DISASM-EMPTY: +// DISASM-NEXT: bar2@plt: +// DISASM-NEXT: 401030: ff 25 0c 30 40 00 jmpl *4206604 +// DISASM-NEXT: 401036: 68 00 00 00 00 pushl $0 +// DISASM-NEXT: 40103b: e9 e0 ff ff ff jmp -32 <.plt> +// DISASM-EMPTY: +// DISASM-NEXT: zed2@plt: +// DISASM-NEXT: 401040: ff 25 10 30 40 00 jmpl *4206608 +// DISASM-NEXT: 401046: 68 08 00 00 00 pushl $8 +// DISASM-NEXT: 40104b: e9 d0 ff ff ff jmp -48 <.plt> + +.text +.type foo STT_GNU_IFUNC +.globl foo +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +bar: + ret + +.globl _start +_start: + call foo@plt + call bar@plt + call bar2@plt + call zed2@plt diff --git a/lld/test/ELF/gnu-ifunc-noplt.s b/lld/test/ELF/gnu-ifunc-noplt.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/gnu-ifunc-noplt.s @@ -0,0 +1,66 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/shared2-x86-64.s -o %t1.o +// RUN: ld.lld %t1.o --shared -o %t.so +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +// RUN: ld.lld -zifunc-noplt --hash-style=sysv %t.so %t.o -o %tout +// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r --dynamic-table %tout | FileCheck %s + +// Check that the IRELATIVE relocations are after the JUMP_SLOT in the plt XXX +// CHECK: Relocations [ +// CHECK-NEXT: Section (4) .rela.dyn { +// CHECK-NEXT: 0x201008 R_X86_64_PLT32 bar 0xFFFFFFFFFFFFFFFC +// CHECK-NEXT: 0x201003 R_X86_64_PLT32 foo 0xFFFFFFFFFFFFFFFC +// CHECK-NEXT: } +// CHECK-NEXT: Section (5) .rela.plt { +// CHECK-NEXT: 0x203018 R_X86_64_JUMP_SLOT bar2 0x0 +// CHECK-NEXT: 0x203020 R_X86_64_JUMP_SLOT zed2 0x0 +// CHECK-NEXT: } + +// Check that a PLT header is written and the ifunc entries appear last XXX +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: foo: +// DISASM-NEXT: 201000: c3 retq +// DISASM: bar: +// DISASM-NEXT: 201001: c3 retq +// DISASM: _start: +// DISASM-NEXT: 201002: e8 00 00 00 00 callq 0 +// DISASM-NEXT: 201007: e8 00 00 00 00 callq 0 +// DISASM-NEXT: 20100c: e8 1f 00 00 00 callq 31 +// DISASM-NEXT: 201011: e8 2a 00 00 00 callq 42 +// DISASM-EMPTY: +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-EMPTY: +// DISASM-NEXT: .plt: +// DISASM-NEXT: 201020: ff 35 e2 1f 00 00 pushq 8162(%rip) +// DISASM-NEXT: 201026: ff 25 e4 1f 00 00 jmpq *8164(%rip) +// DISASM-NEXT: 20102c: 0f 1f 40 00 nopl (%rax) +// DISASM-EMPTY: +// DISASM-NEXT: bar2@plt: +// DISASM-NEXT: 201030: ff 25 e2 1f 00 00 jmpq *8162(%rip) +// DISASM-NEXT: 201036: 68 00 00 00 00 pushq $0 +// DISASM-NEXT: 20103b: e9 e0 ff ff ff jmp -32 <.plt> +// DISASM-EMPTY: +// DISASM-NEXT: zed2@plt: +// DISASM-NEXT: 201040: ff 25 da 1f 00 00 jmpq *8154(%rip) +// DISASM-NEXT: 201046: 68 01 00 00 00 pushq $1 +// DISASM-NEXT: 20104b: e9 d0 ff ff ff jmp -48 <.plt> + +.text +.type foo STT_GNU_IFUNC +.globl foo +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +bar: + ret + +.globl _start +_start: + call foo + call bar + call bar2 + call zed2