[WebAssembly] Don't error when --undefined symbols are not found

This matches the behavior of the ELF linker where -u/--undefined
means symbols will get pulled in from archives but won't result
in link error if they are missing.

Also, don't actually great symbol table entries for the undefined
symbols, again matching more closely the ELF linker.

This also results in simplification of the code.

Differential Revision: https://reviews.llvm.org/D50279

llvm-svn: 338938
This commit is contained in:
Sam Clegg 2018-08-04 00:04:06 +00:00
parent efab30c73e
commit 47e2b6b29e
14 changed files with 71 additions and 93 deletions

View File

@ -105,9 +105,6 @@ define void @call_ptr(i64 (i64)* %arg) {
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: bar
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
@ -117,6 +114,9 @@ define void @call_ptr(i64 (i64)* %arg) {
; CHECK-NEXT: - Name: foo
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: indirect_func
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3

View File

@ -32,12 +32,12 @@ define void @_start() {
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: _Z3fooi
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0

View File

@ -1,17 +1,19 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: not wasm-ld --undefined _Z3fooi \
; RUN: -o %t.wasm %t.o 2>&1 | FileCheck %s
; RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s
; CHECK: error: undefined symbol: foo(int)
; CHECK: error: {{.*}}.o: undefined symbol: foo(int)
; RUN: not wasm-ld --no-demangle --undefined _Z3fooi \
; RUN: -o %t.wasm %t.o 2>&1 | FileCheck -check-prefix=CHECK-NODEMANGLE %s
; RUN: not wasm-ld --no-demangle \
; RUN: -o %t.wasm %t.o 2>&1 | FileCheck -check-prefix=CHECK-NODEMANGLE %s
; CHECK-NODEMANGLE: error: undefined symbol: _Z3fooi
; CHECK-NODEMANGLE: error: {{.*}}.o: undefined symbol: _Z3fooi
target triple = "wasm32-unknown-unknown"
declare void @_Z3fooi(i32);
define hidden void @_start() local_unnamed_addr {
entry:
call void @_Z3fooi(i32 1)
ret void
}

View File

@ -42,7 +42,7 @@ entry:
; CHECK-NOT: - Name: internal_func
; EXPORT: - Type: EXPORT
; EXPORT: - Name: _start
; EXPORT: - Name: bar
; EXPORT: - Name: foo
; EXPORT: - Name: _start
; EXPORT-NOT: - Name: internal_func

View File

@ -28,10 +28,10 @@ entry:
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: hidden_function
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Type: CODE

View File

@ -18,9 +18,9 @@
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Name: _start
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: ret32
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Name: ret64
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Name: ret32
; CHECK-NEXT: ...
; NO-LOAD: Name: name
@ -33,9 +33,6 @@
; NO-LOAD-NEXT: Name: ret64
; NO-LOAD-NEXT: ...
; Verify that referencing a symbol that doesn't exist won't work
; RUN: not wasm-ld %t.start.o -o %t.wasm -u symboldoesnotexist 2>&1 | FileCheck -check-prefix=CHECK-UNDEFINED1 %s
; CHECK-UNDEFINED1: error: undefined symbol: symboldoesnotexist
; RUN: not wasm-ld %t.start.o -o %t.wasm --undefined symboldoesnotexist --allow-undefined 2>&1 | FileCheck -check-prefix=CHECK-UNDEFINED2 %s
; CHECK-UNDEFINED2: symbol forced with --undefined not found: symboldoesnotexist
; Verify that referencing a symbol that is not found doesn't result in a link
; failure. This matches the behaviour of the ELF linker.
; RUN: wasm-ld %t.start.o -o %t.wasm -u symboldoesnotexist

View File

@ -29,10 +29,10 @@ entry:
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: hidden_function
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Type: CODE

View File

@ -1,11 +1,9 @@
RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
RUN: not wasm-ld -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s
RUN: not wasm-ld --allow-undefined -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s
RUN: not wasm-ld -entry=foo -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s -check-prefix=CHECK-CUSTOM
RUN: not wasm-ld --allow-undefined -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s -check-prefix=CHECK-ALLOW
CHECK: error: undefined symbol: _start
CHECK-CUSTOM: error: undefined symbol: foo
CHECK-ALLOW: error: entry symbol not defined (pass --no-entry to supress):
_start
CHECK: error: entry symbol not defined (pass --no-entry to supress): _start
CHECK-CUSTOM: error: entry symbol not defined (pass --no-entry to supress): foo
RUN: wasm-ld --no-entry -o %t.wasm %t.ret32.o

View File

@ -1,10 +1,10 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: wasm-ld --allow-undefined -o %t.wasm %t.o
; Fails due to undefined 'foo' and also 'baz'
; Fails due to undefined 'foo'
; RUN: not wasm-ld --undefined=baz -o %t.wasm %t.o 2>&1 | FileCheck %s
; CHECK: error: {{.*}}.o: undefined symbol: foo
; CHECK: error: undefined symbol: baz
; CHECK-NOT: undefined symbol: baz
; Succeeds if we pass a file containing 'foo' as --allow-undefined-file.
; RUN: echo 'foo' > %t.txt

View File

@ -43,12 +43,12 @@ entry:
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: objectDefault
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: archiveDefault
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 5

View File

@ -74,12 +74,12 @@ entry:
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: alias_fn
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: direct_fn
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3

View File

@ -81,15 +81,15 @@ entry:
; CHECK-NEXT: - Name: __data_end
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: get_address_of_foo
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: get_address_of_global_var
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0

View File

@ -329,14 +329,19 @@ static void handleWeakUndefines() {
}
// Force Sym to be entered in the output. Used for -u or equivalent.
static Symbol *addUndefined(StringRef Name) {
Symbol *S = Symtab->addUndefinedFunction(Name, 0, nullptr, nullptr);
static Symbol *handleUndefined(StringRef Name) {
Symbol *Sym = Symtab->find(Name);
if (!Sym)
return nullptr;
// Since symbol S may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
S->IsUsedInRegularObj = true;
Sym->IsUsedInRegularObj = true;
return S;
if (auto *LazySym = dyn_cast<LazySymbol>(Sym))
LazySym->fetch();
return Sym;
}
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
@ -462,15 +467,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
WasmSym::DsoHandle = Symtab->addSyntheticDataSymbol(
"__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
// For now, since we don't actually use the start function as the
// wasm start symbol, we don't need to care about it signature.
if (!Config->Entry.empty())
EntrySym = addUndefined(Config->Entry);
// Handle the `--undefined <sym>` options.
for (auto *Arg : Args.filtered(OPT_undefined))
addUndefined(Arg->getValue());
}
createFiles(Args);
@ -484,10 +480,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (errorCount())
return;
// Add synthetic dummies for weak undefined functions.
if (!Config->Relocatable)
// Handle the `--undefined <sym>` options.
for (auto *Arg : Args.filtered(OPT_undefined))
handleUndefined(Arg->getValue());
if (!Config->Relocatable) {
// Add synthetic dummies for weak undefined functions.
handleWeakUndefines();
if (!Config->Entry.empty()) {
EntrySym = handleUndefined(Config->Entry);
if (!EntrySym)
error("entry symbol not defined (pass --no-entry to supress): " +
Config->Entry);
}
// Make sure we have resolved all symbols.
if (!Config->AllowUndefined)
Symtab->reportRemainingUndefines();
}
if (errorCount())
return;
// Handle --export.
for (auto *Arg : Args.filtered(OPT_export)) {
StringRef Name = Arg->getValue();
@ -504,27 +519,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (errorCount())
return;
// Make sure we have resolved all symbols.
if (!Config->Relocatable && !Config->AllowUndefined) {
Symtab->reportRemainingUndefines();
} else {
// Even when using --allow-undefined we still want to report the absence of
// our initial set of undefined symbols (i.e. the entry point and symbols
// specified via --undefined).
// Part of the reason for this is that these function don't have signatures
// so which means they cannot be written as wasm function imports.
for (auto *Arg : Args.filtered(OPT_undefined)) {
Symbol *Sym = Symtab->find(Arg->getValue());
if (!Sym->isDefined())
error("symbol forced with --undefined not found: " + Sym->getName());
}
if (EntrySym && !EntrySym->isDefined())
error("entry symbol not defined (pass --no-entry to supress): " +
EntrySym->getName());
}
if (errorCount())
return;
if (EntrySym)
EntrySym->setHidden(false);

View File

@ -60,7 +60,6 @@ void SymbolTable::addCombinedLTOObject() {
}
void SymbolTable::reportRemainingUndefines() {
SetVector<Symbol *> Undefs;
for (Symbol *Sym : SymVector) {
if (!Sym->isUndefined() || Sym->isWeak())
continue;
@ -68,20 +67,8 @@ void SymbolTable::reportRemainingUndefines() {
continue;
if (!Sym->IsUsedInRegularObj)
continue;
Undefs.insert(Sym);
error(toString(Sym->getFile()) + ": undefined symbol: " + toString(*Sym));
}
if (Undefs.empty())
return;
for (ObjFile *File : ObjectFiles)
for (Symbol *Sym : File->getSymbols())
if (Undefs.count(Sym))
error(toString(File) + ": undefined symbol: " + toString(*Sym));
for (Symbol *Sym : Undefs)
if (!Sym->getFile())
error("undefined symbol: " + toString(*Sym));
}
Symbol *SymbolTable::find(StringRef Name) {