From 230dc11d24626c8a717de5073fef31ea8ea2683f Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 7 Feb 2019 22:42:16 +0000 Subject: [PATCH] [WebAssembly] Refactor handling of weak undefined functions. NFC. Also add to the docs. This is refactor in preparation for https://reviews.llvm.org/D57909 Differential Revision: https://reviews.llvm.org/D57920 llvm-svn: 353478 --- lld/docs/WebAssembly.rst | 12 +++++ lld/test/wasm/archive-weak-undefined.ll | 2 +- lld/test/wasm/cxx-mangling.ll | 4 +- lld/test/wasm/lto/weak-undefined.ll | 2 +- lld/test/wasm/undefined-weak-call.ll | 6 +-- lld/wasm/Driver.cpp | 49 +-------------------- lld/wasm/SymbolTable.cpp | 58 ++++++++++++++++++++++++- lld/wasm/SymbolTable.h | 7 ++- lld/wasm/Symbols.h | 2 + 9 files changed, 85 insertions(+), 57 deletions(-) diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst index 9e4b3a17fd88..016fdd74ec39 100644 --- a/lld/docs/WebAssembly.rst +++ b/lld/docs/WebAssembly.rst @@ -124,6 +124,18 @@ The symbols which are preserved by default are: - Any symbol which is to be exported. - Any symbol transitively referenced by the above. +Weak Undefined Functions +~~~~~~~~~~~~~~~~~~~~~~~~ + +On native platforms, calls to weak undefined functions end up as calls to the +null function pointer. With WebAssembly, direct calls must reference a defined +function (with the correct signature). In order to handle this case the linker +will generate function a stub containing only the ``unreachable`` instruction +and use this for any direct references to an undefined weak function. + +For example a runtime call to a weak undefined function ``foo`` will up trapping +on ``unreachable`` inside and linker-generated function called +``undefined:foo``. Missing features ---------------- diff --git a/lld/test/wasm/archive-weak-undefined.ll b/lld/test/wasm/archive-weak-undefined.ll index 3b92612c4942..0285724e5b86 100644 --- a/lld/test/wasm/archive-weak-undefined.ll +++ b/lld/test/wasm/archive-weak-undefined.ll @@ -15,5 +15,5 @@ entry: ret void } -; CHECK: Name: undefined function ret32 +; CHECK: Name: 'undefined:ret32' ; CHECK-NOT: Name: ret32 diff --git a/lld/test/wasm/cxx-mangling.ll b/lld/test/wasm/cxx-mangling.ll index e1f4ea4950a6..9732d21dd8f6 100644 --- a/lld/test/wasm/cxx-mangling.ll +++ b/lld/test/wasm/cxx-mangling.ll @@ -58,8 +58,8 @@ define void @_start() { ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Name: __wasm_call_ctors ; CHECK-NEXT: - Index: 1 -; DEMANGLE-NEXT: Name: 'undefined function bar(int)' -; MANGLE-NEXT: Name: undefined function _Z3bari +; DEMANGLE-NEXT: Name: 'undefined:bar(int)' +; MANGLE-NEXT: Name: 'undefined:_Z3bari' ; CHECK-NEXT: - Index: 2 ; 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 54b302ec2d36..5eb405afa2e6 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 function foo +; CHECK: Name: 'undefined:foo' diff --git a/lld/test/wasm/undefined-weak-call.ll b/lld/test/wasm/undefined-weak-call.ll index 0b7d0c769d47..9f1c39bc2d97 100644 --- a/lld/test/wasm/undefined-weak-call.ll +++ b/lld/test/wasm/undefined-weak-call.ll @@ -110,11 +110,11 @@ define i32 @callWeakFuncs() { ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Name: __wasm_call_ctors ; CHECK-NEXT: - Index: 1 -; CHECK-NEXT: Name: undefined function weakFunc1 +; CHECK-NEXT: Name: 'undefined:weakFunc1' ; CHECK-NEXT: - Index: 2 -; CHECK-NEXT: Name: undefined function weakFunc2 +; CHECK-NEXT: Name: 'undefined:weakFunc2' ; CHECK-NEXT: - Index: 3 -; CHECK-NEXT: Name: undefined function weakFunc3 +; CHECK-NEXT: Name: 'undefined:weakFunc3' ; CHECK-NEXT: - Index: 4 ; CHECK-NEXT: Name: callWeakFuncs ; CHECK-NEXT: ... diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index b866cb264ac8..40e88f2ac2a7 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -286,53 +286,6 @@ static StringRef getEntry(opt::InputArgList &Args, StringRef Default) { return Arg->getValue(); } -static const uint8_t UnreachableFn[] = { - 0x03 /* ULEB length */, 0x00 /* ULEB num locals */, - 0x00 /* opcode unreachable */, 0x0b /* opcode end */ -}; - -// 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. -static void handleWeakUndefines() { - for (Symbol *Sym : Symtab->getSymbols()) { - if (!Sym->isUndefWeak()) - continue; - - const WasmSignature *Sig = nullptr; - - if (auto *FuncSym = dyn_cast(Sym)) { - // It is possible for undefined functions not to have a signature (eg. if - // added via "--undefined"), but weak undefined ones do have a signature. - assert(FuncSym->Signature); - Sig = FuncSym->Signature; - } else if (auto *LazySym = dyn_cast(Sym)) { - // Lazy symbols may not be functions and therefore can have a null - // signature. - Sig = LazySym->Signature; - } - - if (!Sig) - continue; - - - // Add a synthetic dummy for weak undefined functions. These dummies will - // be GC'd if not used as the target of any "call" instructions. - std::string SymName = toString(*Sym); - StringRef DebugName = Saver.save("undefined function " + SymName); - auto *Func = make(*Sig, Sym->getName(), DebugName); - Func->setBody(UnreachableFn); - // 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); - Symtab->SyntheticFunctions.emplace_back(Func); - // Hide our dummy to prevent export. - uint32_t Flags = WASM_SYMBOL_VISIBILITY_HIDDEN; - replaceSymbol(Sym, Sym->getName(), Flags, nullptr, Func); - } -} - // Some Config members do not directly correspond to any particular // command line options, but computed based on other Config values. // This function initialize such members. See Config.h for the details @@ -620,7 +573,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Add synthetic dummies for weak undefined functions. Must happen // after LTO otherwise functions may not yet have signatures. if (!Config->Relocatable) - handleWeakUndefines(); + Symtab->handleWeakUndefines(); if (EntrySym) EntrySym->setHidden(false); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 00ee88f662e9..35ac2c5a6da9 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -102,7 +102,8 @@ std::pair SymbolTable::insertName(StringRef Name) { return {Sym, true}; } -std::pair SymbolTable::insert(StringRef Name, InputFile *File) { +std::pair SymbolTable::insert(StringRef Name, + const InputFile *File) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insertName(Name); @@ -442,3 +443,58 @@ bool SymbolTable::addComdat(StringRef Name) { void SymbolTable::trace(StringRef Name) { SymMap.insert({CachedHashStringRef(Name), -1}); } + +static const uint8_t UnreachableFn[] = { + 0x03 /* ULEB length */, 0x00 /* ULEB num locals */, + 0x00 /* opcode unreachable */, 0x0b /* opcode end */ +}; + +// Replace the given symbol body with an unreachable function. +// This is used by handleWeakUndefines in order to generate a callable +// equivalent of an undefined function. +InputFunction *SymbolTable::replaceWithUnreachable(Symbol *Sym, + const WasmSignature &Sig, + StringRef DebugName) { + auto *Func = make(Sig, Sym->getName(), DebugName); + Func->setBody(UnreachableFn); + SyntheticFunctions.emplace_back(Func); + replaceSymbol(Sym, Sym->getName(), Sym->getFlags(), nullptr, Func); + return Func; +} + +// 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 = nullptr; + + if (auto *FuncSym = dyn_cast(Sym)) { + // It is possible for undefined functions not to have a signature (eg. if + // added via "--undefined"), but weak undefined ones do have a signature. + assert(FuncSym->Signature); + Sig = FuncSym->Signature; + } else if (auto *LazySym = dyn_cast(Sym)) { + // Lazy symbols may not be functions and therefore can have a null + // signature. + Sig = LazySym->Signature; + } + + if (!Sig) + continue; + + // 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); + } +} diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h index 67133cee2566..f03980d7066c 100644 --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -79,10 +79,15 @@ public: DefinedFunction *addSyntheticFunction(StringRef Name, uint32_t Flags, InputFunction *Function); + void handleWeakUndefines(); + private: - std::pair insert(StringRef Name, InputFile *File); + std::pair insert(StringRef Name, const InputFile *File); std::pair insertName(StringRef Name); + InputFunction *replaceWithUnreachable(Symbol *Sym, const WasmSignature &Sig, + StringRef DebugName); + // 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 // ever added. diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index d50f15f70ed2..fd501ed64cdb 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -76,6 +76,8 @@ public: // Returns the file from which this symbol was created. InputFile *getFile() const { return File; } + uint32_t getFlags() const { return Flags; } + InputChunk *getChunk() const; // Indicates that the section or import for this symbol will be included in