Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -194,6 +194,7 @@ bool ZExecstack; bool ZGlobal; bool ZHazardplt; + bool ZIfuncNoplt; bool ZInitfirst; bool ZInterpose; bool ZKeepTextSectionPrefix; Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -309,6 +309,9 @@ if (!Config->Relocatable && !Config->DefineCommon) error("-no-define-common not supported in non relocatable output"); + if (Config->ZText && Config->ZIfuncNoplt) + error("-z text and -z ifunc-noplt may not be used together"); + if (Config->Relocatable) { if (Config->Shared) error("-r and -shared may not be used together"); @@ -358,7 +361,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" || @@ -896,6 +899,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( Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/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) { Index: lld/trunk/docs/ld.lld.1 =================================================================== --- lld/trunk/docs/ld.lld.1 +++ lld/trunk/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 12, 2019 .Dt LD.LLD 1 .Os .Sh NAME @@ -552,6 +552,14 @@ .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 do not have the usual drawbacks. +This option must be combined with the +.Fl z Li notext +option. .It Cm initfirst Sets the .Dv DF_1_INITFIRST Index: lld/trunk/test/ELF/basic.s =================================================================== --- lld/trunk/test/ELF/basic.s +++ lld/trunk/test/ELF/basic.s @@ -252,3 +252,6 @@ # RUN: not ld.lld %t --thinlto-jobs=0 2>&1 | FileCheck --check-prefix=NOTHREADSTHIN %s # RUN: not ld.lld %t --plugin-opt=jobs=0 2>&1 | FileCheck --check-prefix=NOTHREADSTHIN %s # NOTHREADSTHIN: --thinlto-jobs: number of threads must be > 0 + +# RUN: not ld.lld %t -z ifunc-noplt -z text 2>&1 | FileCheck --check-prefix=NOIFUNCPLTNOTEXTREL %s +# NOIFUNCPLTNOTEXTREL: -z text and -z ifunc-noplt may not be used together Index: lld/trunk/test/ELF/gnu-ifunc-noplt-i386.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc-noplt-i386.s +++ lld/trunk/test/ELF/gnu-ifunc-noplt-i386.s @@ -0,0 +1,71 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-pc-freebsd %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-freebsd %s -o %t.o +// RUN: ld.lld -z ifunc-noplt -z notext --hash-style=sysv %t.so %t.o -o %tout +// RUN: llvm-objdump -d --no-show-raw-insn %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r --dynamic-table %tout | FileCheck %s + +// Check that we emitted relocations for the ifunc calls +// 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 ifunc call sites still require relocation +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401000 foo: +// DISASM-NEXT: 401000: retl +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401001 bar: +// DISASM-NEXT: 401001: retl +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401002 _start: +// DISASM-NEXT: 401002: calll -4 <_start+0x1> +// DISASM-NEXT: 401007: calll -4 <_start+0x6> +// DISASM-NEXT: 40100c: calll 31 +// DISASM-NEXT: 401011: calll 42 +// DISASM-EMPTY: +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401020 .plt: +// DISASM-NEXT: 401020: pushl 4206596 +// DISASM-NEXT: 401026: jmpl *4206600 +// DISASM-NEXT: 40102c: nop +// DISASM-NEXT: 40102d: nop +// DISASM-NEXT: 40102e: nop +// DISASM-NEXT: 40102f: nop +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401030 bar2@plt: +// DISASM-NEXT: 401030: jmpl *4206604 +// DISASM-NEXT: 401036: pushl $0 +// DISASM-NEXT: 40103b: jmp -32 <.plt> +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000401040 zed2@plt: +// DISASM-NEXT: 401040: jmpl *4206608 +// DISASM-NEXT: 401046: pushl $8 +// DISASM-NEXT: 40104b: 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 Index: lld/trunk/test/ELF/gnu-ifunc-noplt.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc-noplt.s +++ lld/trunk/test/ELF/gnu-ifunc-noplt.s @@ -0,0 +1,68 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-freebsd %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-pc-freebsd %s -o %t.o +// RUN: ld.lld -z ifunc-noplt -z notext --hash-style=sysv %t.so %t.o -o %tout +// RUN: llvm-objdump -d --no-show-raw-insn %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r --dynamic-table %tout | FileCheck %s + +// Check that we emitted relocations for the ifunc calls +// 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 ifunc call sites still require relocation +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201000 foo: +// DISASM-NEXT: 201000: retq +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201001 bar: +// DISASM-NEXT: 201001: retq +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201002 _start: +// DISASM-NEXT: 201002: callq 0 <_start+0x5> +// DISASM-NEXT: 201007: callq 0 <_start+0xa> +// DISASM-NEXT: 20100c: callq 31 +// DISASM-NEXT: 201011: callq 42 +// DISASM-EMPTY: +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201020 .plt: +// DISASM-NEXT: 201020: pushq 8162(%rip) +// DISASM-NEXT: 201026: jmpq *8164(%rip) +// DISASM-NEXT: 20102c: nopl (%rax) +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201030 bar2@plt: +// DISASM-NEXT: 201030: jmpq *8162(%rip) +// DISASM-NEXT: 201036: pushq $0 +// DISASM-NEXT: 20103b: jmp -32 <.plt> +// DISASM-EMPTY: +// DISASM-NEXT: 0000000000201040 zed2@plt: +// DISASM-NEXT: 201040: jmpq *8154(%rip) +// DISASM-NEXT: 201046: pushq $1 +// DISASM-NEXT: 20104b: 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