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/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,9 +77,8 @@ static void undefine(Symbol *s) { if (auto f = dyn_cast(s)) - replaceSymbol(f, f->getName(), f->getName(), - defaultModule, 0, - f->getFile(), f->signature); + replaceSymbol(f, f->getName(), "", "", 0, f->getFile(), + f->signature); else if (isa(s)) replaceSymbol(s, s->getName(), 0, s->getFile()); else 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,36 @@ return s; } +// This function get called when an undefined symbol is added, and there is +// already an existing one in the symbols table. In this case we check that +// custom 'import-module' and 'import-field' symbol attributes agree. +// With LTO these attributes are not avialable when the bitcode is read and only +// become available when the LTO object is read. In this case we silently +// replace the empty attributes with the valid ones. +template +static void setImportAttributes(T *existing, StringRef importName, + StringRef importModule, InputFile *file) { + if (!importName.empty()) { + if (existing->importName.empty()) + existing->importName = importName; + if (existing->importName != importName) + error("import name mismatch for symbol: " + toString(*existing) + + "\n>>> defined as " + existing->importName + " in " + + toString(existing->getFile()) + "\n>>> defined as " + importName + + " in " + toString(file)); + } + + if (!importModule.empty()) { + if (existing->importModule.empty()) + existing->importModule = importModule; + if (existing->importModule != importModule) + error("import module mismatch for symbol: " + toString(*existing) + + "\n>>> defined as " + existing->importModule + " in " + + toString(existing->getFile()) + "\n>>> defined as " + importModule + + " in " + toString(file)); + } +} + Symbol *SymbolTable::addUndefinedFunction(StringRef name, StringRef importName, StringRef importModule, uint32_t flags, InputFile *file, @@ -424,10 +454,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 +466,8 @@ else if (getFunctionVariant(s, sig, file, &s)) replaceSym(); } + if (existingUndefined) + setImportAttributes(existingUndefined, importName, importModule, file); } return s;