From a6f406480a223068875602fb46e7b1db74873564 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 5 Apr 2021 08:00:30 -0700 Subject: [PATCH] [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 --- lld/docs/WebAssembly.rst | 3 ++- lld/test/wasm/export-if-defined.s | 37 +++++++++++++++++++++++++++++++++++++ lld/wasm/Config.h | 1 + lld/wasm/Driver.cpp | 15 ++++++++++----- lld/wasm/Options.td | 3 +++ lld/wasm/Writer.cpp | 21 +++++++++++++-------- 6 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 lld/test/wasm/export-if-defined.s 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 @@ 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 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 @@ struct Configuration { llvm::StringSet<> allowUndefinedSymbols; llvm::StringSet<> exportedSymbols; + std::vector requiredExports; std::vector searchPaths; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::Optional> 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 @@ void LinkerDriver::linkerMain(ArrayRef 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 argsArr) { // Handle the `--export ` 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 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; } 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 @@ 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)">; 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 @@ 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) { -- 2.7.4