diff --git a/lld/test/wasm/Inputs/import-attributes.ll b/lld/test/wasm/Inputs/import-attributes.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/Inputs/import-attributes.ll @@ -0,0 +1,10 @@ +target triple = "wasm32-unknown-unknown" + +define void @call_foo() { + call void @foo(); + ret void +} + +declare void @foo() #0 + +attributes #0 = { "wasm-import-module"="baz" } diff --git a/lld/test/wasm/import-attribute-mismatch.ll b/lld/test/wasm/import-attribute-mismatch.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/import-attribute-mismatch.ll @@ -0,0 +1,18 @@ +; RUN: llc -filetype=obj %s -o %t1.o +; RUN: llc -filetype=obj %S/Inputs/import-attributes.ll -o %t2.o +; RUN: not wasm-ld --export call_foo --allow-undefined -o %t.wasm %t1.o %t2.o 2>&1 | FileCheck %s + +target triple = "wasm32-unknown-unknown-wasm" + +define void @_start() { + call void @foo(); + ret void +} + +declare void @foo() #0 + +attributes #0 = { "wasm-import-module"="bar" } + +; CHECK: wasm-ld: error: import module mismatch for symbol: foo +; CHECK: >>> defined as bar in {{.*}}1.o +; CHECK: >>> defined as baz in {{.*}}2.o diff --git a/lld/test/wasm/lto/import-attributes.ll b/lld/test/wasm/lto/import-attributes.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/lto/import-attributes.ll @@ -0,0 +1,22 @@ +; RUN: llvm-as %s -o %t.o +; RUN: wasm-ld --allow-undefined -o %t.wasm %t.o +; RUN: obj2yaml %t.wasm | FileCheck %s + +target triple = "wasm32-unknown-unknown-wasm" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" + +define void @_start() { + call void @foo(); + ret void +} + +declare void @foo() #0 + +attributes #0 = { "wasm-import-module"="bar" "wasm-import-name"="customfoo" } + +; CHECK: - Type: IMPORT +; CHECK-NEXT: Imports: +; CHECK-NEXT: - Module: bar +; CHECK-NEXT: Field: customfoo +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: SigIndex: 0 diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -577,9 +577,16 @@ Symbol *wrap; }; -static Symbol *addUndefined(StringRef name) { - return symtab->addUndefinedFunction(name, "", "", WASM_SYMBOL_UNDEFINED, - nullptr, nullptr, false); +static Symbol *addUndefinedWrapper(Symbol* target, StringRef name) { + StringRef importModule = defaultModule; + StringRef importName = name; + if (auto uf = dyn_cast(target)) { + importModule = uf->importModule; + importName = uf->importName; + } + return symtab->addUndefinedFunction(name, importName, importModule, + WASM_SYMBOL_UNDEFINED, nullptr, nullptr, + false); } // Handles -wrap option. @@ -600,8 +607,8 @@ if (!sym) continue; - Symbol *real = addUndefined(saver.save("__real_" + name)); - Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + Symbol *real = addUndefinedWrapper(sym, saver.save("__real_" + name)); + Symbol *wrap = addUndefinedWrapper(sym, saver.save("__wrap_" + name)); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -527,8 +527,8 @@ if (objSym.isUndefined() || excludedByComdat) { flags |= WASM_SYMBOL_UNDEFINED; if (objSym.isExecutable()) - return symtab->addUndefinedFunction(name, name, defaultModule, flags, &f, - nullptr, true); + return symtab->addUndefinedFunction(name, "", "", flags, &f, nullptr, + true); return symtab->addUndefinedData(name, flags, &f); } diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp --- a/lld/wasm/LTO.cpp +++ b/lld/wasm/LTO.cpp @@ -77,8 +77,7 @@ static void undefine(Symbol *s) { if (auto f = dyn_cast(s)) - replaceSymbol(f, f->getName(), f->getName(), - defaultModule, 0, + replaceSymbol(f, f->getName(), f->getName(), "", 0, f->getFile(), f->signature); else if (isa(s)) replaceSymbol(s, s->getName(), 0, s->getFile()); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -393,6 +393,32 @@ return s; } +template +static void setImportAttributes(T *sym, StringRef importName, + StringRef importModule, InputFile *file) { + // Silently allow for empty atttibutes to be replaced with non-empty ones. + // This occurs during LTO when the initial undefined symbol in the bitcode + // is replaced with the one in the LTO object which contains the extra + // symbol attributes. + if (sym->importName.empty() && !importName.empty()) { + sym->importName = importName; + dbgs() << "setting import name: " << importName << "\n"; + } + if (sym->importModule.empty() && !importModule.empty()) { + sym->importModule = importModule; + dbgs() << "setting import module: " << importName << "\n"; + } + if (sym->importModule != importModule) + error("import module mismatch for symbol: " + toString(*sym) + + "\n>>> defined as " + sym->importModule + " in " + + toString(sym->getFile()) + "\n>>> defined as " + importModule + + " in " + toString(file)); + if (sym->importName != importName) + error("import name mismatch for symbol: " + toString(*sym) + "\n>>> defined as " + + sym->importName + " in " + toString(sym->getFile()) + + "\n>>> defined as " + importName + " in " + toString(file)); +} + Symbol *SymbolTable::addUndefinedFunction(StringRef name, StringRef importName, StringRef importModule, uint32_t flags, InputFile *file, @@ -424,10 +450,10 @@ reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); return s; } + auto* existingUndefined = dyn_cast(existingFunction); if (!existingFunction->signature && sig) existingFunction->signature = sig; if (isCalledDirectly && !signatureMatches(existingFunction, sig)) { - auto* existingUndefined = dyn_cast(existingFunction); // If the existing undefined functions is not called direcltly then let // this one take precedence. Otherwise the existing function is either // direclty called or defined, in which case we need a function variant. @@ -436,6 +462,8 @@ else if (getFunctionVariant(s, sig, file, &s)) replaceSym(); } + if (existingUndefined) + setImportAttributes(existingUndefined, importName, importModule, file); } return s; @@ -479,8 +507,11 @@ file, type); else if (auto *lazy = dyn_cast(s)) lazy->fetch(); - else if (s->isDefined()) + else if (s->isDefined()) { checkGlobalType(s, file, type); + if (auto existingUndefined = dyn_cast(s)) + setImportAttributes(existingUndefined, importName, importModule, file); + } return s; }