diff --git a/lld/test/wasm/unresolved-symbols.s b/lld/test/wasm/unresolved-symbols.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/unresolved-symbols.s @@ -0,0 +1,40 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t1.o + +## Check that %t1.o contains undefined symbol undef. +# RUN: not wasm-ld %t1.o -o /dev/null 2>&1 | \ +# RUN: FileCheck -check-prefix=UNDCHECK %s +# UNDCHECK: error: {{.*}}1.o: undefined symbol: undef + +## Error out if unknown option value was set. +# RUN: not wasm-ld %t1.o -o /dev/null --unresolved-symbols=xxx 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR1 %s +# ERR1: unknown --unresolved-symbols value: xxx +## Check alias. +# RUN: not wasm-ld %t1.o -o /dev/null --unresolved-symbols xxx 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR1 %s + +## Ignore all should not produce error for symbols from object except +## case when --no-undefined specified. +# RUN: wasm-ld %t1.o -o %t1_1 --unresolved-symbols=ignore-all +# RUN: llvm-readobj %t1_1 > /dev/null 2>&1 + +# RXUN: not wasm-ld %t2.o -o /dev/null --unresolved-symbols=ignore-all --no-undefined 2>&1 | \ +# RXUN: FileCheck -check-prefix=ERRUND %s +# ERRUND: error: undefined symbol: undef +# ERRUND: >>> referenced by {{.*}}:(.text+0x1) + +## Also ignore all should not produce error for symbols from DSOs. +# RXUN: wasm-ld %t1.o %t.so -o %t1_3 --allow-shlib-undefined --unresolved-symbols=ignore-all +# RXUN: llvm-readobj %t1_3 > /dev/null 2>&1 + +## Do not report undefines if linking relocatable. +# RXUN: wasm-ld -r %t1.o %t2.o -o %t5 --unresolved-symbols=report-all +# RXUN: llvm-readobj %t5 > /dev/null 2>&1 + +.globl _start +_start: + .functype _start () -> () + call undef + end_function + +.functype undef () -> () diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -17,12 +17,14 @@ namespace lld { namespace wasm { +// For --unresolved-symbols. +enum class UnresolvedPolicy { ReportError, Warn, Ignore }; + // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { - bool allowUndefined; bool checkFeatures; bool compressRelocations; bool demangle; @@ -54,6 +56,7 @@ unsigned ltoo; unsigned optimize; llvm::StringRef thinLTOJobs; + UnresolvedPolicy unresolvedSymbols; llvm::StringRef entry; llvm::StringRef outputFile; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -323,9 +323,33 @@ return arg->getValue(); } +// Determines what we should do if there are remaining unresolved +// symbols after the name resolution. +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { + UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, + OPT_warn_unresolved_symbols, true) + ? UnresolvedPolicy::ReportError + : UnresolvedPolicy::Warn; + + if (auto *arg = args.getLastArg(OPT_unresolved_symbols)) { + StringRef s = arg->getValue(); + if (s == "ignore-all") + return UnresolvedPolicy::Ignore; + if (s == "report-all") + return errorOrWarn; + error("unknown --unresolved-symbols value: " + s); + } + + // Legacy --allow-undefined flag which is equivalent to + // --unresolve-symbols=ignore-all + if (args.hasArg(OPT_allow_undefined)) + return UnresolvedPolicy::Ignore; + + return errorOrWarn; +} + // Initializes Config members by the command line options. static void readConfigs(opt::InputArgList &args) { - config->allowUndefined = args.hasArg(OPT_allow_undefined); config->checkFeatures = args.hasFlag(OPT_check_features, OPT_no_check_features, true); config->compressRelocations = args.hasArg(OPT_compress_relocations); @@ -365,6 +389,7 @@ config->thinLTOCachePolicy = CHECK( parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); + config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); errorHandler().verbose = args.hasArg(OPT_verbose); LLVM_DEBUG(errorHandler().verbose = true); @@ -415,7 +440,7 @@ if (config->shared) { config->importMemory = true; - config->allowUndefined = true; + config->unresolvedSymbols = UnresolvedPolicy::Ignore; } } @@ -833,9 +858,11 @@ Symbol *sym = symtab->find(arg->getValue()); if (sym && sym->isDefined()) sym->forceExport = true; - else if (!config->allowUndefined) + else if (config->unresolvedSymbols == UnresolvedPolicy::ReportError) error(Twine("symbol exported via --export not found: ") + arg->getValue()); + else if (config->unresolvedSymbols == UnresolvedPolicy::Warn) + warn(Twine("symbol exported via --export not found: ") + arg->getValue()); } if (!config->relocatable) { diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -33,6 +33,9 @@ def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; +def error_unresolved_symbols: F<"error-unresolved-symbols">, + HelpText<"Report unresolved symbols as errors">; + defm export_dynamic: B<"export-dynamic", "Put symbols in the dynamic symbol table", "Do not put symbols in the dynamic symbol table (default)">; @@ -105,18 +108,24 @@ defm undefined: Eq<"undefined", "Force undefined symbol during linking">; +defm unresolved_symbols: + Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">; + def v: Flag<["-"], "v">, HelpText<"Display the version number">; def verbose: F<"verbose">, HelpText<"Verbose mode">; def version: F<"version">, HelpText<"Display the version number and exit">; -def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"