svn commit: r338251 - in head: contrib/llvm/tools/lld/ELF usr.bin/clang/lld

Mark Johnston markj at FreeBSD.org
Thu Aug 23 14:58:21 UTC 2018


Author: markj
Date: Thu Aug 23 14:58:19 2018
New Revision: 338251
URL: https://svnweb.freebsd.org/changeset/base/338251

Log:
  Add an lld option to emit PC-relative relocations for ifunc calls.
  
  The current kernel ifunc implementation creates a PLT entry for each
  ifunc definition.  ifunc calls therefore consist of a call to the
  PLT entry followed by an indirect jump.  The jump target is written
  during boot when the kernel linker resolves R_[*]_IRELATIVE relocations.
  This implementation is defined by requirements for userland code, where
  text relocations are avoided.  This requirement is not present for the
  kernel, so the implementation has avoidable overhead (namely, an extra
  indirect jump per call).
  
  Address this for now by adding a special option to the static linker
  to inhibit PLT creation for ifuncs.  Instead, relocations to ifunc call
  sites are passed through to the output file, so the kernel linker can
  enumerate such call sites and apply PC-relative relocations directly
  to the text section.  Thus the overhead of an ifunc call becomes exactly
  the same as that of an ordinary function call.  This option is only for
  use by the kernel and will not work for regular programs.
  
  The final form of this optimization is up for debate; for now, this
  change is simple and static enough to be acceptable as an interim
  solution.
  
  Reviewed by:	emaste
  Discussed with:	arichardson, dim
  MFC after:	1 month
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D16748

Modified:
  head/contrib/llvm/tools/lld/ELF/Config.h
  head/contrib/llvm/tools/lld/ELF/Driver.cpp
  head/contrib/llvm/tools/lld/ELF/Relocations.cpp
  head/contrib/llvm/tools/lld/ELF/Writer.cpp
  head/usr.bin/clang/lld/ld.lld.1

Modified: head/contrib/llvm/tools/lld/ELF/Config.h
==============================================================================
--- head/contrib/llvm/tools/lld/ELF/Config.h	Thu Aug 23 13:38:38 2018	(r338250)
+++ head/contrib/llvm/tools/lld/ELF/Config.h	Thu Aug 23 14:58:19 2018	(r338251)
@@ -155,6 +155,7 @@ struct Configuration {
   bool ZCombreloc;
   bool ZExecstack;
   bool ZHazardplt;
+  bool ZIfuncnoplt;
   bool ZNocopyreloc;
   bool ZNodelete;
   bool ZNodlopen;

Modified: head/contrib/llvm/tools/lld/ELF/Driver.cpp
==============================================================================
--- head/contrib/llvm/tools/lld/ELF/Driver.cpp	Thu Aug 23 13:38:38 2018	(r338250)
+++ head/contrib/llvm/tools/lld/ELF/Driver.cpp	Thu Aug 23 14:58:19 2018	(r338251)
@@ -669,6 +669,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args
   Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
   Config->ZExecstack = hasZOption(Args, "execstack");
   Config->ZHazardplt = hasZOption(Args, "hazardplt");
+  Config->ZIfuncnoplt = hasZOption(Args, "ifunc-noplt");
   Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc");
   Config->ZNodelete = hasZOption(Args, "nodelete");
   Config->ZNodlopen = hasZOption(Args, "nodlopen");

Modified: head/contrib/llvm/tools/lld/ELF/Relocations.cpp
==============================================================================
--- head/contrib/llvm/tools/lld/ELF/Relocations.cpp	Thu Aug 23 13:38:38 2018	(r338250)
+++ head/contrib/llvm/tools/lld/ELF/Relocations.cpp	Thu Aug 23 14:58:19 2018	(r338251)
@@ -374,6 +374,9 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelTyp
                      R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT>(E))
     return true;
 
+  if (Sym.isGnuIFunc() && Config->ZIfuncnoplt)
+    return false;
+
   // These never do, except if the entire file is position dependent or if
   // only the low bits are used.
   if (E == R_GOT || E == R_PLT || E == R_TLSDESC)
@@ -921,7 +924,9 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef
     // Strenghten or relax a PLT access.
     //
     // GNU ifunc symbols must be accessed via PLT because their addresses
-    // are determined by runtime.
+    // are determined by runtime. If the -z ifunc-noplt option is specified,
+    // we permit the optimization of ifunc calls by omitting the PLT entry
+    // and preserving relocations at ifunc call sites.
     //
     // On the other hand, if we know that a PLT entry will be resolved within
     // the same ELF module, we can skip PLT access and directly jump to the
@@ -929,7 +934,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef
     // 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() && !Config->ZIfuncnoplt)
       Expr = toPlt(Expr);
     else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym))
       Expr =
@@ -1031,6 +1036,16 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef
     // uses Elf_Rel, since in that case the written value is the addend.
     if (IsConstant) {
       Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+      continue;
+    }
+
+    // Preserve relocations against ifuncs if we were asked to do so.
+    if (Sym.isGnuIFunc() && Config->ZIfuncnoplt) {
+      if (Config->IsRela)
+        InX::RelaDyn->addReloc({Type, &Sec, Offset, false, &Sym, Addend});
+      else
+        // Preserve the existing addend.
+        InX::RelaDyn->addReloc({Type, &Sec, Offset, false, &Sym, 0});
       continue;
     }
 

Modified: head/contrib/llvm/tools/lld/ELF/Writer.cpp
==============================================================================
--- head/contrib/llvm/tools/lld/ELF/Writer.cpp	Thu Aug 23 13:38:38 2018	(r338250)
+++ head/contrib/llvm/tools/lld/ELF/Writer.cpp	Thu Aug 23 14:58:19 2018	(r338251)
@@ -1400,8 +1400,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSecti
   applySynthetic({InX::EhFrame},
                  [](SyntheticSection *SS) { SS->finalizeContents(); });
 
-  for (Symbol *S : Symtab->getSymbols())
+  for (Symbol *S : Symtab->getSymbols()) {
     S->IsPreemptible |= computeIsPreemptible(*S);
+    if (S->isGnuIFunc() && Config->ZIfuncnoplt)
+      S->ExportDynamic = true;
+  }
 
   // Scan relocations. This must be done after every symbol is declared so that
   // we can correctly decide if a dynamic relocation is needed.

Modified: head/usr.bin/clang/lld/ld.lld.1
==============================================================================
--- head/usr.bin/clang/lld/ld.lld.1	Thu Aug 23 13:38:38 2018	(r338250)
+++ head/usr.bin/clang/lld/ld.lld.1	Thu Aug 23 14:58:19 2018	(r338251)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 7, 2018
+.Dd August 22, 2018
 .Dt LD.LLD 1
 .Os
 .Sh NAME
@@ -443,6 +443,13 @@ Make the main stack executable.
 Stack permissions are recorded in the
 .Dv PT_GNU_STACK
 segment.
+.It Cm ifunc-noplt
+Do not emit PLT entries for GNU ifuncs.
+Instead, preserve relocations for ifunc call sites so that they may
+be applied by a run-time loader.
+Note that this feature requires special loader support and will
+generally result in application crashes when used outside of freestanding
+environments.
 .It Cm muldefs
 Do not error if a symbol is defined multiple times.
 The first definition will be used.


More information about the svn-src-head mailing list