diff --git a/lld/test/wasm/Inputs/libstub.so b/lld/test/wasm/Inputs/libstub.so --- a/lld/test/wasm/Inputs/libstub.so +++ b/lld/test/wasm/Inputs/libstub.so @@ -3,3 +3,4 @@ foo: foodep1,foodep2 # This symbols as no dependencies bar +baz: bazdep diff --git a/lld/test/wasm/stub-library-archive.s b/lld/test/wasm/stub-library-archive.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/stub-library-archive.s @@ -0,0 +1,60 @@ +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %t/main.s +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/foodeps.o %t/foodeps.s +# RUN: rm -f %t/libfoodeps.a +# RUN: llvm-ar rcs %t/libfoodeps.a %t/foodeps.o +# RUN: wasm-ld %t.o %p/Inputs/libstub.so %t/libfoodeps.a -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + +#--- main.s + +# The function foo is defined in libstub.so but depends on foodep1 and foodep2 + +# foodep1 and foodep2 a defined libfoodeps.a(foodeps.o) but this function +# depeds on baz which is also defined in libstub.so. + +.functype foo () -> () + +.globl _start +_start: + .functype _start () -> () + call foo + end_function + +.globl bazdep +bazdep: + .functype bazdep () -> () + end_function + +#--- foodeps.s + +.functype baz () -> () + +.globl foodep1 +foodep1: + .functype foodep1 () -> () + call baz + end_function + +.globl foodep2 +foodep2: + .functype foodep2 () -> () + end_function + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Name: _start +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: - Name: bazdep +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 3 +# CHECK-NEXT: - Name: foodep1 +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 4 +# CHECK-NEXT: - Name: foodep2 +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 5 diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -898,52 +898,61 @@ static void processStubLibraries() { log("-- processStubLibraries"); - for (auto &stub_file : symtab->stubFiles) { - LLVM_DEBUG(llvm::dbgs() - << "processing stub file: " << stub_file->getName() << "\n"); - for (auto [name, deps]: stub_file->symbolDependencies) { - auto* sym = symtab->find(name); - if (!sym || !sym->isUndefined()) { - LLVM_DEBUG(llvm::dbgs() << "stub symbol not needed: " << name << "\n"); - continue; - } - // The first stub library to define a given symbol sets this and - // definitions in later stub libraries are ignored. - if (sym->forceImport) - continue; // Already handled - sym->forceImport = true; - if (sym->traced) - message(toString(stub_file) + ": importing " + name); - else - LLVM_DEBUG(llvm::dbgs() - << toString(stub_file) << ": importing " << name << "\n"); - for (const auto dep : deps) { - auto* needed = symtab->find(dep); - if (!needed) { - error(toString(stub_file) + ": undefined symbol: " + dep + - ". Required by " + toString(*sym)); - } else if (needed->isUndefined()) { - error(toString(stub_file) + - ": undefined symbol: " + toString(*needed) + - ". Required by " + toString(*sym)); - } else { - if (needed->traced) - message(toString(stub_file) + ": exported " + toString(*needed) + - " due to import of " + name); + bool depsAdded = false; + do { + depsAdded = false; + for (auto &stub_file : symtab->stubFiles) { + LLVM_DEBUG(llvm::dbgs() + << "processing stub file: " << stub_file->getName() << "\n"); + for (auto [name, deps]: stub_file->symbolDependencies) { + auto* sym = symtab->find(name); + if (!sym || !sym->isUndefined()) { + if (sym && sym->traced) + message(toString(stub_file) + ": stub symbol not needed: " + name); else - LLVM_DEBUG(llvm::dbgs() - << "force export: " << toString(*needed) << "\n"); - needed->forceExport = true; - if (auto *lazy = dyn_cast(needed)) { - lazy->fetch(); - if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back(stub_file->getName(), - sym->getFile(), *sym); + LLVM_DEBUG(llvm::dbgs() << "stub symbol not needed: `" << name << "`\n"); + continue; + } + // The first stub library to define a given symbol sets this and + // definitions in later stub libraries are ignored. + if (sym->forceImport) + continue; // Already handled + sym->forceImport = true; + if (sym->traced) + message(toString(stub_file) + ": importing " + name); + else + LLVM_DEBUG(llvm::dbgs() + << toString(stub_file) << ": importing " << name << "\n"); + for (const auto dep : deps) { + auto* needed = symtab->find(dep); + if (!needed) { + error(toString(stub_file) + ": undefined symbol: " + dep + + ". Required by " + toString(*sym)); + } else if (needed->isUndefined()) { + error(toString(stub_file) + + ": undefined symbol: " + toString(*needed) + + ". Required by " + toString(*sym)); + } else { + if (needed->traced) + message(toString(stub_file) + ": exported " + toString(*needed) + + " due to import of " + name); + else + LLVM_DEBUG(llvm::dbgs() + << "force export: " << toString(*needed) << "\n"); + needed->forceExport = true; + if (auto *lazy = dyn_cast(needed)) { + depsAdded = true; + lazy->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(stub_file->getName(), + sym->getFile(), *sym); + } } } } } - } + } while (depsAdded); + log("-- done processStubLibraries"); }