[lld] 206884b - [lld][WebAssembly] Implement --unresolved-symbols
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 17 16:28:37 PST 2020
Author: Sam Clegg
Date: 2020-11-17T16:27:06-08:00
New Revision: 206884bf90cd9793558a9c80315ca3cfa35c25d6
URL: https://github.com/llvm/llvm-project/commit/206884bf90cd9793558a9c80315ca3cfa35c25d6
DIFF: https://github.com/llvm/llvm-project/commit/206884bf90cd9793558a9c80315ca3cfa35c25d6.diff
LOG: [lld][WebAssembly] Implement --unresolved-symbols
This is a more full featured version of ``--allow-undefined``.
The semantics of the different methods are as follows:
report-all:
Report all unresolved symbols. This is the default. Normally the
linker will generate an error message for each reported unresolved
symbol but the option ``--warn-unresolved-symbols`` can change this
to a warning.
ignore-all:
Resolve all undefined symbols to zero. For data and function
addresses this is trivial. For direct function calls, the linker
will generate a trapping stub function in place of the undefined
function.
import-functions:
Generate WebAssembly imports for any undefined functions. Undefined
data symbols are resolved to zero as in `ignore-all`. This
corresponds to the legacy ``--allow-undefined`` flag.
The plan is to followup with a new mode called `import-dynamic` which
allows for statically linked binaries to refer to both data and
functions symbols from the embedder.
Differential Revision: https://reviews.llvm.org/D79248
Added:
lld/test/wasm/unresolved-symbols.s
Modified:
lld/docs/WebAssembly.rst
lld/test/wasm/archive-weak-undefined.ll
lld/test/wasm/cxx-mangling.ll
lld/test/wasm/lto/weak-undefined.ll
lld/test/wasm/undefined-weak-call.ll
lld/test/wasm/weak-undefined.ll
lld/wasm/Config.h
lld/wasm/Driver.cpp
lld/wasm/Options.td
lld/wasm/Relocations.cpp
lld/wasm/SymbolTable.cpp
lld/wasm/SymbolTable.h
lld/wasm/Symbols.cpp
lld/wasm/Symbols.h
lld/wasm/Writer.cpp
Removed:
################################################################################
diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst
index bf1f008e608e..36062f5f0ac0 100644
--- a/lld/docs/WebAssembly.rst
+++ b/lld/docs/WebAssembly.rst
@@ -71,7 +71,31 @@ WebAssembly-specific options:
.. option:: --allow-undefined
- Allow undefined symbols in linked binary.
+ Allow undefined symbols in linked binary. This is the legacy
+ flag which corresponds to ``--unresolved-symbols=import-functions``.
+
+.. option:: --unresolved-symbols=<method>
+
+ This is a more full featured version of ``--allow-undefined``.
+ The semanatics of the
diff erent methods are as follows:
+
+ report-all:
+
+ Report all unresolved symbols. This is the default. Normally the linker
+ will generate an error message for each reported unresolved symbol but the
+ option ``--warn-unresolved-symbols`` can change this to a warning.
+
+ ignore-all:
+
+ Resolve all undefined symbols to zero. For data and function addresses
+ this is trivial. For direct function calls, the linker will generate a
+ trapping stub function in place of the undefined function.
+
+ import-functions:
+
+ Generate WebAssembly imports for any undefined functions. Undefined data
+ symbols are resolved to zero as in ``ignore-all``. This corresponds to
+ the legacy ``--allow-undefined`` flag.
.. option:: --import-memory
diff --git a/lld/test/wasm/archive-weak-undefined.ll b/lld/test/wasm/archive-weak-undefined.ll
index 9539cf91af36..297f01c80343 100644
--- a/lld/test/wasm/archive-weak-undefined.ll
+++ b/lld/test/wasm/archive-weak-undefined.ll
@@ -39,5 +39,5 @@ if.end:
; CHECK-NOT: Type: DATA
; CHECK-DATA: Type: DATA
-; CHECK: Name: 'undefined:ret32'
+; CHECK: Name: 'undefined_weak:ret32'
; CHECK-NOT: Name: ret32
diff --git a/lld/test/wasm/cxx-mangling.ll b/lld/test/wasm/cxx-mangling.ll
index 415ad2f92524..8c97ae0e6b84 100644
--- a/lld/test/wasm/cxx-mangling.ll
+++ b/lld/test/wasm/cxx-mangling.ll
@@ -47,8 +47,8 @@ define void @_start() {
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; DEMANGLE-NEXT: Name: 'undefined:bar(int)'
-; MANGLE-NEXT: Name: 'undefined:_Z3bari'
+; DEMANGLE-NEXT: Name: 'undefined_weak:bar(int)'
+; MANGLE-NEXT: Name: 'undefined_weak:_Z3bari'
; CHECK-NEXT: - Index: 1
; DEMANGLE-NEXT: Name: 'foo(int)'
; MANGLE-NEXT: Name: _Z3fooi
diff --git a/lld/test/wasm/lto/weak-undefined.ll b/lld/test/wasm/lto/weak-undefined.ll
index 5eb405afa2e6..080e23cbbc15 100644
--- a/lld/test/wasm/lto/weak-undefined.ll
+++ b/lld/test/wasm/lto/weak-undefined.ll
@@ -17,4 +17,4 @@ entry:
ret void
}
-; CHECK: Name: 'undefined:foo'
+; CHECK: Name: 'undefined_weak:foo'
diff --git a/lld/test/wasm/undefined-weak-call.ll b/lld/test/wasm/undefined-weak-call.ll
index a1491549ae7e..74b59808c08b 100644
--- a/lld/test/wasm/undefined-weak-call.ll
+++ b/lld/test/wasm/undefined-weak-call.ll
@@ -89,11 +89,11 @@ define i32 @callWeakFuncs() {
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: 'undefined:weakFunc1'
+; CHECK-NEXT: Name: 'undefined_weak:weakFunc1'
; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: 'undefined:weakFunc2'
+; CHECK-NEXT: Name: 'undefined_weak:weakFunc2'
; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: 'undefined:weakFunc3'
+; CHECK-NEXT: Name: 'undefined_weak:weakFunc3'
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Name: callWeakFuncs
; CHECK-NEXT: ...
diff --git a/lld/test/wasm/unresolved-symbols.s b/lld/test/wasm/unresolved-symbols.s
new file mode 100644
index 000000000000..26079a964102
--- /dev/null
+++ b/lld/test/wasm/unresolved-symbols.s
@@ -0,0 +1,94 @@
+# 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=ERRUND %s
+# ERRUND: error: {{.*}}1.o: undefined symbol: undef
+
+## report-all is the default one. Check that we get the same error
+# RUN: not wasm-ld %t1.o -o /dev/null --unresolved-symbols=report-all 2>&1 | \
+# RUN: FileCheck -check-prefix=ERRUND %s
+
+## 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 and should not produce
+# any imports. It should create a stub function in the place of the missing
+# function symbol.
+# RUN: wasm-ld %t1.o -o %t2.wasm --unresolved-symbols=ignore-all
+# RUN: obj2yaml %t2.wasm | FileCheck -check-prefix=IGNORE %s
+# IGNORE-NOT: - Type: IMPORT
+# IGNORE-NOT: - Type: ELEM
+#
+# IGNORE: - Type: CODE
+# IGNORE-NEXT: Functions:
+# IGNORE-NEXT: - Index: 0
+# IGNORE-NEXT: Locals: []
+# IGNORE-NEXT: Body: 000B
+# IGNORE-NEXT: - Index: 1
+# IGNORE-NEXT: Locals: []
+# IGNORE-NEXT: Body: 1080808080001082808080001083808080000B
+# IGNORE-NEXT: - Index: 2
+# IGNORE-NEXT: Locals: []
+# IGNORE-NEXT: Body: 4180808080000F0B
+# IGNORE-NEXT: - Index: 3
+# IGNORE-NEXT: Locals: []
+# IGNORE-NEXT: Body: 4180808080000F0B
+#
+# IGNORE: - Type: CUSTOM
+# IGNORE-NEXT: Name: name
+# IGNORE-NEXT: FunctionNames:
+# IGNORE-NEXT: - Index: 0
+# IGNORE-NEXT: Name: undefined
+# IGNORE-NEXT: - Index: 1
+# IGNORE-NEXT: Name: _start
+# IGNORE-NEXT: - Index: 2
+# IGNORE-NEXT: Name: get_data_addr
+# IGNORE-NEXT: - Index: 3
+# IGNORE-NEXT: Name: get_func_addr
+
+## import-functions should not produce errors and should resolve in
+# imports for the missing functions but not the missing data symbols.
+# `--allow-undefined` should behave exactly the same.
+# RUN: wasm-ld %t1.o -o %t3.wasm --unresolved-symbols=import-functions
+# RUN: obj2yaml %t3.wasm | FileCheck -check-prefix=IMPORT %s
+# IMPORT: - Type: IMPORT
+# IMPORT-NEXT: Imports:
+# IMPORT-NEXT: - Module: env
+# IMPORT-NEXT: Field: undef
+# IMPORT-NEXT: Kind: FUNCTION
+# IMPORT-NEXT: SigIndex: 0
+# IMPORT-NEXT: - Type: FUNCTION
+
+## Do not report undefines if linking relocatable.
+# RUN: wasm-ld -r %t1.o -o %t4.wasm --unresolved-symbols=report-all
+# RUN: llvm-readobj %t4.wasm > /dev/null 2>&1
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ call undef
+ call get_data_addr
+ call get_func_addr
+ end_function
+
+.globl get_data_addr
+get_data_addr:
+ .functype get_data_addr () -> (i32)
+ i32.const undef_data
+ return
+ end_function
+
+.globl get_func_addr
+get_func_addr:
+ .functype get_func_addr () -> (i32)
+ i32.const undef
+ return
+ end_function
+
+.functype undef () -> ()
diff --git a/lld/test/wasm/weak-undefined.ll b/lld/test/wasm/weak-undefined.ll
index 769c531e0c61..878c0278c35c 100644
--- a/lld/test/wasm/weak-undefined.ll
+++ b/lld/test/wasm/weak-undefined.ll
@@ -24,6 +24,7 @@ define void @_start() #0 {
entry:
%call1 = call i32* @get_address_of_global_var()
%call2 = call i8* @get_address_of_foo()
+ call i32 @foo()
ret void
}
@@ -41,7 +42,7 @@ entry:
; CHECK-NEXT: ParamTypes: []
; CHECK-NEXT: ReturnTypes: []
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 0, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0, 1 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - Index: 0
@@ -68,16 +69,19 @@ entry:
; CHECK-NEXT: Index: 0
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4180808080000B
+; CHECK-NEXT: Body: 000B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 4180808080000B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080001A1080808080001A0B
+; CHECK-NEXT: Body: 4180808080000B
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 1082808080001A1081808080001A1080808080001A0B
; CHECK-NEXT: ...
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 4439098e65ba..68b09a653d40 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -17,12 +17,17 @@
namespace lld {
namespace wasm {
+// For --unresolved-symbols.
+// The `ImportFuncs` mode is an additional mode that corresponds to the
+// --allow-undefined flag which turns undefined functions in imports
+// as opposed ed to Ignore or Warn which turn them into unreachables.
+enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportFuncs };
+
// 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 bsymbolic;
bool checkFeatures;
bool compressRelocations;
@@ -57,6 +62,7 @@ struct Configuration {
unsigned ltoo;
unsigned optimize;
llvm::StringRef thinLTOJobs;
+ UnresolvedPolicy unresolvedSymbols;
llvm::StringRef entry;
llvm::StringRef mapFile;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 5b12b48b0be6..8aa124795f6f 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -331,9 +331,35 @@ static StringRef getEntry(opt::InputArgList &args) {
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 == "import-functions")
+ return UnresolvedPolicy::ImportFuncs;
+ 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::ImportFuncs;
+
+ return errorOrWarn;
+}
+
// Initializes Config members by the command line options.
static void readConfigs(opt::InputArgList &args) {
- config->allowUndefined = args.hasArg(OPT_allow_undefined);
config->bsymbolic = args.hasArg(OPT_Bsymbolic);
config->checkFeatures =
args.hasFlag(OPT_check_features, OPT_no_check_features, true);
@@ -376,6 +402,7 @@ static void readConfigs(opt::InputArgList &args) {
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);
@@ -440,7 +467,7 @@ static void setConfigs() {
if (config->shared) {
config->importMemory = true;
- config->allowUndefined = true;
+ config->unresolvedSymbols = UnresolvedPolicy::ImportFuncs;
}
}
@@ -939,9 +966,11 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
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
index 2955e6e146f9..620b3bd10550 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -36,6 +36,9 @@ defm demangle: B<"demangle",
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)">;
@@ -112,18 +115,24 @@ defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
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<"<option>">,
- HelpText<"Linker option extensions">;
+def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
+ HelpText<"Report unresolved symbols as warnings">;
defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
MetaVarName<"<symbol>=<symbol>">;
+def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
+ HelpText<"Linker option extensions">;
+
// The follow flags are unique to wasm
def allow_undefined: F<"allow-undefined">,
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 5d6e82b412a3..5b131d4f23f4 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -10,6 +10,7 @@
#include "InputChunks.h"
#include "OutputSegment.h"
+#include "SymbolTable.h"
#include "SyntheticSections.h"
using namespace llvm;
@@ -39,15 +40,36 @@ static bool allowUndefined(const Symbol* sym) {
if (auto *g = dyn_cast<UndefinedGlobal>(sym))
if (g->importName)
return true;
- return (config->allowUndefined ||
- config->allowUndefinedSymbols.count(sym->getName()) != 0);
+ if (auto *g = dyn_cast<UndefinedGlobal>(sym))
+ if (g->importName)
+ return true;
+ return config->allowUndefinedSymbols.count(sym->getName()) != 0;
}
-static void reportUndefined(const Symbol* sym) {
- assert(sym->isUndefined());
- assert(!sym->isWeak());
- if (!allowUndefined(sym))
- error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
+static void reportUndefined(Symbol *sym) {
+ if (!allowUndefined(sym)) {
+ switch (config->unresolvedSymbols) {
+ case UnresolvedPolicy::ReportError:
+ error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
+ break;
+ case UnresolvedPolicy::Warn:
+ warn(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
+ break;
+ case UnresolvedPolicy::Ignore:
+ if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
+ if (!f->stubFunction) {
+ LLVM_DEBUG(dbgs()
+ << "ignoring undefined symbol: " + toString(*sym) + "\n");
+ f->stubFunction = symtab->createUndefinedStub(*f->getSignature());
+ f->stubFunction->markLive();
+ f->setTableIndex(0);
+ }
+ }
+ break;
+ case UnresolvedPolicy::ImportFuncs:
+ break;
+ }
+ }
}
static void addGOTEntry(Symbol *sym) {
@@ -131,7 +153,6 @@ void scanRelocations(InputChunk *chunk) {
if (sym->isUndefined() && !config->relocatable && !sym->isWeak())
reportUndefined(sym);
}
-
}
}
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index ce119851dc32..fc92020d07dd 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -673,40 +673,58 @@ InputFunction *SymbolTable::replaceWithUnreachable(Symbol *sym,
// to be exported outside the object file.
replaceSymbol<DefinedFunction>(sym, debugName, WASM_SYMBOL_BINDING_LOCAL,
nullptr, func);
+ // Ensure it compares equal to the null pointer, and so that table relocs
+ // don't pull in the stub body (only call-operand relocs should do that).
+ func->setTableIndex(0);
return func;
}
+void SymbolTable::replaceWithUndefined(Symbol *sym) {
+ // Add a synthetic dummy for weak undefined functions. These dummies will
+ // be GC'd if not used as the target of any "call" instructions.
+ StringRef debugName = saver.save("undefined_weak:" + toString(*sym));
+ replaceWithUnreachable(sym, *sym->getSignature(), debugName);
+ // Hide our dummy to prevent export.
+ sym->setHidden(true);
+}
+
// For weak undefined functions, there may be "call" instructions that reference
// the symbol. In this case, we need to synthesise a dummy/stub function that
// will abort at runtime, so that relocations can still provided an operand to
// the call instruction that passes Wasm validation.
void SymbolTable::handleWeakUndefines() {
for (Symbol *sym : getSymbols()) {
- if (!sym->isUndefWeak())
- continue;
-
- const WasmSignature *sig = sym->getSignature();
- if (!sig) {
- // It is possible for undefined functions not to have a signature (eg. if
- // added via "--undefined"), but weak undefined ones do have a signature.
- // Lazy symbols may not be functions and therefore Sig can still be null
- // in some circumstance.
- assert(!isa<FunctionSymbol>(sym));
- continue;
+ if (sym->isUndefWeak()) {
+ if (sym->getSignature()) {
+ replaceWithUndefined(sym);
+ } else {
+ // It is possible for undefined functions not to have a signature (eg.
+ // if added via "--undefined"), but weak undefined ones do have a
+ // signature. Lazy symbols may not be functions and therefore Sig can
+ // still be null in some circumstance.
+ assert(!isa<FunctionSymbol>(sym));
+ }
}
-
- // Add a synthetic dummy for weak undefined functions. These dummies will
- // be GC'd if not used as the target of any "call" instructions.
- StringRef debugName = saver.save("undefined:" + toString(*sym));
- InputFunction* func = replaceWithUnreachable(sym, *sig, debugName);
- // Ensure it compares equal to the null pointer, and so that table relocs
- // don't pull in the stub body (only call-operand relocs should do that).
- func->setTableIndex(0);
- // Hide our dummy to prevent export.
- sym->setHidden(true);
}
}
+DefinedFunction *SymbolTable::createUndefinedStub(const WasmSignature &sig) {
+ if (stubFunctions.count(sig))
+ return stubFunctions[sig];
+ LLVM_DEBUG(dbgs() << "createUndefinedStub: " << toString(sig) << "\n");
+ auto *sym = reinterpret_cast<DefinedFunction *>(make<SymbolUnion>());
+ sym->isUsedInRegularObj = true;
+ sym->canInline = true;
+ sym->traced = false;
+ sym->forceExport = false;
+ sym->signature = &sig;
+ replaceSymbol<DefinedFunction>(
+ sym, "undefined_stub", WASM_SYMBOL_VISIBILITY_HIDDEN, nullptr, nullptr);
+ replaceWithUnreachable(sym, sig, "undefined_stub");
+ stubFunctions[sig] = sym;
+ return sym;
+}
+
static void reportFunctionSignatureMismatch(StringRef symName,
FunctionSymbol *a,
FunctionSymbol *b, bool isError) {
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 08978d338b63..d3a937a7a473 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -16,6 +16,7 @@
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/BinaryFormat/WasmTraits.h"
namespace lld {
namespace wasm {
@@ -88,6 +89,7 @@ class SymbolTable {
void handleSymbolVariants();
void handleWeakUndefines();
+ DefinedFunction *createUndefinedStub(const WasmSignature &sig);
std::vector<ObjFile *> objectFiles;
std::vector<SharedFile *> sharedFiles;
@@ -103,6 +105,7 @@ class SymbolTable {
const InputFile *file, Symbol **out);
InputFunction *replaceWithUnreachable(Symbol *sym, const WasmSignature &sig,
StringRef debugName);
+ void replaceWithUndefined(Symbol *sym);
// Maps symbol names to index into the symVector. -1 means that symbols
// is to not yet in the vector but it should have tracing enabled if it is
@@ -113,6 +116,7 @@ class SymbolTable {
// For certain symbols types, e.g. function symbols, we allow for multiple
// variants of the same symbol with
diff erent signatures.
llvm::DenseMap<llvm::CachedHashStringRef, std::vector<Symbol *>> symVariants;
+ llvm::DenseMap<WasmSignature, DefinedFunction *> stubFunctions;
// Comdat groups define "link once" sections. If two comdat groups have the
// same name, only one of them is linked, and the other is ignored. This set
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 784a20fa777a..ffb52c1c5746 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -109,6 +109,9 @@ const WasmSignature *Symbol::getSignature() const {
InputChunk *Symbol::getChunk() const {
if (auto *f = dyn_cast<DefinedFunction>(this))
return f->function;
+ if (auto *f = dyn_cast<UndefinedFunction>(this))
+ if (f->stubFunction)
+ return f->stubFunction->function;
if (auto *d = dyn_cast<DefinedData>(this))
return d->segment;
return nullptr;
@@ -207,6 +210,11 @@ bool Symbol::isNoStrip() const {
uint32_t FunctionSymbol::getFunctionIndex() const {
if (auto *f = dyn_cast<DefinedFunction>(this))
return f->function->getFunctionIndex();
+ if (const auto *u = dyn_cast<UndefinedFunction>(this)) {
+ if (u->stubFunction) {
+ return u->stubFunction->getFunctionIndex();
+ }
+ }
assert(functionIndex != INVALID_INDEX);
return functionIndex;
}
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 69195e414b47..865a9e7fee99 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -217,6 +217,7 @@ class UndefinedFunction : public FunctionSymbol {
llvm::Optional<StringRef> importName;
llvm::Optional<StringRef> importModule;
+ DefinedFunction *stubFunction = nullptr;
bool isCalledDirectly;
};
@@ -516,7 +517,7 @@ union SymbolUnion {
// It is important to keep the size of SymbolUnion small for performance and
// memory usage reasons. 96 bytes is a soft limit based on the size of
// UndefinedFunction on a 64-bit system.
-static_assert(sizeof(SymbolUnion) <= 112, "SymbolUnion too large");
+static_assert(sizeof(SymbolUnion) <= 120, "SymbolUnion too large");
void printTraceSymbol(Symbol *sym);
void printTraceSymbolUndefined(StringRef name, const InputFile* file);
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 8178489c3d00..0be9a044763b 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -539,6 +539,25 @@ void Writer::populateTargetFeatures() {
}
}
+static bool shouldImport(Symbol *sym) {
+ // We don't generate imports for data symbols. They however can be imported
+ // as GOT entries.
+ if (isa<DataSymbol>(sym))
+ return false;
+
+ if (config->relocatable ||
+ config->unresolvedSymbols == UnresolvedPolicy::ImportFuncs)
+ return true;
+ if (config->allowUndefinedSymbols.count(sym->getName()) != 0)
+ return true;
+ if (auto *g = dyn_cast<UndefinedGlobal>(sym))
+ return g->importName.hasValue();
+ if (auto *f = dyn_cast<UndefinedFunction>(sym))
+ return f->importName.hasValue();
+
+ return false;
+}
+
void Writer::calculateImports() {
for (Symbol *sym : symtab->getSymbols()) {
if (!sym->isUndefined())
@@ -549,13 +568,10 @@ void Writer::calculateImports() {
continue;
if (!sym->isUsedInRegularObj)
continue;
- // We don't generate imports for data symbols. They however can be imported
- // as GOT entries.
- if (isa<DataSymbol>(sym))
- continue;
-
- LLVM_DEBUG(dbgs() << "import: " << sym->getName() << "\n");
- out.importSec->addImport(sym);
+ if (shouldImport(sym)) {
+ LLVM_DEBUG(dbgs() << "import: " << sym->getName() << "\n");
+ out.importSec->addImport(sym);
+ }
}
}
More information about the llvm-commits
mailing list