[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