diff --git a/lld/test/wasm/Inputs/allow-multiple-definition.s b/lld/test/wasm/Inputs/allow-multiple-definition.s new file mode 100644 index 0000000000000..7a5577cb12791 --- /dev/null +++ b/lld/test/wasm/Inputs/allow-multiple-definition.s @@ -0,0 +1,6 @@ + .hidden foo + .globl foo +foo: + .functype foo () -> (i32) + i32.const 1 + end_function diff --git a/lld/test/wasm/allow-multiple-definition.s b/lld/test/wasm/allow-multiple-definition.s new file mode 100644 index 0000000000000..93fccd3c46f90 --- /dev/null +++ b/lld/test/wasm/allow-multiple-definition.s @@ -0,0 +1,38 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t1 +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/allow-multiple-definition.s -o %t2 +# RUN: not wasm-ld %t1 %t2 -o /dev/null +# RUN: not wasm-ld --allow-multiple-definition --no-allow-multiple-definition %t1 %t2 -o /dev/null +# RUN: wasm-ld --allow-multiple-definition --fatal-warnings %t1 %t2 -o %t3 +# RUN: wasm-ld --allow-multiple-definition --fatal-warnings %t2 %t1 -o %t4 +# RUN: llvm-objdump --no-print-imm-hex -d %t3 | FileCheck %s +# RUN: llvm-objdump --no-print-imm-hex -d %t4 | FileCheck --check-prefix=REVERT %s + +# RUN: wasm-ld --noinhibit-exec %t2 %t1 -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN +# WARN: warning: duplicate symbol: foo + +# RUN: wasm-ld -z muldefs --fatal-warnings %t1 %t2 -o %t3 +# RUN: wasm-ld -z muldefs --fatal-warnings %t2 %t1 -o %t4 +# RUN: llvm-objdump --no-print-imm-hex -d %t3 | FileCheck %s +# RUN: llvm-objdump --no-print-imm-hex -d %t4 | FileCheck --check-prefix=REVERT %s + +# CHECK: i32.const 0 +# REVERT: i32.const 1 + +# inputs contain different constants for function foo return. +# Tests below checks that order of files in command line +# affects on what symbol will be used. +# If flag allow-multiple-definition is enabled the first +# meet symbol should be used. + + .hidden foo + .globl foo +foo: + .functype foo () -> (i32) + i32.const 0 + end_function + + .globl _start +_start: + .functype _start () -> (i32) + call foo + end_function diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index 915c53c437172..05a547ff9278a 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -12,6 +12,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/CachePruning.h" #include @@ -43,6 +44,7 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid }; // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { + bool allowMultipleDefinition; bool bsymbolic; bool checkFeatures; bool compressRelocations; @@ -64,6 +66,7 @@ struct Configuration { bool importUndefined; std::optional is64; bool mergeDataSegments; + bool noinhibitExec; bool pie; bool printGcSections; bool relocatable; @@ -148,6 +151,8 @@ struct Ctx { extern Ctx ctx; +void errorOrWarn(const llvm::Twine &msg); + } // namespace lld::wasm #endif diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 8c83d17db02f5..e70fc7d9af37f 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -47,6 +47,13 @@ namespace lld::wasm { Configuration *config; Ctx ctx; +void errorOrWarn(const llvm::Twine &msg) { + if (config->noinhibitExec) + warn(msg); + else + error(msg); +} + void Ctx::reset() { objectFiles.clear(); stubFiles.clear(); @@ -99,6 +106,16 @@ class LinkerDriver { std::vector files; }; + +static bool hasZOption(opt::InputArgList &args, StringRef key) { + bool ret = false; + for (const auto *arg : args.filtered(OPT_z)) + if (key == arg->getValue()) { + ret = true; + arg->claim(); + } + return ret; +} } // anonymous namespace bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, @@ -467,6 +484,10 @@ getBuildId(opt::InputArgList &args) { // Initializes Config members by the command line options. static void readConfigs(opt::InputArgList &args) { + config->allowMultipleDefinition = + hasZOption(args, "muldefs") || + args.hasFlag(OPT_allow_multiple_definition, + OPT_no_allow_multiple_definition, false); config->bsymbolic = args.hasArg(OPT_Bsymbolic); config->checkFeatures = args.hasFlag(OPT_check_features, OPT_no_check_features, true); @@ -479,6 +500,7 @@ static void readConfigs(opt::InputArgList &args) { config->exportAll = args.hasArg(OPT_export_all); config->exportTable = args.hasArg(OPT_export_table); config->growableTable = args.hasArg(OPT_growable_table); + config->noinhibitExec = args.hasArg(OPT_noinhibit_exec); if (args.hasArg(OPT_import_memory_with_name)) { config->memoryImport = @@ -1162,7 +1184,7 @@ static void splitSections() { static bool isKnownZFlag(StringRef s) { // For now, we only support a very limited set of -z flags - return s.starts_with("stack-size="); + return s.starts_with("stack-size=") || s.starts_with("muldefs"); } // Report a warning for an unknown -z option. diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td index 3a70ee65f7c4f..c5febd145a54f 100644 --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -42,6 +42,10 @@ def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">; def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries (default)">; +defm allow_multiple_definition: B<"allow-multiple-definition", + "Allow multiple definitions", + "Do not allow multiple definitions (default)">; + def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">; def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">, @@ -105,6 +109,9 @@ defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option proces defm Map: Eq<"Map", "Print a link map to the specified file">; +def noinhibit_exec: F<"noinhibit-exec">, + HelpText<"Retain the executable output file whenever it is still usable">; + def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, HelpText<"Path to file to write output">; diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index a5d37a5eba6d5..d2216ff5a39a0 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -319,9 +319,12 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile, } // Neither symbol is week. They conflict. - error("duplicate symbol: " + toString(*existing) + "\n>>> defined in " + - toString(existing->getFile()) + "\n>>> defined in " + - toString(newFile)); + if (config->allowMultipleDefinition) + return false; + + errorOrWarn("duplicate symbol: " + toString(*existing) + "\n>>> defined in " + + toString(existing->getFile()) + "\n>>> defined in " + + toString(newFile)); return true; }