ELF: Implement basic support for --version-script.

This patch only implements support for version scripts of the form:
  { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; };
No wildcards are supported, other than for the local entry. Symbol versioning
is also not supported.

It works by introducing a new Symbol flag which tracks whether a symbol
appears in the global section of a version script.

This patch also simplifies the logic in SymbolBody::isPreemptible(), and
teaches it to handle the case where symbols with default visibility in DSOs
do not appear in the dynamic symbol table because of a version script.

Fixes PR27482.

Differential Revision: http://reviews.llvm.org/D19430

llvm-svn: 267208
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 4ebfefb..f01f78a 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -5,7 +5,6 @@
 add_lld_library(lldELF
   Driver.cpp
   DriverUtils.cpp
-  DynamicList.cpp
   Error.cpp
   ICF.cpp
   InputFiles.cpp
@@ -15,6 +14,7 @@
   MarkLive.cpp
   OutputSections.cpp
   ScriptParser.cpp
+  SymbolListFile.cpp
   SymbolTable.cpp
   Symbols.cpp
   Target.cpp
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index b050d9b..d15c254 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -51,6 +51,7 @@
   std::vector<llvm::StringRef> DynamicList;
   std::vector<llvm::StringRef> SearchPaths;
   std::vector<llvm::StringRef> Undefined;
+  std::vector<llvm::StringRef> VersionScriptGlobals;
   bool AllowMultipleDefinition;
   bool AsNeeded = false;
   bool Bsymbolic;
@@ -85,6 +86,7 @@
   bool Threads;
   bool Trace;
   bool Verbose;
+  bool VersionScript;
   bool WarnCommon;
   bool ZExecStack;
   bool ZNodelete;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 64ec15a..3cfd033 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -9,11 +9,11 @@
 
 #include "Driver.h"
 #include "Config.h"
-#include "DynamicList.h"
 #include "Error.h"
 #include "ICF.h"
 #include "InputFiles.h"
 #include "LinkerScript.h"
+#include "SymbolListFile.h"
 #include "SymbolTable.h"
 #include "Target.h"
 #include "Writer.h"
@@ -146,11 +146,6 @@
   return MBRef;
 }
 
-void LinkerDriver::readDynamicList(StringRef Path) {
-  if (Optional<MemoryBufferRef> Buffer = readFile(Path))
-    parseDynamicList(*Buffer);
-}
-
 // Add a given library by searching it from input search paths.
 void LinkerDriver::addLibrary(StringRef Name) {
   std::string Path = searchLibrary(Name);
@@ -372,10 +367,18 @@
     Config->Undefined.push_back(Arg->getValue());
 
   if (Args.hasArg(OPT_dynamic_list))
-    readDynamicList(getString(Args, OPT_dynamic_list));
+    if (Optional<MemoryBufferRef> Buffer =
+            readFile(getString(Args, OPT_dynamic_list)))
+      parseDynamicList(*Buffer);
 
   for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
     Config->DynamicList.push_back(Arg->getValue());
+
+  Config->VersionScript = Args.hasArg(OPT_version_script);
+  if (Config->VersionScript)
+    if (Optional<MemoryBufferRef> Buffer =
+            readFile(getString(Args, OPT_version_script)))
+      parseVersionScript(*Buffer);
 }
 
 void LinkerDriver::createFiles(opt::InputArgList &Args) {
@@ -457,6 +460,7 @@
 
   Symtab.scanShlibUndefined();
   Symtab.scanDynamicList();
+  Symtab.scanVersionScript();
 
   Symtab.addCombinedLtoObject();
 
diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h
index 6756fab..eb42e90 100644
--- a/lld/ELF/Driver.h
+++ b/lld/ELF/Driver.h
@@ -32,7 +32,6 @@
   std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef MB);
   llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
   void readConfigs(llvm::opt::InputArgList &Args);
-  void readDynamicList(StringRef Path);
   void createFiles(llvm::opt::InputArgList &Args);
   template <class ELFT> void link(llvm::opt::InputArgList &Args);
 
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index d199b96..b4cee26 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -160,6 +160,9 @@
 def version : Flag<["--", "-"], "version">,
   HelpText<"Display the version number">;
 
+def version_script : Separate<["--", "-"], "version-script">,
+  HelpText<"Read a version script">;
+
 def warn_common : Flag<["--", "-"], "warn-common">,
   HelpText<"Warn about duplicate common symbols">;
 
@@ -234,7 +237,6 @@
 def no_warn_mismatch : Flag<["--"], "no-warn-mismatch">;
 def rpath_link : Separate<["--", "-"], "rpath-link">;
 def rpath_link_eq : Joined<["--", "-"], "rpath-link=">;
-def version_script : Separate<["--"], "version-script">;
 def warn_execstack : Flag<["--"], "warn-execstack">;
 def warn_shared_textrel : Flag<["--"], "warn-shared-textrel">;
 def G : Separate<["-"], "G">;
diff --git a/lld/ELF/DynamicList.cpp b/lld/ELF/SymbolListFile.cpp
similarity index 61%
rename from lld/ELF/DynamicList.cpp
rename to lld/ELF/SymbolListFile.cpp
index 0bfdd0f..8323933 100644
--- a/lld/ELF/DynamicList.cpp
+++ b/lld/ELF/SymbolListFile.cpp
@@ -1,4 +1,4 @@
-//===- LinkerScript.cpp ---------------------------------------------------===//
+//===- SymbolListFile.cpp -------------------------------------------------===//
 //
 //                             The LLVM Linker
 //
@@ -13,7 +13,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "DynamicList.h"
+#include "SymbolListFile.h"
 #include "Config.h"
 #include "ScriptParser.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -62,3 +62,42 @@
 void elf::parseDynamicList(MemoryBufferRef MB) {
   DynamicListParser(MB.getBuffer()).run();
 }
+
+// Parse the --version-script argument. We currently only accept the following
+// version script syntax:
+//
+//  { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; };
+//
+// No wildcards are supported, other than for the local entry. Symbol versioning
+// is also not supported.
+
+class VersionScriptParser final : public ScriptParserBase {
+public:
+  VersionScriptParser(StringRef S) : ScriptParserBase(S) {}
+
+  void run() override;
+};
+
+void VersionScriptParser::run() {
+  expect("{");
+  if (peek() == "global:") {
+    next();
+    while (!Error) {
+      Config->VersionScriptGlobals.push_back(next());
+      expect(";");
+      if (peek() == "local:")
+        break;
+    }
+  }
+  expect("local:");
+  expect("*");
+  expect(";");
+  expect("}");
+  expect(";");
+  if (!atEOF())
+    setError("expected EOF");
+}
+
+void elf::parseVersionScript(MemoryBufferRef MB) {
+  VersionScriptParser(MB.getBuffer()).run();
+}
diff --git a/lld/ELF/DynamicList.h b/lld/ELF/SymbolListFile.h
similarity index 71%
rename from lld/ELF/DynamicList.h
rename to lld/ELF/SymbolListFile.h
index 35449f6..60362a8 100644
--- a/lld/ELF/DynamicList.h
+++ b/lld/ELF/SymbolListFile.h
@@ -1,4 +1,4 @@
-//===- DynamicList.h --------------------------------------------*- C++ -*-===//
+//===- SymbolListFile.h -----------------------------------------*- C++ -*-===//
 //
 //                             The LLVM Linker
 //
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLD_ELF_DYNAMIC_LIST_H
-#define LLD_ELF_DYNAMIC_LIST_H
+#ifndef LLD_ELF_SYMBOL_LIST_FILE_H
+#define LLD_ELF_SYMBOL_LIST_FILE_H
 
 #include "lld/Core/LLVM.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -17,6 +17,7 @@
 namespace elf {
 
 void parseDynamicList(MemoryBufferRef MB);
+void parseVersionScript(MemoryBufferRef MB);
 
 } // namespace elf
 } // namespace lld
diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index 9c73a6a..a7337aa 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -290,7 +290,12 @@
   auto P = Symtab.insert(std::make_pair(Name, NumSyms));
   Symbol *Sym;
   if (P.second) {
-    Sym = new (Alloc) Symbol{New, STV_DEFAULT, false, false};
+    Sym = new (Alloc) Symbol;
+    Sym->Body = New;
+    Sym->Visibility = STV_DEFAULT;
+    Sym->IsUsedInRegularObj = false;
+    Sym->ExportDynamic = false;
+    Sym->VersionScriptGlobal = !Config->VersionScript;
     SymVector.push_back(Sym);
   } else {
     Sym = SymVector[P.first->second];
@@ -374,6 +379,15 @@
       B->Backref->ExportDynamic = true;
 }
 
+// This function processes the --version-script option by marking all global
+// symbols with the VersionScriptGlobal flag, which acts as a filter on the
+// dynamic symbol table.
+template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
+  for (StringRef S : Config->VersionScriptGlobals)
+    if (SymbolBody *B = find(S))
+      B->Backref->VersionScriptGlobal = true;
+}
+
 template class elf::SymbolTable<ELF32LE>;
 template class elf::SymbolTable<ELF32BE>;
 template class elf::SymbolTable<ELF64LE>;
diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h
index 3d7d5c1..e67bd32 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -61,6 +61,7 @@
 
   void scanShlibUndefined();
   void scanDynamicList();
+  void scanVersionScript();
   SymbolBody *find(StringRef Name);
   void wrap(StringRef Name);
   InputFile *findFile(SymbolBody *B);
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 206a523..1b82348 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -115,31 +115,26 @@
   if (isLocal())
     return false;
 
+  // Shared symbols resolve to the definition in the DSO.
   if (isShared())
     return true;
 
-  if (Backref->Visibility != STV_DEFAULT)
-    return false;
-
-  if (isUndefined()) {
-    if (!isWeak())
-      return true;
-
-    // Ideally the static linker should see a definition for every symbol, but
-    // shared object are normally allowed to have undefined references that the
-    // static linker never sees a definition for.
-    if (Config->Shared)
-      return true;
-
-    // Otherwise, just resolve to 0.
-    return false;
-  }
-
+  // That's all that can be preempted in a non-DSO.
   if (!Config->Shared)
     return false;
+
+  // Undefined symbols in DSOs can only be preempted if they are strong.
+  // Weak symbols just resolve to zero.
+  if (isUndefined())
+    return !isWeak();
+
+  // -Bsymbolic means that not even default visibility symbols can be preempted.
   if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc()))
     return false;
-  return true;
+
+  // Only default visibility symbols that appear in the dynamic symbol table can
+  // be preempted.
+  return Backref->Visibility == STV_DEFAULT && Backref->includeInDynsym();
 }
 
 template <class ELFT>
@@ -328,7 +323,7 @@
 bool Symbol::includeInDynsym() const {
   if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
     return false;
-  return ExportDynamic || Body->isShared();
+  return (ExportDynamic && VersionScriptGlobal) || Body->isShared();
 }
 
 template uint32_t SymbolBody::template getVA<ELF32LE>(uint32_t) const;
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 3b1ceea..7aed12d 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -60,6 +60,11 @@
   // --export-dynamic, and by dynamic lists.
   unsigned ExportDynamic : 1;
 
+  // This flag acts as an additional filter on the dynamic symbol list. It is
+  // set if there is no version script, or if the symbol appears in the global
+  // section of the version script.
+  unsigned VersionScriptGlobal : 1;
+
   bool includeInDynsym() const;
 };
 
diff --git a/lld/test/ELF/lto/internalize-version-script.ll b/lld/test/ELF/lto/internalize-version-script.ll
new file mode 100644
index 0000000..c25328f
--- /dev/null
+++ b/lld/test/ELF/lto/internalize-version-script.ll
@@ -0,0 +1,22 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+; RUN: echo "{ global: foo; local: *; };" > %t.script
+; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
+; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define void @foo() {
+  ret void
+}
+
+define void @bar() {
+  ret void
+}
+
+; Check that foo is not internalized.
+; CHECK: define void @foo()
+
+; Check that bar is correctly internalized.
+; CHECK: define internal void @bar()
diff --git a/lld/test/ELF/no-inhibit-exec.s b/lld/test/ELF/no-inhibit-exec.s
index 2f2a62b..128f44d 100644
--- a/lld/test/ELF/no-inhibit-exec.s
+++ b/lld/test/ELF/no-inhibit-exec.s
@@ -6,7 +6,7 @@
 
 # CHECK: Disassembly of section .text:
 # CHECK-NEXT: _start
-# CHECK-NEXT: 11000:   e8 00 00 00 00  callq   0
+# CHECK-NEXT: 11000:   e8 fb ef fe ff callq -69637
 
 # next code will not link without noinhibit-exec flag
 # because of undefined symbol _bar
diff --git a/lld/test/ELF/version-script.s b/lld/test/ELF/version-script.s
new file mode 100644
index 0000000..2878004
--- /dev/null
+++ b/lld/test/ELF/version-script.s
@@ -0,0 +1,126 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -soname shared -o %t2.so
+
+# RUN: echo "{ global: foo1; foo3; local: *; };" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld --version-script %t.script -shared %t.o %t2.so -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
+
+# RUN: echo "{ local: *; };" > %t3.script
+# RUN: ld.lld --version-script %t3.script -shared %t.o %t2.so -o %t3.so
+# RUN: llvm-readobj -dyn-symbols %t3.so | FileCheck --check-prefix=DSO2 %s
+
+# --version-script filters --dynamic-list.
+# RUN: echo "{ foo1; foo2; };" > %t.list
+# RUN: ld.lld --version-script %t.script --dynamic-list %t.list %t.o %t2.so -o %t
+# RUN: llvm-readobj -dyn-symbols %t | FileCheck --check-prefix=EXE %s
+
+# DSO:      DynamicSymbols [
+# DSO-NEXT:   Symbol {
+# DSO-NEXT:     Name: @ (0)
+# DSO-NEXT:     Value: 0x0
+# DSO-NEXT:     Size: 0
+# DSO-NEXT:     Binding: Local (0x0)
+# DSO-NEXT:     Type: None (0x0)
+# DSO-NEXT:     Other: 0
+# DSO-NEXT:     Section: Undefined (0x0)
+# DSO-NEXT:   }
+# DSO-NEXT:   Symbol {
+# DSO-NEXT:     Name: bar@ (1)
+# DSO-NEXT:     Value: 0x0
+# DSO-NEXT:     Size: 0
+# DSO-NEXT:     Binding: Global (0x1)
+# DSO-NEXT:     Type: Function (0x2)
+# DSO-NEXT:     Other: 0
+# DSO-NEXT:     Section: Undefined (0x0)
+# DSO-NEXT:   }
+# DSO-NEXT:   Symbol {
+# DSO-NEXT:     Name: foo1@ (5)
+# DSO-NEXT:     Value: 0x1000
+# DSO-NEXT:     Size: 0
+# DSO-NEXT:     Binding: Global (0x1)
+# DSO-NEXT:     Type: None (0x0)
+# DSO-NEXT:     Other: 0
+# DSO-NEXT:     Section: .text (0x5)
+# DSO-NEXT:   }
+# DSO-NEXT:   Symbol {
+# DSO-NEXT:     Name: foo3@ (10)
+# DSO-NEXT:     Value: 0x1007
+# DSO-NEXT:     Size: 0
+# DSO-NEXT:     Binding: Global (0x1)
+# DSO-NEXT:     Type: None (0x0)
+# DSO-NEXT:     Other: 0
+# DSO-NEXT:     Section: .text (0x5)
+# DSO-NEXT:   }
+# DSO-NEXT: ]
+
+# DSO2:      DynamicSymbols [
+# DSO2-NEXT:   Symbol {
+# DSO2-NEXT:     Name: @ (0)
+# DSO2-NEXT:     Value: 0x0
+# DSO2-NEXT:     Size: 0
+# DSO2-NEXT:     Binding: Local (0x0)
+# DSO2-NEXT:     Type: None (0x0)
+# DSO2-NEXT:     Other: 0
+# DSO2-NEXT:     Section: Undefined (0x0)
+# DSO2-NEXT:   }
+# DSO2-NEXT:   Symbol {
+# DSO2-NEXT:     Name: bar@ (1)
+# DSO2-NEXT:     Value: 0x0
+# DSO2-NEXT:     Size: 0
+# DSO2-NEXT:     Binding: Global (0x1)
+# DSO2-NEXT:     Type: Function (0x2)
+# DSO2-NEXT:     Other: 0
+# DSO2-NEXT:     Section: Undefined (0x0)
+# DSO2-NEXT:   }
+# DSO2-NEXT: ]
+
+# EXE:      DynamicSymbols [
+# EXE-NEXT:   Symbol {
+# EXE-NEXT:     Name: @ (0)
+# EXE-NEXT:     Value: 0x0
+# EXE-NEXT:     Size: 0
+# EXE-NEXT:     Binding: Local (0x0)
+# EXE-NEXT:     Type: None (0x0)
+# EXE-NEXT:     Other: 0
+# EXE-NEXT:     Section: Undefined (0x0)
+# EXE-NEXT:   }
+# EXE-NEXT:   Symbol {
+# EXE-NEXT:     Name: bar@ (1)
+# EXE-NEXT:     Value: 0x0
+# EXE-NEXT:     Size: 0
+# EXE-NEXT:     Binding: Global (0x1)
+# EXE-NEXT:     Type: Function (0x2)
+# EXE-NEXT:     Other: 0
+# EXE-NEXT:     Section: Undefined (0x0)
+# EXE-NEXT:   }
+# EXE-NEXT:   Symbol {
+# EXE-NEXT:     Name: foo1@ (5)
+# EXE-NEXT:     Value: 0x11000
+# EXE-NEXT:     Size: 0
+# EXE-NEXT:     Binding: Global (0x1)
+# EXE-NEXT:     Type: None (0x0)
+# EXE-NEXT:     Other: 0
+# EXE-NEXT:     Section: .text (0x5)
+# EXE-NEXT:   }
+# EXE-NEXT: ]
+
+.globl foo1
+foo1:
+  call bar@PLT
+  ret
+
+.globl foo2
+foo2:
+  ret
+
+.globl foo3
+foo3:
+  call foo2@PLT
+  ret
+
+.globl _start
+_start:
+  ret