diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 92d2282a6fe6..f9fc10c28b90 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -176,6 +176,7 @@ struct Configuration { bool UseAndroidRelrTags = false; bool WarnBackrefs; bool WarnCommon; + bool WarnIfuncTextrel; bool WarnMissingEntry; bool WarnSymbolOrdering; bool WriteAddends; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 34bfba8c941a..b01fd6dcdd81 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -857,6 +857,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->WarnBackrefs = Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); + Config->WarnIfuncTextrel = + Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); Config->WarnSymbolOrdering = Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 49f486325be9..67d3dc608602 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -352,6 +352,10 @@ defm warn_common: B<"warn-common", "Warn about duplicate common symbols", "Do not warn about duplicate common symbols (default)">; +defm warn_ifunc_textrel: B<"warn-ifunc-textrel", + "Warn about using ifunc symbols with text relocations", + "Do not warn about using ifunc symbols with text relocations (default)">; + defm warn_symbol_ordering: B<"warn-symbol-ordering", "Warn about problems with the symbol ordering file (default)", "Do not warn about problems with the symbol ordering file">; diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 139cd3923de3..8e4ea1de471d 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -50,6 +50,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" +#include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/SmallSet.h" @@ -979,12 +980,22 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, // all dynamic symbols that can 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.isGnuIFunc()) + if (Sym.isGnuIFunc()) { + if (!Config->ZText && Config->WarnIfuncTextrel) { + warn("using ifunc symbols when text relocations are allowed may produce " + "a binary that will segfault, if the object file is linked with " + "old version of glibc (glibc 2.28 and earlier). If this applies to " + "you, consider recompiling the object files without -fPIC and " + "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to " + "turn off this warning." + + getLocation(Sec, Sym, Offset)); + } Expr = toPlt(Expr); - else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) + } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) { Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); - else if (!Sym.IsPreemptible) + } else if (!Sym.IsPreemptible) { Expr = fromPlt(Expr); + } // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 5094253419db..0a520f22d928 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -446,6 +446,17 @@ This can be used to ensure linker invocation remains compatible with traditional Unix-like linkers. .It Fl -warn-common Warn about duplicate common symbols. +.It Fl -warn-ifunc-textrel +Warn about using ifunc symbols in conjunction with text relocations. +Older versions of glibc library (2.28 and earlier) has a bug that causes +the segment that includes ifunc symbols to be marked as not executable when +they are relocated. As a result, although the program compiles and links +successfully, it gives segmentation fault when the instruction pointer reaches +an ifunc symbol. Use -warn-ifunc-textrel to let lld give a warning, if the +code may include ifunc symbols, may do text relocations and be linked with +an older glibc version. Otherwise, there is no need to use it, as the default +value does not give a warning. This flag has been introduced in late 2018, +has no counter part in ld and gold linkers, and may be removed in the future. .It Fl -warn-unresolved-symbols Report unresolved symbols as warnings. .It Fl -whole-archive diff --git a/lld/test/ELF/textrel.s b/lld/test/ELF/textrel.s new file mode 100644 index 000000000000..fac67d542af7 --- /dev/null +++ b/lld/test/ELF/textrel.s @@ -0,0 +1,40 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu %s -o %t.o + +# Without --warn-text-ifunc, lld should run fine: +# RUN: ld.lld -z notext %t.o -o %t2 + +# With --warn-text-ifunc, lld should run with warnings: +# RUN: ld.lld --warn-ifunc-textrel -z notext %t.o -o /dev/null 2>&1 | FileCheck %s +# CHECK: using ifunc symbols when text relocations are allowed may produce +# CHECK-SAME: a binary that will segfault, if the object file is linked with +# CHECK-SAME: old version of glibc (glibc 2.28 and earlier). If this applies to +# CHECK-SAME: you, consider recompiling the object files without -fPIC and +# CHECK-SAME: without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to +# CHECK-SAME: turn off this warning. +# CHECK: >>> defined in {{.*}} +# CHECK: >>> referenced by {{.*}}:(.text+0x8) + +# Without text relocations, lld should run fine: +# RUN: ld.lld --fatal-warnings %t.o -o /dev/null + +.text +.globl a_func_impl +a_func_impl: + nop + +.globl selector +.type selector,@function +selector: + movl $a_func_impl, %eax + retq + +.globl a_func +.type a_func,@gnu_indirect_function +.set a_func, selector + +.globl _start +.type _start,@function +main: + callq a_func + retq