diff --git a/lld/test/wasm/shared-weak-undefined.s b/lld/test/wasm/shared-weak-undefined.s --- a/lld/test/wasm/shared-weak-undefined.s +++ b/lld/test/wasm/shared-weak-undefined.s @@ -3,11 +3,24 @@ # RUN: obj2yaml %t.wasm | FileCheck %s # RUN: llvm-objdump -d %t.wasm | FileCheck %s -check-prefix=ASM +# Run the same test but include a definition of ret32 in a library file. +# This verifies that LazySymbols (those found in library archives) are correctly +# demoted to undefined symbols in the final link when they are only weakly +# referenced. +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.ret32.o %p/Inputs/ret32.s +# RUN: rm -f %T/libret32.a +# RUN: llvm-ar cru %T/libret32.a %t.ret32.o +# RUN: wasm-ld --experimental-pic -shared -o %t.ret32.wasm %t.o %T/libret32.a +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump -d %t.wasm | FileCheck %s -check-prefix=ASM + # Verify the weak undefined symbols are marked as such in the # dylink section. .weak weak_func .functype weak_func () -> (i32) +.weak ret32 +.functype ret32 (f32) -> (i32) .globl call_weak call_weak: @@ -18,6 +31,19 @@ end_function # ASM-NEXT: 0b end +# This function is defined in library archive, but since our reference to it +# is weak we don't expect this definition to be used. Instead we expect it to +# act like an undefined reference and result in an imported function. +.globl call_weak_libfunc +call_weak_libfunc: +# ASM: : + .functype call_weak_libfunc () -> (i32) + f32.const 1.0 + call ret32 +# ASM: 10 81 80 80 80 00 call 1 + end_function +# ASM-NEXT: 0b end + # CHECK: Sections: # CHECK-NEXT: - Type: CUSTOM # CHECK-NEXT: Name: dylink.0 @@ -30,3 +56,6 @@ # CHECK-NEXT: - Module: env # CHECK-NEXT: Field: weak_func # CHECK-NEXT: Flags: [ BINDING_WEAK, UNDEFINED ] +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: ret32 +# CHECK-NEXT: Flags: [ BINDING_WEAK, UNDEFINED ] diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -570,6 +570,21 @@ } } +// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker +static void demoteLazySymbols() { + for (Symbol *sym : symtab->getSymbols()) { + if (auto* s = dyn_cast(sym)) { + if (s->signature) { + LLVM_DEBUG(llvm::dbgs() + << "demoting lazy func: " << s->getName() << "\n"); + replaceSymbol(s, s->getName(), None, None, + WASM_SYMBOL_BINDING_WEAK, s->getFile(), + s->signature); + } + } + } +} + static UndefinedGlobal * createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) { auto *sym = cast(symtab->addUndefinedGlobal( @@ -1030,6 +1045,9 @@ // collection. splitSections(); + // Any remaining lazy symbols should be demoted to Undefined + demoteLazySymbols(); + // Do size optimizations: garbage collection markLive(); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -112,6 +112,7 @@ sym->canInline = true; sym->traced = trace; sym->forceExport = false; + sym->referenced = !config->gcSections; symVector.emplace_back(sym); return {sym, true}; } diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -500,7 +500,7 @@ MemoryBufferRef getMemberBuffer(); // Lazy symbols can have a signature because they can replace an - // UndefinedFunction which which case we need to be able to preserve the + // UndefinedFunction in which case we need to be able to preserve the // signature. // TODO(sbc): This repetition of the signature field is inelegant. Revisit // the use of class hierarchy to represent symbol taxonomy. @@ -649,6 +649,7 @@ s2->forceExport = symCopy.forceExport; s2->canInline = symCopy.canInline; s2->traced = symCopy.traced; + s2->referenced = symCopy.referenced; // Print out a log message if --trace-symbol was specified. // This is for debugging.