[lld][WebAssembly] Add `--export-if-defined`
authorSam Clegg <sbc@chromium.org>
Mon, 5 Apr 2021 15:00:30 +0000 (08:00 -0700)
committerSam Clegg <sbc@chromium.org>
Thu, 29 Apr 2021 17:58:45 +0000 (10:58 -0700)
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

lld/docs/WebAssembly.rst
lld/test/wasm/export-if-defined.s [new file with mode: 0644]
lld/wasm/Config.h
lld/wasm/Driver.cpp
lld/wasm/Options.td
lld/wasm/Writer.cpp

index 36062f5..749f580 100644 (file)
@@ -152,7 +152,8 @@ the ``WASM_SYMBOL_EXPORTED`` flag are exported by default.  In LLVM the
 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 (file)
index 0000000..395a5ea
--- /dev/null
@@ -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
index b91c3d9..796610e 100644 (file)
@@ -73,6 +73,7 @@ struct Configuration {
 
   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;
index 8e14546..095aef5 100644 (file)
@@ -865,9 +865,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   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 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // 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 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   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;
   }
index 8e9ebeb..6b4c2f4 100644 (file)
@@ -151,6 +151,9 @@ def allow_undefined_file_s: Separate<["-"], "allow-undefined-file">,
 
 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)">;
 
index 3a7104e..62df038 100644 (file)
@@ -1397,17 +1397,22 @@ void Writer::run() {
     }
   }
 
+  for (auto &pair : config->exportedSymbols) {
+    Symbol *sym = symtab->find(pair.first());
+    if (sym && sym->isDefined())
+      sym->forceExport = true;
+  }
+
   // Delay reporting error about explict exports until after addStartStopSymbols
   // which can create optional symbols.
-  for (auto &entry : config->exportedSymbols) {
-    StringRef name = entry.first();
+  for (auto &name : config->requiredExports) {
     Symbol *sym = symtab->find(name);
-    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);
+    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) {