COFF: Implement DLL symbol forwarding.

DLL export tables usually contain dllexport'ed symbol RVAs so that
applications which use the DLLs can find symbols from the DLLs.
However, there's a minor feature to "forward" DLL symbols to other
DLLs.

If you set an RVA to a string whose form is "<dllname>.<symbolname>"
(e.g. "KERNEL32.ExitProcess") instead of symbol RVA to the export
table, the loader interprets that as a forwarder symbol, and resolve
that symbol from the specified DLL.

This patch implements that feature.

llvm-svn: 257243
This commit is contained in:
Rui Ueyama 2016-01-09 01:22:00 +00:00
parent 22861aeab8
commit 84425d7289
5 changed files with 63 additions and 7 deletions

View File

@ -25,6 +25,7 @@ using llvm::COFF::WindowsSubsystem;
using llvm::StringRef;
class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
class Undefined;
// Short aliases.
@ -42,6 +43,12 @@ struct Export {
bool Data = false;
bool Private = false;
// If an export is a form of /export:foo=dllname.bar, that means
// that foo should be exported as an alias to bar in the DLL.
// ForwardTo is set to "dllname.bar" part. Usually empty.
StringRef ForwardTo;
StringChunk *ForwardChunk = nullptr;
// True if this /export option was in .drectves section.
bool Directives = false;
StringRef SymbolName;

View File

@ -320,8 +320,12 @@ public:
void writeTo(uint8_t *Buf) const override {
for (Export &E : Config->Exports) {
auto *D = cast<Defined>(E.Sym->repl());
write32le(Buf + OutputSectionOff + E.Ordinal * 4, D->getRVA());
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
if (E.ForwardChunk) {
write32le(P, E.ForwardChunk->getRVA());
} else {
write32le(P, cast<Defined>(E.Sym->repl())->getRVA());
}
}
}
@ -539,6 +543,15 @@ EdataContents::EdataContents() {
for (Export &E : Config->Exports)
if (!E.Noname)
Names.push_back(new StringChunk(E.ExportName));
std::vector<Chunk *> Forwards;
for (Export &E : Config->Exports) {
if (E.ForwardTo.empty())
continue;
E.ForwardChunk = new StringChunk(E.ForwardTo);
Forwards.push_back(E.ForwardChunk);
}
auto *NameTab = new NamePointersChunk(Names);
auto *OrdinalTab = new ExportOrdinalChunk(Names.size());
auto *Dir = new ExportDirectoryChunk(MaxOrdinal, Names.size(), DLLName,
@ -550,6 +563,8 @@ EdataContents::EdataContents() {
Chunks.push_back(std::unique_ptr<Chunk>(OrdinalTab));
for (Chunk *C : Names)
Chunks.push_back(std::unique_ptr<Chunk>(C));
for (Chunk *C : Forwards)
Chunks.push_back(std::unique_ptr<Chunk>(C));
}
} // namespace coff

View File

@ -586,6 +586,8 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// Windows specific -- Make sure we resolve all dllexported symbols.
for (Export &E : Config->Exports) {
if (!E.ForwardTo.empty())
continue;
E.Sym = addUndefined(E.Name);
if (!E.Directives)
Symtab.mangleMaybe(E.Sym);

View File

@ -321,7 +321,8 @@ void createSideBySideManifest() {
}
// Parse a string in the form of
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]".
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
// or "<name>=<dllname>.<name>".
// Used for parsing /export arguments.
Export parseExport(StringRef Arg) {
Export E;
@ -329,12 +330,25 @@ Export parseExport(StringRef Arg) {
std::tie(E.Name, Rest) = Arg.split(",");
if (E.Name.empty())
goto err;
if (E.Name.find('=') != StringRef::npos) {
std::tie(E.ExtName, E.Name) = E.Name.split("=");
StringRef X, Y;
std::tie(X, Y) = E.Name.split("=");
// If "<name>=<dllname>.<name>".
if (Y.find(".") != StringRef::npos) {
E.Name = X;
E.ForwardTo = Y;
return E;
}
E.ExtName = X;
E.Name = Y;
if (E.Name.empty())
goto err;
}
// If "<name>=<internalname>[,@ordinal[,NONAME]][,DATA][,PRIVATE]"
while (!Rest.empty()) {
StringRef Tok;
std::tie(Tok, Rest) = Rest.split(",");
@ -388,15 +402,22 @@ void fixupExports() {
}
for (Export &E : Config->Exports) {
if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
if (!E.ForwardTo.empty()) {
E.SymbolName = E.Name;
} else if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
E.SymbolName = U->getName();
} else {
E.SymbolName = E.Sym->getName();
}
}
for (Export &E : Config->Exports)
E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
for (Export &E : Config->Exports) {
if (!E.ForwardTo.empty()) {
E.ExportName = undecorate(E.Name);
} else {
E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
}
}
// Uniquefy by name.
std::map<StringRef, Export *> Map;

View File

@ -80,3 +80,14 @@ SYMTAB: __imp_exportfn2 in export.test.tmp.DLL
SYMTAB: exportfn2 in export.test.tmp.DLL
SYMTAB: __imp_exportfn3 in export.test.tmp.DLL
SYMTAB: exportfn3 in export.test.tmp.DLL
# RUN: lld-link /out:%t.dll /dll %t.obj /export:foo=kernel32.foobar
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=FORWARDER %s
FORWARDER: Export Table:
FORWARDER: DLL name: export.test.tmp.dll
FORWARDER: Ordinal base: 0
FORWARDER: Ordinal RVA Name
FORWARDER: 0 0
FORWARDER: 1 0x1010 exportfn
FORWARDER: 2 0x2062 foo