<div dir="ltr">MSVC link.exe actually prints out an interesting fatal error message like this for _foo and _foo@4.<div><br></div><div><div>LINK : warning LNK4022: cannot find unique match for symbol 'foo'</div><div>LINK : warning LNK4002: _foo defined in 2.obj</div><div>LINK : warning LNK4002: _foo@4 defined in 1.obj</div><div>LINK : error LNK2001: unresolved external symbol foo</div></div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Dec 3, 2014 at 10:18 PM, David Majnemer <span dir="ltr"><<a href="mailto:david.majnemer@gmail.com" target="_blank">david.majnemer@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">What happens if the object file contains 'foo' and '_foo@8' and you /export:foo?<br><div class="gmail_extra"><br><div class="gmail_quote"><div><div class="h5">On Wed, Dec 3, 2014 at 10:09 PM, Rui Ueyama <span dir="ltr"><<a href="mailto:ruiu@google.com" target="_blank">ruiu@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: ruiu<br>
Date: Thu Dec 4 00:09:39 2014<br>
New Revision: 223341<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=223341&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=223341&view=rev</a><br>
Log:<br>
[PECOFF] Improve /export compatibility.<br>
<br>
Looks like the rule of /export is more complicated than<br>
I was thinking. If /export:foo, for example, is given, and<br>
if the actual symbol name in an object file is _foo@<number>,<br>
we need to export that symbol as foo, not as the mangled name.<br>
<br>
If only /export:_foo@<number> is given, the symbol is exported<br>
as _foo@<number>.<br>
<br>
If both /export:foo and /export:_foo@<number> are given,<br>
they are considered as duplicates, and the linker needs to<br>
choose the unmangled name.<br>
<br>
The basic idea seems that the linker needs to export a symbol<br>
with the same name as given as /export.<br>
<br>
We exported mangled symbols. This patch fixes that issue.<br>
<br>
Modified:<br>
lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h<br>
lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp<br>
lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h<br>
lld/trunk/test/pecoff/export.test<br>
<br>
Modified: lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h?rev=223341&r1=223340&r2=223341&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h?rev=223341&r1=223340&r2=223341&view=diff</a><br>
==============================================================================<br>
--- lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h (original)<br>
+++ lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h Thu Dec 4 00:09:39 2014<br>
@@ -65,14 +65,17 @@ public:<br>
return getExternalName().compare(other.getExternalName()) < 0;<br>
}<br>
<br>
+ StringRef getRealName() const {<br>
+ return mangledName.empty() ? name : mangledName;<br>
+ }<br>
+<br>
StringRef getExternalName() const {<br>
- if (!externalName.empty())<br>
- return externalName;<br>
- return name;<br>
+ return externalName.empty() ? name : externalName;<br>
}<br>
<br>
std::string name;<br>
std::string externalName;<br>
+ std::string mangledName;<br>
int ordinal;<br>
bool noname;<br>
bool isData;<br>
<br>
Modified: lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp?rev=223341&r1=223340&r2=223341&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp?rev=223341&r1=223340&r2=223341&view=diff</a><br>
==============================================================================<br>
--- lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp (original)<br>
+++ lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp Thu Dec 4 00:09:39 2014<br>
@@ -26,20 +26,54 @@ using llvm::object::export_directory_tab<br>
namespace lld {<br>
namespace pecoff {<br>
<br>
+typedef PECOFFLinkingContext::ExportDesc ExportDesc;<br>
+<br>
+// dedupExports removes duplicate export entries. If two exports are<br>
+// referring the same symbol, they are considered duplicates.<br>
+// This could happen if the same symbol name is specified as an argument<br>
+// to /export more than once, or an unmangled and mangled name of the<br>
+// same symbol are given to /export. In the latter case, we choose<br>
+// unmangled (shorter) name.<br>
+static void dedupExports(PECOFFLinkingContext &ctx) {<br>
+ std::vector<ExportDesc> &exports = ctx.getDllExports();<br>
+ // Pass 1: find duplicate entries<br>
+ std::set<const ExportDesc *> dup;<br>
+ std::map<StringRef, ExportDesc *> map;<br>
+ for (ExportDesc &exp : exports) {<br>
+ if (!exp.externalName.empty())<br>
+ continue;;<br></blockquote><div><br></div></div></div><div>Extra semi-colon?</div><div><div class="h5"><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ StringRef symbol = exp.getRealName();<br>
+ auto it = map.find(symbol);<br>
+ if (it == map.end()) {<br>
+ map[symbol] = &exp;<br>
+ } else if (symbol.size() < it->second->getRealName().size()) {<br>
+ map[symbol] = &exp;<br>
+ dup.insert(it->second);<br>
+ } else {<br>
+ dup.insert(&exp);<br>
+ }<br>
+ }<br>
+ // Pass 2: remove duplicate entries<br>
+ auto pred = [&](const ExportDesc &exp) {<br>
+ return dup.count(&exp) == 1;<br>
+ };<br>
+ exports.erase(std::remove_if(exports.begin(), exports.end(), pred),<br>
+ exports.end());<br>
+}<br>
+<br>
static void assignOrdinals(PECOFFLinkingContext &ctx) {<br>
- std::vector<PECOFFLinkingContext::ExportDesc> &exports = ctx.getDllExports();<br>
+ std::vector<ExportDesc> &exports = ctx.getDllExports();<br>
int maxOrdinal = -1;<br>
- for (PECOFFLinkingContext::ExportDesc &desc : exports)<br>
+ for (ExportDesc &desc : exports)<br>
maxOrdinal = std::max(maxOrdinal, desc.ordinal);<br>
<br>
std::sort(exports.begin(), exports.end(),<br>
- [](const PECOFFLinkingContext::ExportDesc &a,<br>
- const PECOFFLinkingContext::ExportDesc &b) {<br>
+ [](const ExportDesc &a, const ExportDesc &b) {<br>
return a.getExternalName().compare(b.getExternalName()) < 0;<br>
});<br>
<br>
int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1);<br>
- for (PECOFFLinkingContext::ExportDesc &desc : exports)<br>
+ for (ExportDesc &desc : exports)<br>
if (desc.ordinal == -1)<br>
desc.ordinal = nextOrdinal++;<br>
}<br>
@@ -51,7 +85,7 @@ static bool getExportedAtoms(PECOFFLinki<br>
definedAtoms[atom->name()] = atom;<br>
<br>
for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) {<br>
- auto it = definedAtoms.find(<a href="http://desc.name" target="_blank">desc.name</a>);<br>
+ auto it = definedAtoms.find(desc.getRealName());<br>
if (it == definedAtoms.end()) {<br>
llvm::errs() << "Symbol <" << <a href="http://desc.name" target="_blank">desc.name</a><br>
<< "> is exported but not defined.\n";<br>
@@ -142,6 +176,7 @@ EdataPass::createOrdinalTable(const std:<br>
}<br>
<br>
void EdataPass::perform(std::unique_ptr<MutableFile> &file) {<br>
+ dedupExports(_ctx);<br>
assignOrdinals(_ctx);<br>
<br>
std::vector<TableEntry> entries;<br>
<br>
Modified: lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h?rev=223341&r1=223340&r2=223341&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h?rev=223341&r1=223340&r2=223341&view=diff</a><br>
==============================================================================<br>
--- lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h (original)<br>
+++ lld/trunk/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h Thu Dec 4 00:09:39 2014<br>
@@ -261,29 +261,11 @@ public:<br>
if (!findDecoratedSymbol(_ctx, _syms.get(), sym.str(), replace))<br>
return nullptr;<br>
<br>
- // We found a decorated symbol. There may be another symbol that<br>
- // has the same decorated name. If that's the case, we remove the<br>
- // duplicate item.<br>
- std::vector<ExportDesc> &exp = _ctx->getDllExports();<br>
- auto isFound = std::find_if(<br>
- exp.begin(), exp.end(),<br>
- [&](ExportDesc &e) { return e.getExternalName().equals(replace); });<br>
- if (isFound != exp.end()) {<br>
- exp.erase(<br>
- std::remove_if(exp.begin(), exp.end(),<br>
- [&](ExportDesc &e) { return <a href="http://e.name" target="_blank">e.name</a> == sym; }),<br>
- exp.end());<br>
- } else {<br>
- for (ExportDesc &e : exp) {<br>
- if (<a href="http://e.name" target="_blank">e.name</a> == sym) {<br>
- <a href="http://e.name" target="_blank">e.name</a> = replace;<br>
- break;<br>
- }<br>
- }<br>
- if (_ctx->deadStrip())<br>
- _ctx->addDeadStripRoot(_ctx->allocate(replace));<br>
- }<br>
-<br>
+ for (ExportDesc &exp : _ctx->getDllExports())<br>
+ if (<a href="http://exp.name" target="_blank">exp.name</a> == sym)<br>
+ exp.mangledName = replace;<br>
+ if (_ctx->deadStrip())<br>
+ _ctx->addDeadStripRoot(_ctx->allocate(replace));<br>
return new (_alloc) impl::SymbolRenameFile(sym, replace);<br>
}<br>
<br>
<br>
Modified: lld/trunk/test/pecoff/export.test<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lld/trunk/test/pecoff/export.test?rev=223341&r1=223340&r2=223341&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/lld/trunk/test/pecoff/export.test?rev=223341&r1=223340&r2=223341&view=diff</a><br>
==============================================================================<br>
--- lld/trunk/test/pecoff/export.test (original)<br>
+++ lld/trunk/test/pecoff/export.test Thu Dec 4 00:09:39 2014<br>
@@ -59,30 +59,31 @@ CHECK5-NEXT: 2 0x2010 exportfn<br>
CHECK6: Export Table:<br>
CHECK6: DLL name: export.test.tmp6.dll<br>
CHECK6: Ordinal RVA Name<br>
-CHECK6-NEXT: 1 0x2010 ?exportfn8@@YAXXZ<br>
+CHECK6-NEXT: 1 0x2010 exportfn3@256<br>
+CHECK6-NEXT: 2 0x2010 exportfn8<br>
<br>
-# RUN: lld -flavor link /out:%t6.dll /dll /entry:init \<br>
+# RUN: lld -flavor link /out:%t7.dll /dll /entry:init \<br>
# RUN: /export:exportfn7 /export:exportfn7@8 \<br>
# RUN: /export:exportfn8 /export:exportfn8 /export:exportfn3 -- %t.obj<br>
-# RUN: llvm-objdump -p %t6.dll | FileCheck -check-prefix=DUP %s<br>
+# RUN: llvm-objdump -p %t7.dll | FileCheck -check-prefix=DUP %s<br>
<br>
DUP: Export Table:<br>
-DUP: DLL name: export.test.tmp6.dll<br>
+DUP: DLL name: export.test.tmp7.dll<br>
DUP: Ordinal RVA Name<br>
-DUP-NEXT: 1 0x2010 ?exportfn8@@YAXXZ<br>
-DUP-NEXT: 2 0x2010 exportfn3@256<br>
-DUP-NEXT: 3 0x2010 exportfn7@8<br>
+DUP-NEXT: 1 0x2010 exportfn3<br>
+DUP-NEXT: 2 0x2010 exportfn7<br>
+DUP-NEXT: 3 0x2010 exportfn8<br>
DUP-NOT: ?exportfn8@@YAXXZ<br>
DUP-NOT: exportfn3@256<br>
<br>
# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj<br>
#<br>
-# RUN: lld -flavor link /out:%t1.dll /dll /entry:init \<br>
+# RUN: lld -flavor link /out:%t8.dll /dll /entry:init \<br>
# RUN: /export:f1=exportfn1 /export:f2@4=exportfn2,private -- %t.obj<br>
-# RUN: llvm-objdump -p %t1.dll | FileCheck -check-prefix=EQUAL %s<br>
+# RUN: llvm-objdump -p %t8.dll | FileCheck -check-prefix=EQUAL %s<br>
<br>
EQUAL: Export Table:<br>
-EQUAL: DLL name: export.test.tmp1.dll<br>
+EQUAL: DLL name: export.test.tmp8.dll<br>
EQUAL: Ordinal RVA Name<br>
EQUAL-NEXT: 1 0x2010 exportfn3@256<br>
EQUAL-NEXT: 2 0x2008 f1<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu" target="_blank">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div></div></div><br></div></div>
</blockquote></div><br></div>