[ELF] - Implemented --symbol-ordering-file option.
authorGeorge Rimar <grimar@accesssoftek.com>
Thu, 10 Nov 2016 09:05:20 +0000 (09:05 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Thu, 10 Nov 2016 09:05:20 +0000 (09:05 +0000)
Patch allows to pass a symbols file to linker.
LLD will map symbols to sections and sort sections
in output according to symbol ordering file.

That can help to reduce the startup time and/or
amount of pagefaults during startup.

Also, interesting benchmark result was produced by Rafael EspĂ­ndola.
After applying the symbols file for clang he timed compiling
X86MCTargetDesc.ii to an object file.

The page faults went from just
56,988 to 56,946 since most faults are not in the binary.
Running time went from 4.403053515 to 4.178112244.
The speedup seems to be because of better cache
locality.

Differential revision: https://reviews.llvm.org/D26130

llvm-svn: 286440

lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/Options.td
lld/ELF/OutputSections.cpp
lld/ELF/OutputSections.h
lld/ELF/Relocations.cpp
lld/ELF/Symbols.cpp
lld/ELF/Symbols.h
lld/ELF/Writer.cpp
lld/test/ELF/symbol-ordering-file.s [new file with mode: 0644]

index 52b4efcd4191949a6b2d5485399b66a1ca4d1b9b..384884e86ec71ac8173cea164e33bc7ba098a155 100644 (file)
@@ -10,6 +10,7 @@
 #ifndef LLD_ELF_CONFIG_H
 #define LLD_ELF_CONFIG_H
 
+#include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ELF.h"
@@ -71,6 +72,7 @@ struct VersionDefinition {
 struct Configuration {
   InputFile *FirstElf = nullptr;
   uint8_t OSABI = 0;
+  llvm::DenseMap<llvm::CachedHashStringRef, unsigned> SymbolOrderingFile;
   llvm::StringMap<uint64_t> SectionStartMap;
   llvm::StringRef DynamicLinker;
   llvm::StringRef Entry;
index 40bfc2b4d1da3eaf27281dcb6199fabf69e3a95a..d60fd7db7db6cc245430fbb0ee90b85ae883feca 100644 (file)
@@ -447,6 +447,18 @@ static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
   return SortSectionPolicy::Default;
 }
 
+// Parse the --symbol-ordering-file argument. File has form:
+// symbolName1
+// [...]
+// symbolNameN
+static void parseSymbolOrderingList(MemoryBufferRef MB) {
+  unsigned I = 0;
+  SmallVector<StringRef, 0> Arr;
+  MB.getBuffer().split(Arr, '\n');
+  for (StringRef S : Arr)
+    Config->SymbolOrderingFile.insert({CachedHashStringRef(S.trim()), I++});
+}
+
 // Initializes Config members by the command line options.
 void LinkerDriver::readConfigs(opt::InputArgList &Args) {
   for (auto *Arg : Args.filtered(OPT_L))
@@ -580,6 +592,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
     if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
       parseDynamicList(*Buffer);
 
+  if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
+    if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
+      parseSymbolOrderingList(*Buffer);
+
   for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
     Config->DynamicList.push_back(Arg->getValue());
 
index c87c7c520fc73cab19b22806280c0ddc1d19aef0..6fcc7779197e7a9267e248800cb8eee1bc62eaf4 100644 (file)
@@ -177,6 +177,9 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
 
 def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
 
+def symbol_ordering_file: S<"symbol-ordering-file">,
+  HelpText<"Layout sections in the order specified by symbol file">;
+
 def sysroot: J<"sysroot=">, HelpText<"Set the system root">;
 
 def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
index 4df316e2c1cec2b0c16edd8875bd8053c36f4b6e..5e7050ff14f8f72a536d5cf4e0db648c4d1d8f0f 100644 (file)
@@ -14,6 +14,7 @@
 #include "LinkerScript.h"
 #include "Memory.h"
 #include "Strings.h"
+#include "SymbolListFile.h"
 #include "SymbolTable.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -978,26 +979,32 @@ template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
   this->Size = Off;
 }
 
-// Sorts input sections by section name suffixes, so that .foo.N comes
-// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
-// We want to keep the original order if the priorities are the same
-// because the compiler keeps the original initialization order in a
-// translation unit and we need to respect that.
-// For more detail, read the section of the GCC's manual about init_priority.
-template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
-  // Sort sections by priority.
-  typedef std::pair<int, InputSection<ELFT> *> Pair;
+template <class ELFT>
+void OutputSection<ELFT>::sort(
+    std::function<unsigned(InputSection<ELFT> *S)> Order) {
+  typedef std::pair<unsigned, InputSection<ELFT> *> Pair;
   auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
 
   std::vector<Pair> V;
   for (InputSection<ELFT> *S : Sections)
-    V.push_back({getPriority(S->Name), S});
+    V.push_back({Order(S), S});
   std::stable_sort(V.begin(), V.end(), Comp);
   Sections.clear();
   for (Pair &P : V)
     Sections.push_back(P.second);
 }
 
+// Sorts input sections by section name suffixes, so that .foo.N comes
+// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
+// We want to keep the original order if the priorities are the same
+// because the compiler keeps the original initialization order in a
+// translation unit and we need to respect that.
+// For more detail, read the section of the GCC's manual about init_priority.
+template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
+  // Sort sections by priority.
+  sort([](InputSection<ELFT> *S) { return getPriority(S->Name); });
+}
+
 // Returns true if S matches /Filename.?\.o$/.
 static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
   if (!S.endswith(".o"))
index bc4f81abfb51ed716d025e4100c61050b31a3d18..f06c2ed632d5ce97da399701d6232e4d63e46f11 100644 (file)
@@ -429,6 +429,7 @@ public:
   typedef typename ELFT::uint uintX_t;
   OutputSection(StringRef Name, uint32_t Type, uintX_t Flags);
   void addSection(InputSectionData *C) override;
+  void sort(std::function<unsigned(InputSection<ELFT> *S)> Order);
   void sortInitFini();
   void sortCtorsDtors();
   void writeTo(uint8_t *Buf) override;
index 9d254bb198bc94040ecfd07dde75eaaaa307e1c7..20995c7fd024c9ce5fd46c1b11ab9e8dd67f1c67 100644 (file)
@@ -417,16 +417,6 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol<ELFT> *SS) {
       {Target->CopyRel, Out<ELFT>::Bss, SS->OffsetInBss, false, SS, 0});
 }
 
-template <class ELFT>
-static StringRef getSymbolName(const elf::ObjectFile<ELFT> &File,
-                               SymbolBody &Body) {
-  if (Body.isLocal() && Body.getNameOffset())
-    return File.getStringTable().data() + Body.getNameOffset();
-  if (!Body.isLocal())
-    return Body.getName();
-  return "";
-}
-
 template <class ELFT>
 static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
                           bool IsWrite, RelExpr Expr, uint32_t Type,
@@ -449,7 +439,7 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
   // only memory. We can hack around it if we are producing an executable and
   // the refered symbol can be preemepted to refer to the executable.
   if (Config->Shared || (Config->Pic && !isRelExpr(Expr))) {
-    StringRef Name = getSymbolName(File, Body);
+    StringRef Name = getSymbolName(File.getStringTable(), Body);
     error("can't create dynamic relocation " + getRelName(Type) + " against " +
           (Name.empty() ? "readonly segment" : "symbol " + Name));
     return Expr;
@@ -557,7 +547,7 @@ std::string getLocation(InputSectionBase<ELFT> &S, typename ELFT::uint Offset) {
   // Find a symbol at a given location.
   DefinedRegular<ELFT> *Encl = getSymbolAt(&S, Offset);
   if (Encl && Encl->Type == STT_FUNC) {
-    StringRef Func = getSymbolName(*File, *Encl);
+    StringRef Func = getSymbolName(File->getStringTable(), *Encl);
     return SrcFile + " (function " + maybeDemangle(Func) + ")";
   }
 
index bfff4e484a79d48d9b146cd425fe004408ad011d..decf62b963a54dd0d6fa194dffda4c2cdcd8de99 100644 (file)
@@ -282,6 +282,14 @@ void elf::printTraceSymbol(Symbol *Sym) {
   outs() << B->getName() << "\n";
 }
 
+StringRef elf::getSymbolName(StringRef SymTab, SymbolBody &Body) {
+  if (Body.isLocal() && Body.getNameOffset())
+    return SymTab.data() + Body.getNameOffset();
+  if (!Body.isLocal())
+    return Body.getName();
+  return "";
+}
+
 template bool SymbolBody::hasThunk<ELF32LE>() const;
 template bool SymbolBody::hasThunk<ELF32BE>() const;
 template bool SymbolBody::hasThunk<ELF64LE>() const;
index cfb98979c3923e28eaab44e496427d0f441f87c0..192433ed4d9df8f4ae296457ad85461e3801ada8 100644 (file)
@@ -466,6 +466,8 @@ inline Symbol *SymbolBody::symbol() {
                                     offsetof(Symbol, Body));
 }
 
+StringRef getSymbolName(StringRef SymTab, SymbolBody &Body);
+
 } // namespace elf
 } // namespace lld
 
index 662baf4942ef346b357de8a107ce2ea752b1da8d..581401d94dfb71c636c76fd31f03b30a83a98127 100644 (file)
@@ -659,6 +659,38 @@ template <class ELFT> static void sortCtorsDtors(OutputSectionBase *S) {
     reinterpret_cast<OutputSection<ELFT> *>(S)->sortCtorsDtors();
 }
 
+// Sort input sections using the list provided by --symbol-ordering-file.
+template <class ELFT>
+static void sortBySymbolsOrder(ArrayRef<OutputSectionBase *> V) {
+  if (Config->SymbolOrderingFile.empty())
+    return;
+
+  // Build sections order map from symbols list.
+  DenseMap<InputSectionBase<ELFT> *, unsigned> SectionsOrder;
+  for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles()) {
+    for (SymbolBody *Body : File->getSymbols()) {
+      auto *D = dyn_cast<DefinedRegular<ELFT>>(Body);
+      if (!D || !D->Section)
+        continue;
+      StringRef SymName = getSymbolName(File->getStringTable(), *Body);
+      auto It = Config->SymbolOrderingFile.find(CachedHashString(SymName));
+      if (It == Config->SymbolOrderingFile.end())
+        continue;
+
+      auto It2 = SectionsOrder.insert({D->Section, It->second});
+      if (!It2.second)
+        It2.first->second = std::min(It->second, It2.first->second);
+    }
+  }
+
+  for (OutputSectionBase *Base : V)
+    if (OutputSection<ELFT> *Sec = dyn_cast<OutputSection<ELFT>>(Base))
+      Sec->sort([&](InputSection<ELFT> *S) {
+        auto It = SectionsOrder.find(S);
+        return It == SectionsOrder.end() ? UINT32_MAX : It->second;
+      });
+}
+
 template <class ELFT>
 void Writer<ELFT>::forEachRelSec(
     std::function<void(InputSectionBase<ELFT> &, const typename ELFT::Shdr &)>
@@ -703,6 +735,7 @@ template <class ELFT> void Writer<ELFT>::createSections() {
   for (InputSectionBase<ELFT> *IS : Symtab<ELFT>::X->Sections)
     addInputSec(IS);
 
+  sortBySymbolsOrder<ELFT>(OutputSections);
   sortInitFini<ELFT>(findSection(".init_array"));
   sortInitFini<ELFT>(findSection(".fini_array"));
   sortCtorsDtors<ELFT>(findSection(".ctors"));
diff --git a/lld/test/ELF/symbol-ordering-file.s b/lld/test/ELF/symbol-ordering-file.s
new file mode 100644 (file)
index 0000000..e729d84
--- /dev/null
@@ -0,0 +1,44 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.out
+# RUN: llvm-objdump -s %t.out| FileCheck %s --check-prefix=BEFORE
+
+# BEFORE:      Contents of section .foo:
+# BEFORE-NEXT:  11000 11223344 5566
+
+# RUN: echo "_foo4  " > %t_order.txt
+# RUN: echo "  _foo3" >> %t_order.txt
+# RUN: echo "_foo5" >> %t_order.txt
+# RUN: echo "_foo2" >> %t_order.txt
+# RUN: echo " " >> %t_order.txt
+# RUN: echo "_foo4" >> %t_order.txt
+# RUN: echo "_bar1" >> %t_order.txt
+# RUN: echo "_foo1" >> %t_order.txt
+
+# RUN: ld.lld --symbol-ordering-file %t_order.txt %t.o -o %t2.out
+# RUN: llvm-objdump -s %t2.out| FileCheck %s --check-prefix=AFTER
+
+# AFTER:      Contents of section .foo:
+# AFTER-NEXT:  11000 44335566 2211
+
+.section .foo,"ax",@progbits,unique,1
+_foo1:
+ .byte 0x11
+
+.section .foo,"ax",@progbits,unique,2
+_foo2:
+ .byte 0x22
+
+.section .foo,"ax",@progbits,unique,3
+_foo3:
+ .byte 0x33
+
+.section .foo,"ax",@progbits,unique,4
+_foo4:
+ .byte 0x44
+
+.section .foo,"ax",@progbits,unique,5
+_foo5:
+ .byte 0x55
+_bar1:
+ .byte 0x66