mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-03 11:23:58 +00:00
[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:
parent
efab30c73e
commit
47e2b6b29e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user