Recommit r275257 "[ELF] - Implement extern "c++" version script tag"
authorGeorge Rimar <grimar@accesssoftek.com>
Sat, 16 Jul 2016 12:26:39 +0000 (12:26 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Sat, 16 Jul 2016 12:26:39 +0000 (12:26 +0000)
BSD toolchain contains a bug:
https://sourceforge.net/p/elftoolchain/tickets/491/

In short demangler works differently, fix was to update the testcase.
It should fix the FreeBSD bot failture:
http://lab.llvm.org:8011/builders/lld-x86_64-freebsd/builds/19432/steps/test_lld/logs/stdio

Original commit message was:
[ELF] - Implement extern "c++" version script tag

Patch implements 'extern' version script tag.
Currently only values in quotes(") are supported.

Matching of externs is performed in the same pass as exact match of globals.

Differential revision: http://reviews.llvm.org/D21930

llvm-svn: 275682

lld/ELF/Config.h
lld/ELF/SymbolListFile.cpp
lld/ELF/SymbolTable.cpp
lld/ELF/SymbolTable.h
lld/test/ELF/version-script-err.s
lld/test/ELF/version-script-extern.s [new file with mode: 0644]

index 1c874c7..a43ce33 100644 (file)
@@ -35,13 +35,18 @@ enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring };
 
 enum class UnresolvedPolicy { NoUndef, Error, Warn, Ignore };
 
+struct SymbolVersion {
+  llvm::StringRef Name;
+  bool IsExternCpp;
+};
+
 // This struct contains symbols version definition that
 // can be found in version script if it is used for link.
 struct VersionDefinition {
   VersionDefinition(llvm::StringRef Name, size_t Id) : Name(Name), Id(Id) {}
   llvm::StringRef Name;
   size_t Id;
-  std::vector<llvm::StringRef> Globals;
+  std::vector<SymbolVersion> Globals;
   size_t NameOff; // Offset in string table.
 };
 
@@ -68,7 +73,7 @@ struct Configuration {
   std::vector<llvm::StringRef> DynamicList;
   std::vector<llvm::StringRef> SearchPaths;
   std::vector<llvm::StringRef> Undefined;
-  std::vector<llvm::StringRef> VersionScriptGlobals;
+  std::vector<SymbolVersion> VersionScriptGlobals;
   std::vector<uint8_t> BuildIdVector;
   bool AllowMultipleDefinition;
   bool AsNeeded = false;
index c30726b..a283e6f 100644 (file)
@@ -69,6 +69,7 @@ public:
   void run();
 
 private:
+  void parseExtern(std::vector<SymbolVersion> *Globals);
   void parseVersion(StringRef VerStr);
   void parseGlobal(StringRef VerStr);
   void parseLocal();
@@ -106,21 +107,38 @@ void VersionScriptParser::parseLocal() {
   expect(";");
 }
 
+void VersionScriptParser::parseExtern(std::vector<SymbolVersion> *Globals) {
+  expect("extern");
+  expect("C++");
+  expect("{");
+
+  for (;;) {
+    if (peek() == "}" || Error)
+      break;
+    Globals->push_back({next(), true});
+    expect(";");
+  }
+
+  expect("}");
+  expect(";");
+}
+
 void VersionScriptParser::parseGlobal(StringRef VerStr) {
-  std::vector<StringRef> *Globals;
+  std::vector<SymbolVersion> *Globals;
   if (VerStr.empty())
     Globals = &Config->VersionScriptGlobals;
   else
     Globals = &Config->VersionDefinitions.back().Globals;
 
   for (;;) {
+    if (peek() == "extern")
+      parseExtern(Globals);
+
     StringRef Cur = peek();
-    if (Cur == "extern")
-      setError("extern keyword is not supported");
     if (Cur == "}" || Cur == "local:" || Error)
       return;
     next();
-    Globals->push_back(Cur);
+    Globals->push_back({Cur, false});
     expect(";");
   }
 }
index 0fbfab2..590f01f 100644 (file)
@@ -566,6 +566,37 @@ static bool hasWildcard(StringRef S) {
   return S.find_first_of("?*") != StringRef::npos;
 }
 
+static void setVersionId(SymbolBody *Body, StringRef VersionName,
+                         StringRef Name, uint16_t Version) {
+  if (!Body || Body->isUndefined()) {
+    if (Config->NoUndefinedVersion)
+      error("version script assignment of " + VersionName + " to symbol " +
+            Name + " failed: symbol not defined");
+    return;
+  }
+
+  Symbol *Sym = Body->symbol();
+  if (Sym->VersionId != VER_NDX_GLOBAL && Sym->VersionId != VER_NDX_LOCAL)
+    warning("duplicate symbol " + Name + " in version script");
+  Sym->VersionId = Version;
+}
+
+template <class ELFT>
+std::map<std::string, SymbolBody *> SymbolTable<ELFT>::getDemangledSyms() {
+  std::map<std::string, SymbolBody *> Result;
+  for (std::pair<SymName, unsigned> Sym : Symtab)
+    Result[demangle(Sym.first.Val)] = SymVector[Sym.second]->body();
+  return Result;
+}
+
+static bool hasExternCpp() {
+  for (VersionDefinition &V : Config->VersionDefinitions)
+    for (SymbolVersion Sym : V.Globals)
+      if (Sym.IsExternCpp)
+        return true;
+  return false;
+}
+
 // 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.
@@ -573,8 +604,8 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
   // If version script does not contain versions declarations,
   // we just should mark global symbols.
   if (!Config->VersionScriptGlobals.empty()) {
-    for (StringRef S : Config->VersionScriptGlobals)
-      if (SymbolBody *B = find(S))
+    for (SymbolVersion &Sym : Config->VersionScriptGlobals)
+      if (SymbolBody *B = find(Sym.Name))
         B->symbol()->VersionId = VER_NDX_GLOBAL;
     return;
   }
@@ -585,35 +616,32 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
   // If we have symbols version declarations, we should
   // assign version references for each symbol.
   // Current rules are:
-  // * If there is an exact match for the mangled name, we use it.
+  // * If there is an exact match for the mangled name or we have extern C++
+  //   exact match, then we use it.
   // * Otherwise, we look through the wildcard patterns. We look through the
   //   version tags in reverse order. We use the first match we find (the last
   //   matching version tag in the file).
-  for (size_t I = 0, E = Config->VersionDefinitions.size(); I < E; ++I) {
-    VersionDefinition &V = Config->VersionDefinitions[I];
-    for (StringRef Name : V.Globals) {
-      if (hasWildcard(Name))
-        continue;
-
-      SymbolBody *B = find(Name);
-      if (!B || B->isUndefined()) {
-        if (Config->NoUndefinedVersion)
-          error("version script assignment of " + V.Name + " to symbol " +
-                Name + " failed: symbol not defined");
+  // Handle exact matches and build a map of demangled externs for
+  // quick search during next step.
+  std::map<std::string, SymbolBody *> Demangled;
+  if (hasExternCpp())
+    Demangled = getDemangledSyms();
+
+  for (VersionDefinition &V : Config->VersionDefinitions) {
+    for (SymbolVersion Sym : V.Globals) {
+      if (hasWildcard(Sym.Name))
         continue;
-      }
-
-      if (B->symbol()->VersionId != Config->DefaultSymbolVersion)
-        warning("duplicate symbol " + Name + " in version script");
-      B->symbol()->VersionId = V.Id;
+      SymbolBody *B = Sym.IsExternCpp ? Demangled[Sym.Name] : find(Sym.Name);
+      setVersionId(B, V.Name, Sym.Name, V.Id);
     }
   }
 
+  // Handle wildcards.
   for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) {
     VersionDefinition &V = Config->VersionDefinitions[I];
-    for (StringRef Name : V.Globals)
-      if (hasWildcard(Name))
-        for (SymbolBody *B : findAll(Name))
+    for (SymbolVersion &Sym : V.Globals)
+      if (hasWildcard(Sym.Name))
+        for (SymbolBody *B : findAll(Sym.Name))
           if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
             B->symbol()->VersionId = V.Id;
   }
index 39a6ec9..9e26ffe 100644 (file)
@@ -97,6 +97,8 @@ private:
   std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile);
   void reportDuplicate(SymbolBody *Existing, InputFile *NewFile);
 
+  std::map<std::string, SymbolBody *> getDemangledSyms();
+
   // The order the global symbols are in is not defined. We can use an arbitrary
   // order, but it has to be reproducible. That is true even when cross linking.
   // The default hashing of StringRef produces different results on 32 and 64
index 4df15d5..15b69e9 100644 (file)
@@ -8,8 +8,3 @@
 // RUN: not ld.lld --version-script %terr1.script -shared %t.o -o %t.so 2>&1 | \
 // RUN:   FileCheck -check-prefix=ERR1 %s
 // ERR1: unclosed quote
-
-// RUN: echo "VERSION { extern "C++" {}; }; " > %terr2.script
-// RUN: not ld.lld --version-script %terr2.script -shared %t.o -o %t.so 2>&1 | \
-// RUN:   FileCheck -check-prefix=ERR2 %s
-// ERR2: extern keyword is not supported
diff --git a/lld/test/ELF/version-script-extern.s b/lld/test/ELF/version-script-extern.s
new file mode 100644 (file)
index 0000000..a75bd78
--- /dev/null
@@ -0,0 +1,98 @@
+# REQUIRES: x86
+# XFAIL: win32
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "LIBSAMPLE_1.0 { \
+# RUN:   global:             \
+# RUN:      extern "C++" {   \
+# RUN:         \"foo(int)\";    \
+# RUN:         \"zed(int)\";    \
+# RUN:   };                  \
+# RUN: };                    \
+# RUN: LIBSAMPLE_2.0 {       \
+# RUN:   global:             \
+# RUN:     extern "C++" {    \
+# RUN:       \"bar(int)\";      \
+# RUN:   };                  \
+# RUN: }; " > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
+
+# DSO:      DynamicSymbols [
+# DSO-NEXT:    Symbol {
+# DSO-NEXT:      Name: @
+# DSO-NEXT:      Value: 0x0
+# DSO-NEXT:      Size: 0
+# DSO-NEXT:      Binding: Local
+# DSO-NEXT:      Type: None
+# DSO-NEXT:      Other: 0
+# DSO-NEXT:      Section: Undefined
+# DSO-NEXT:    }
+# DSO-NEXT:    Symbol {
+# DSO-NEXT:      Name: _Z3bari@@LIBSAMPLE_2.0
+# DSO-NEXT:      Value: 0x1001
+# DSO-NEXT:      Size: 0
+# DSO-NEXT:      Binding: Global
+# DSO-NEXT:      Type: Function
+# DSO-NEXT:      Other: 0
+# DSO-NEXT:      Section: .text
+# DSO-NEXT:    }
+# DSO-NEXT:    Symbol {
+# DSO-NEXT:      Name: _Z3fooi@@LIBSAMPLE_1.0
+# DSO-NEXT:      Value: 0x1000
+# DSO-NEXT:      Size: 0
+# DSO-NEXT:      Binding: Global
+# DSO-NEXT:      Type: Function
+# DSO-NEXT:      Other: 0
+# DSO-NEXT:      Section: .text
+# DSO-NEXT:    }
+# DSO-NEXT:    Symbol {
+# DSO-NEXT:      Name: _Z3zedi@@LIBSAMPLE_1.0
+# DSO-NEXT:      Value: 0x1002
+# DSO-NEXT:      Size: 0
+# DSO-NEXT:      Binding: Global (0x1)
+# DSO-NEXT:      Type: Function (0x2)
+# DSO-NEXT:      Other: 0
+# DSO-NEXT:      Section: .text (0x6)
+# DSO-NEXT:    }
+# DSO-NEXT:  ]
+# DSO-NEXT:  Version symbols {
+# DSO-NEXT:    Section Name: .gnu.version
+# DSO-NEXT:    Address: 0x228
+# DSO-NEXT:    Offset: 0x228
+# DSO-NEXT:    Link: 1
+# DSO-NEXT:    Symbols [
+# DSO-NEXT:      Symbol {
+# DSO-NEXT:        Version: 0
+# DSO-NEXT:        Name: @
+# DSO-NEXT:      }
+# DSO-NEXT:      Symbol {
+# DSO-NEXT:        Version: 3
+# DSO-NEXT:        Name: _Z3bari@@LIBSAMPLE_2.0
+# DSO-NEXT:      }
+# DSO-NEXT:      Symbol {
+# DSO-NEXT:        Version: 2
+# DSO-NEXT:        Name: _Z3fooi@@LIBSAMPLE_1.0
+# DSO-NEXT:      }
+# DSO-NEXT:      Symbol {
+# DSO-NEXT:        Version: 2
+# DSO-NEXT:        Name: _Z3zedi@@LIBSAMPLE_1.0
+# DSO-NEXT:      }
+# DSO-NEXT:    ]
+# DSO-NEXT:  }
+
+.text
+.globl _Z3fooi
+.type _Z3fooi,@function
+_Z3fooi:
+retq
+
+.globl _Z3bari
+.type _Z3bari,@function
+_Z3bari:
+retq
+
+.globl _Z3zedi
+.type _Z3zedi,@function
+_Z3zedi:
+retq