[lld][WebAssembly] Add `--export-if-defined`
Unlike the existing `--export` option this will not causes errors
or warnings if the specified symbol is not defined.
See: https://github.com/emscripten-core/emscripten/issues/13736
Differential Revision: https://reviews.llvm.org/D99887
diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst
index 36062f5..749f580 100644
--- a/lld/docs/WebAssembly.rst
+++ b/lld/docs/WebAssembly.rst
@@ -152,7 +152,8 @@
in turn can be set using ``__attribute__((export_name))`` clang attribute.
In addition, symbols can be exported via the linker command line using
-``--export``.
+``--export`` (which will error if the symbol is not found) or
+``--export-if-defined`` (which will not).
Finally, just like with native ELF linker the ``--export-dynamic`` flag can be
used to export symbols in the executable which are marked as
diff --git a/lld/test/wasm/export-if-defined.s b/lld/test/wasm/export-if-defined.s
new file mode 100644
index 0000000..395a5ea
--- /dev/null
+++ b/lld/test/wasm/export-if-defined.s
@@ -0,0 +1,37 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --export-if-defined=foo -o %t1.wasm %t.o
+# RUN: obj2yaml %t1.wasm | FileCheck %s
+
+# RUN: wasm-ld --export-if-defined=bar -o %t2.wasm %t.o
+# RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=MISSING
+
+.globl foo
+foo:
+ .functype foo () -> ()
+ end_function
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
+
+# CHECK: - Type: EXPORT
+# CHECK-NEXT: Exports:
+# CHECK-NEXT: - Name: memory
+# CHECK-NEXT: Kind: MEMORY
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: foo
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: _start
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 1
+
+# MISSING: - Type: EXPORT
+# MISSING-NEXT: Exports:
+# MISSING-NEXT: - Name: memory
+# MISSING-NEXT: Kind: MEMORY
+# MISSING-NEXT: Index: 0
+# MISSING-NEXT: - Name: _start
+# MISSING-NEXT: Kind: FUNCTION
+# MISSING-NEXT: Index: 0
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index b91c3d9..796610e 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -73,6 +73,7 @@
llvm::StringSet<> allowUndefinedSymbols;
llvm::StringSet<> exportedSymbols;
+ std::vector<llvm::StringRef> requiredExports;
std::vector<llvm::StringRef> searchPaths;
llvm::CachePruningPolicy thinLTOCachePolicy;
llvm::Optional<std::vector<std::string>> features;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 8e14546..095aef5 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -865,9 +865,14 @@
for (auto *arg : args.filtered(OPT_trace_symbol))
symtab->trace(arg->getValue());
- for (auto *arg : args.filtered(OPT_export))
+ for (auto *arg : args.filtered(OPT_export_if_defined))
config->exportedSymbols.insert(arg->getValue());
+ for (auto *arg : args.filtered(OPT_export)) {
+ config->exportedSymbols.insert(arg->getValue());
+ config->requiredExports.push_back(arg->getValue());
+ }
+
createSyntheticSymbols();
// Add all files to the symbol table. This will add almost all
@@ -883,8 +888,8 @@
// Handle the `--export <sym>` options
// This works like --undefined but also exports the symbol if its found
- for (auto *arg : args.filtered(OPT_export))
- handleUndefined(arg->getValue());
+ for (auto &iter : config->exportedSymbols)
+ handleUndefined(iter.first());
Symbol *entrySym = nullptr;
if (!config->relocatable && !config->entry.empty()) {
@@ -958,8 +963,8 @@
if (!wrapped.empty())
wrapSymbols(wrapped);
- for (auto *arg : args.filtered(OPT_export)) {
- Symbol *sym = symtab->find(arg->getValue());
+ for (auto &iter : config->exportedSymbols) {
+ Symbol *sym = symtab->find(iter.first());
if (sym && sym->isDefined())
sym->forceExport = true;
}
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 8e9ebeb..6b4c2f4 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -151,6 +151,9 @@
defm export: Eq<"export", "Force a symbol to be exported">;
+defm export_if_defined: Eq<"export-if-defined",
+ "Force a symbol to be exported, if it is defined in the input">;
+
def export_all: F<"export-all">,
HelpText<"Export all symbols (normally combined with --no-gc-sections)">;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 3a7104e..62df038 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1397,17 +1397,22 @@
}
}
- // Delay reporting error about explict exports until after addStartStopSymbols
- // which can create optional symbols.
- for (auto &entry : config->exportedSymbols) {
- StringRef name = entry.first();
- Symbol *sym = symtab->find(name);
+ for (auto &pair : config->exportedSymbols) {
+ Symbol *sym = symtab->find(pair.first());
if (sym && sym->isDefined())
sym->forceExport = true;
- else if (config->unresolvedSymbols == UnresolvedPolicy::ReportError)
- error(Twine("symbol exported via --export not found: ") + name);
- else if (config->unresolvedSymbols == UnresolvedPolicy::Warn)
- warn(Twine("symbol exported via --export not found: ") + name);
+ }
+
+ // Delay reporting error about explict exports until after addStartStopSymbols
+ // which can create optional symbols.
+ for (auto &name : config->requiredExports) {
+ Symbol *sym = symtab->find(name);
+ if (!sym || !sym->isDefined()) {
+ if (config->unresolvedSymbols == UnresolvedPolicy::ReportError)
+ error(Twine("symbol exported via --export not found: ") + name);
+ if (config->unresolvedSymbols == UnresolvedPolicy::Warn)
+ warn(Twine("symbol exported via --export not found: ") + name);
+ }
}
if (config->isPic) {