[lld] r246424 - COFF: Improve dllexported name mangling compatibility.

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 31 01:43:23 PDT 2015


Author: ruiu
Date: Mon Aug 31 03:43:21 2015
New Revision: 246424

URL: http://llvm.org/viewvc/llvm-project?rev=246424&view=rev
Log:
COFF: Improve dllexported name mangling compatibility.

The rules for dllexported symbols are overly complicated due to
x86 name decoration, fuzzy symbol resolution, and the fact that
one symbol can be resolved by so many different names. The rules
are probably intended to be "intuitive", so that users don't have
to understand the name mangling schemes, but it seems that it can
lead to unintended symbol exports.

To make it clear what I'm trying to do with this patch, let me
write how the export rules are subtle and complicated.

 - x86 name decoration: If machine type is i386 and export name
   is given by a command line option, like /export:foo, the
   real symbol name the linker has to search for is _foo because
   all symbols are decorated with "_" prefixes. This doesn't happen
   on non-x86 machines. This automatic name decoration happens only
   when the name is not C++ mangled.

   However, the symbol name exported from DLLs are ones without "_"
   on all platforms.

   Moreover, if the option is given via .drectve section, no
   symbol decoration is done (the reason being that the .drectve
   section is created by a compiler and the compiler should always
   know the exact name of the symbol, I guess).

 - Fuzzy symbol resolution: In addition to x86 name decoration,
   the linker has to look for cdecl or C++ mangled symbols
   for a given /export. For example, it searches for not only
   _foo but also _foo@<number> or ??foo at ... for /export:foo.

Previous implementation didn't get it right. I'm trying to make
it as compatible with MSVC linker as possible with this patch
however the rules are. The new code looks a bit messy to me, but
I don't think it can be simpler due to the ad-hoc-ness of the rules.

Modified:
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/DLL.cpp
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/DriverUtils.cpp
    lld/trunk/COFF/ModuleDef.cpp
    lld/trunk/test/COFF/dll.test
    lld/trunk/test/COFF/export32.test

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Mon Aug 31 03:43:21 2015
@@ -36,14 +36,17 @@ static const auto I386 = llvm::COFF::IMA
 struct Export {
   StringRef Name;       // N in /export:N or /export:E=N
   StringRef ExtName;    // E in /export:E=N
-  StringRef ExtDLLName; // Symbol name written to a DLL export table
-  StringRef ExtLibName; // Symbol name written to a import library
   Undefined *Sym = nullptr;
   uint16_t Ordinal = 0;
   bool Noname = false;
   bool Data = false;
   bool Private = false;
 
+  // True if this /export option was in .drectves section.
+  bool Directives = false;
+  StringRef SymbolName;
+  StringRef ExportName; // Name in DLL
+
   bool operator==(const Export &E) {
     return (Name == E.Name && ExtName == E.ExtName &&
             Ordinal == E.Ordinal && Noname == E.Noname &&

Modified: lld/trunk/COFF/DLL.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DLL.cpp?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/COFF/DLL.cpp (original)
+++ lld/trunk/COFF/DLL.cpp Mon Aug 31 03:43:21 2015
@@ -539,7 +539,7 @@ EdataContents::EdataContents() {
   std::vector<Chunk *> Names;
   for (Export &E : Config->Exports)
     if (!E.Noname)
-      Names.push_back(new StringChunk(E.ExtDLLName));
+      Names.push_back(new StringChunk(E.ExportName));
   auto *NameTab = new NamePointersChunk(Names);
   auto *OrdinalTab = new ExportOrdinalChunk(Names.size());
   auto *Dir = new ExportDirectoryChunk(MaxOrdinal, Names.size(), DLLName,

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Mon Aug 31 03:43:21 2015
@@ -81,6 +81,10 @@ static std::unique_ptr<InputFile> create
   return std::unique_ptr<InputFile>(new ObjectFile(MB));
 }
 
+static bool isDecorated(StringRef Sym) {
+  return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
+}
+
 // Parses .drectve section contents and returns a list of files
 // specified by /defaultlib.
 void LinkerDriver::parseDirectives(StringRef S) {
@@ -99,8 +103,7 @@ void LinkerDriver::parseDirectives(Strin
       break;
     case OPT_export: {
       Export E = parseExport(Arg->getValue());
-      if (Config->Machine == I386 && E.ExtName.startswith("_"))
-        E.ExtName = E.ExtName.substr(1);
+      E.Directives = true;
       Config->Exports.push_back(E);
       break;
     }
@@ -520,8 +523,12 @@ void LinkerDriver::link(llvm::ArrayRef<c
   // Handle /export
   for (auto *Arg : Args.filtered(OPT_export)) {
     Export E = parseExport(Arg->getValue());
-    if (Config->Machine == I386 && !E.Name.startswith("_@?"))
-      E.Name = mangle(E.Name);
+    if (Config->Machine == I386) {
+      if (!isDecorated(E.Name))
+        E.Name = Alloc.save("_" + E.Name);
+      if (!E.ExtName.empty() && !isDecorated(E.ExtName))
+        E.ExtName = Alloc.save("_" + E.ExtName);
+    }
     Config->Exports.push_back(E);
   }
 
@@ -574,7 +581,8 @@ void LinkerDriver::link(llvm::ArrayRef<c
     // Windows specific -- Make sure we resolve all dllexported symbols.
     for (Export &E : Config->Exports) {
       E.Sym = addUndefined(E.Name);
-      Symtab.mangleMaybe(E.Sym);
+      if (!E.Directives)
+        Symtab.mangleMaybe(E.Sym);
     }
 
     // Add weak aliases. Weak aliases is a mechanism to give remaining
@@ -626,7 +634,7 @@ void LinkerDriver::link(llvm::ArrayRef<c
 
   // Windows specific -- when we are creating a .dll file, we also
   // need to create a .lib file.
-  if (!Config->Exports.empty()) {
+  if (Config->DLL) {
     fixupExports();
     writeImportLibrary();
     assignExportOrdinals();

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Mon Aug 31 03:43:21 2015
@@ -370,6 +370,12 @@ err:
   error(Twine("invalid /export: ") + Arg);
 }
 
+static StringRef undecorate(StringRef Sym) {
+  if (Config->Machine != I386)
+    return Sym;
+  return Sym.startswith("_") ? Sym.substr(1) : Sym;
+}
+
 // Performs error checking on all /export arguments.
 // It also sets ordinals.
 void fixupExports() {
@@ -379,30 +385,25 @@ void fixupExports() {
     if (E.Ordinal == 0)
       continue;
     if (!Ords.insert(E.Ordinal).second)
-      error(Twine("duplicate export ordinal: ") + E.Name);
+      error("duplicate export ordinal: " + E.Name);
   }
 
   for (Export &E : Config->Exports) {
-    if (!E.ExtName.empty()) {
-      E.ExtDLLName = E.ExtName;
-      E.ExtLibName = E.ExtName;
-      continue;
-    }
-    StringRef S = E.Sym->repl()->getName();
-    if (Config->Machine == I386 && S.startswith("_")) {
-      E.ExtDLLName = S.substr(1).split('@').first;
-      E.ExtLibName = S.substr(1);
-      continue;
+    if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
+      E.SymbolName = U->getName();
+    } else {
+      E.SymbolName = E.Sym->getName();
     }
-    E.ExtDLLName = S;
-    E.ExtLibName = S;
   }
 
+  for (Export &E : Config->Exports)
+    E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
+
   // Uniquefy by name.
   std::map<StringRef, Export *> Map;
   std::vector<Export> V;
   for (Export &E : Config->Exports) {
-    auto Pair = Map.insert(std::make_pair(E.ExtLibName, &E));
+    auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
     bool Inserted = Pair.second;
     if (Inserted) {
       V.push_back(E);
@@ -418,7 +419,7 @@ void fixupExports() {
   // Sort by name.
   std::sort(Config->Exports.begin(), Config->Exports.end(),
             [](const Export &A, const Export &B) {
-              return A.ExtDLLName < B.ExtDLLName;
+              return A.ExportName < B.ExportName;
             });
 }
 
@@ -535,12 +536,8 @@ class ShortImportCreator {
 public:
   ShortImportCreator(object::Archive *A, StringRef S) : Parent(A), DLLName(S) {}
 
-  NewArchiveIterator create(const Export &E) {
-    std::string Sym = E.ExtLibName;
-    bool Decorated =
-        E.ExtLibName.startswith("?") || E.ExtLibName.startswith("@");
-    if (Config->Machine == I386 && !Decorated)
-      Sym = (Twine("_") + Sym).str();
+  NewArchiveIterator create(StringRef Sym, uint16_t Ordinal,
+                            ImportNameType NameType, bool isData) {
     size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs
     size_t Size = sizeof(object::ArchiveMemberHeader) +
                   sizeof(coff_import_header) + ImpSize;
@@ -564,22 +561,16 @@ public:
     Imp->Sig2 = 0xFFFF;
     Imp->Machine = Config->Machine;
     Imp->SizeOfData = ImpSize;
-    if (E.Ordinal > 0)
-      Imp->OrdinalHint = E.Ordinal;
-    Imp->TypeInfo = (E.Data ? IMPORT_DATA : IMPORT_CODE);
+    if (Ordinal > 0)
+      Imp->OrdinalHint = Ordinal;
+    Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE);
     Imp->TypeInfo = IMPORT_CODE;
-    if (E.Noname) {
-      Imp->TypeInfo |= IMPORT_ORDINAL << 2;
-    } else if (Decorated) {
-      Imp->TypeInfo |= IMPORT_NAME << 2;
-    } else {
-      Imp->TypeInfo |= IMPORT_NAME_NOPREFIX << 2;
-    }
+    Imp->TypeInfo |= NameType << 2;
 
     // Write symbol name and DLL name.
-    sprintf(P, Sym.data(), Sym.size());
+    memcpy(P, Sym.data(), Sym.size());
     P += Sym.size() + 1;
-    sprintf(P, DLLName.data(), DLLName.size());
+    memcpy(P, DLLName.data(), DLLName.size());
 
     object::Archive::Child C(Parent, Buf);
     return NewArchiveIterator(C, nextFilename());
@@ -598,6 +589,20 @@ private:
   int Idx = 1;
 };
 
+static ImportNameType getNameType(StringRef Sym, StringRef ExtName) {
+  if (Sym != ExtName)
+    return IMPORT_NAME_UNDECORATE;
+  if (Config->Machine == I386 && Sym.startswith("_"))
+    return IMPORT_NAME_NOPREFIX;
+  return IMPORT_NAME;
+}
+
+static std::string replace(StringRef S, StringRef From, StringRef To) {
+  size_t Pos = S.find(From);
+  assert(Pos != StringRef::npos);
+  return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
+}
+
 // Creates an import library for a DLL. In this function, we first
 // create an empty import library using lib.exe and then adds short
 // import files to that file.
@@ -610,9 +615,18 @@ void writeImportLibrary() {
 
   std::string DLLName = llvm::sys::path::filename(Config->OutputFile);
   ShortImportCreator ShortImport(&Archive, DLLName);
-  for (Export &E : Config->Exports)
-    if (!E.Private)
-      Members.push_back(ShortImport.create(E));
+  for (Export &E : Config->Exports) {
+    if (E.Private)
+      continue;
+    if (E.ExtName.empty()) {
+      Members.push_back(ShortImport.create(
+          E.SymbolName, E.Ordinal, getNameType(E.SymbolName, E.Name), E.Data));
+    } else {
+      Members.push_back(ShortImport.create(
+          replace(E.SymbolName, E.Name, E.ExtName), E.Ordinal,
+          getNameType(E.SymbolName, E.Name), E.Data));
+    }
+  }
 
   std::string Path = getImplibPath();
   std::pair<StringRef, std::error_code> Result =

Modified: lld/trunk/COFF/ModuleDef.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/ModuleDef.cpp?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/COFF/ModuleDef.cpp (original)
+++ lld/trunk/COFF/ModuleDef.cpp Mon Aug 31 03:43:21 2015
@@ -54,6 +54,10 @@ struct Token {
   StringRef Value;
 };
 
+static bool isDecorated(StringRef Sym) {
+  return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
+}
+
 class Lexer {
 public:
   explicit Lexer(StringRef S) : Buf(S) {}
@@ -191,8 +195,12 @@ private:
       unget();
     }
 
-    if (Config->Machine == I386 && !E.Name.startswith("_@?"))
-      E.Name = Alloc->save("_" + E.Name);
+    if (Config->Machine == I386) {
+      if (!isDecorated(E.Name))
+        E.Name = Alloc->save("_" + E.Name);
+      if (!E.ExtName.empty() && !isDecorated(E.ExtName))
+        E.ExtName = Alloc->save("_" + E.ExtName);
+    }
 
     for (;;) {
       read();

Modified: lld/trunk/test/COFF/dll.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/dll.test?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/test/COFF/dll.test (original)
+++ lld/trunk/test/COFF/dll.test Mon Aug 31 03:43:21 2015
@@ -10,10 +10,10 @@ EXPORT:      Export Table:
 EXPORT:      DLL name: dll.test.tmp.dll
 EXPORT:      Ordinal      RVA  Name
 EXPORT-NEXT:       0        0
-EXPORT-NEXT:       1   0x1010  ?mangled@@YAHXZ
-EXPORT-NEXT:       2   0x1008  exportfn1
-EXPORT-NEXT:       3   0x1010  exportfn2
-EXPORT-NEXT:       4   0x1010  exportfn3
+EXPORT-NEXT:       1   0x1008  exportfn1
+EXPORT-NEXT:       2   0x1010  exportfn2
+EXPORT-NEXT:       3   0x1010  exportfn3
+EXPORT-NEXT:       4   0x1010  mangled
 
 # RUN: yaml2obj < %p/Inputs/export2.yaml > %t5.obj
 # RUN: rm -f %t5.lib
@@ -25,8 +25,8 @@ EXPORT2:      Export Table:
 EXPORT2:      DLL name: dll.test.tmp5.dll
 EXPORT2:      Ordinal      RVA  Name
 EXPORT2-NEXT:       0        0
-EXPORT2-NEXT:       1   0x101c  ?mangled2@@YAHXZ
-EXPORT2-NEXT:       2   0x1010  exportfn3
+EXPORT2-NEXT:       1   0x1010  exportfn3
+EXPORT2-NEXT:       2   0x101c  mangled2
 
 # RUN: llvm-as -o %t.lto.obj %p/Inputs/export.ll
 # RUN: lld-link /out:%t.lto.dll /dll %t.lto.obj /export:exportfn1 /export:exportfn2

Modified: lld/trunk/test/COFF/export32.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/export32.test?rev=246424&r1=246423&r2=246424&view=diff
==============================================================================
--- lld/trunk/test/COFF/export32.test (original)
+++ lld/trunk/test/COFF/export32.test Mon Aug 31 03:43:21 2015
@@ -24,9 +24,9 @@
 # CHECK2-NEXT:       3        0
 # CHECK2-NEXT:       4        0
 # CHECK2-NEXT:       5   0x1008  exportfn1
-# CHECK2-NEXT:       6   0x1010  ?mangled@@YAHXZ
-# CHECK2-NEXT:       7   0x1010  exportfn2
-# CHECK2-NEXT:       8   0x1010  exportfn3
+# CHECK2-NEXT:       6   0x1010  exportfn2
+# CHECK2-NEXT:       7   0x1010  exportfn3
+# CHECK2-NEXT:       8   0x1010  mangled
 
 # RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1, at 5,noname /export:exportfn2
 # RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK3 %s




More information about the llvm-commits mailing list