[lld][WebAssembly] Add allow-multiple-definition flag (#97699)
Add `allow-multiple-definition` flag to `wasm-ld`. This follows the ELF linker logic. In case of duplication, the first symbol met is used. This PR resolves the #97543
This commit is contained in:
6
lld/test/wasm/Inputs/allow-multiple-definition.s
Normal file
6
lld/test/wasm/Inputs/allow-multiple-definition.s
Normal file
@@ -0,0 +1,6 @@
|
||||
.hidden foo
|
||||
.globl foo
|
||||
foo:
|
||||
.functype foo () -> (i32)
|
||||
i32.const 1
|
||||
end_function
|
||||
38
lld/test/wasm/allow-multiple-definition.s
Normal file
38
lld/test/wasm/allow-multiple-definition.s
Normal file
@@ -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
|
||||
@@ -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 <optional>
|
||||
@@ -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<bool> 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
|
||||
|
||||
@@ -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 @@ private:
|
||||
|
||||
std::vector<InputFile *> 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<const char *> 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 =
|
||||
@@ -1173,7 +1195,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.
|
||||
|
||||
@@ -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<"<path>">,
|
||||
HelpText<"Path to file to write output">;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user