Index: test/wasm/call-indirect.ll =================================================================== --- test/wasm/call-indirect.ll +++ test/wasm/call-indirect.ll @@ -58,11 +58,24 @@ ; CHECK-NEXT: InitExpr: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 66576 +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: false +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1024 +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: false +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1028 ; CHECK-NEXT: - Type: EXPORT ; CHECK-NEXT: Exports: ; CHECK-NEXT: - Name: memory ; CHECK-NEXT: Kind: MEMORY ; CHECK-NEXT: Index: 0 +; CHECK-NEXT: - Name: _start +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 3 ; CHECK-NEXT: - Name: bar ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 @@ -72,10 +85,13 @@ ; CHECK-NEXT: - Name: foo ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 2 -; CHECK-NEXT: - Name: _start -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 3 -; CHECK: - Type: ELEM +; CHECK-NEXT: - Name: indirect_bar +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: indirect_func +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 2 +; CHECK-NEXT: - Type: ELEM ; CHECK-NEXT: Segments: ; CHECK-NEXT: - Offset: ; CHECK-NEXT: Opcode: I32_CONST Index: test/wasm/data-layout.ll =================================================================== --- test/wasm/data-layout.ll +++ test/wasm/data-layout.ll @@ -12,7 +12,29 @@ @hello_str = external global i8* @external_ref = global i8** @hello_str, align 8 -; CHECK: - Type: GLOBAL +; CHECK: - Type: IMPORT +; CHECK-NEXT: Imports: +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: _start +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: SigIndex: 0 +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: puts +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: SigIndex: 1 +; CHECK-NEXT: - Type: FUNCTION +; CHECK-NEXT: FunctionTypes: [ 0 ] +; CHECK-NEXT: - Type: TABLE +; CHECK-NEXT: Tables: +; CHECK-NEXT: - ElemType: ANYFUNC +; CHECK-NEXT: Limits: +; CHECK-NEXT: Flags: 0x00000001 +; CHECK-NEXT: Initial: 0x00000001 +; CHECK-NEXT: Maximum: 0x00000001 +; CHECK-NEXT: - Type: MEMORY +; CHECK-NEXT: Memories: +; CHECK-NEXT: - Initial: 0x00000002 +; CHECK-NEXT: - Type: GLOBAL ; CHECK-NEXT: Globals: ; CHECK-NEXT: - Type: I32 ; CHECK-NEXT: Mutable: true Index: test/wasm/local-symbols.ll =================================================================== --- test/wasm/local-symbols.ll +++ test/wasm/local-symbols.ll @@ -46,6 +46,16 @@ ; CHECK-NEXT: InitExpr: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 66576 +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: false +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1024 +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: false +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1028 ; CHECK-NEXT: - Type: EXPORT ; CHECK-NEXT: Exports: ; CHECK-NEXT: - Name: memory @@ -54,6 +64,9 @@ ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: foo +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 1 ; CHECK-NEXT: - Type: CODE ; CHECK-NEXT: Functions: ; CHECK-NEXT: - Locals: Index: test/wasm/relocatable.ll =================================================================== --- test/wasm/relocatable.ll +++ test/wasm/relocatable.ll @@ -93,6 +93,18 @@ ; CHECK-NEXT: - Name: my_func ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 3 +; CHECK-NEXT: - Name: hello_str +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: func_addr1 +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 2 +; CHECK-NEXT: - Name: func_addr2 +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 3 +; CHECK-NEXT: - Name: data_addr1 +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 4 ; CHECK-NEXT: - Type: ELEM ; CHECK-NEXT: Segments: ; CHECK-NEXT: - Offset: Index: test/wasm/signature-mismatch.ll =================================================================== --- /dev/null +++ test/wasm/signature-mismatch.ll @@ -0,0 +1,19 @@ +; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o +; RUN: llc -filetype=obj %s -o %t.main.o +; RUN: not lld -flavor wasm --check-signatures -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown-wasm" + +; Function Attrs: nounwind +define hidden void @_start() local_unnamed_addr #0 { +entry: + %call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2 + ret void +} + +declare i32 @ret32(i32, i64, i32) local_unnamed_addr #1 + +; CHECK: error: function signature mismatch: ret32 +; CHECK-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o +; CHECK-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o Index: test/wasm/weak-alias-overide.ll =================================================================== --- test/wasm/weak-alias-overide.ll +++ test/wasm/weak-alias-overide.ll @@ -53,12 +53,12 @@ ; CHECK-NEXT: - Name: memory ; CHECK-NEXT: Kind: MEMORY ; CHECK-NEXT: Index: 0 -; CHECK-NEXT: - Name: bar -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 0 ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: bar +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 0 ; CHECK-NEXT: - Name: foo ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 2 Index: test/wasm/weak-alias.ll =================================================================== --- test/wasm/weak-alias.ll +++ test/wasm/weak-alias.ll @@ -47,9 +47,6 @@ ; CHECK-NEXT: - Name: memory ; CHECK-NEXT: Kind: MEMORY ; CHECK-NEXT: Index: 0 -; CHECK-NEXT: - Name: bar -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 1 ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 Index: test/wasm/weak-external.ll =================================================================== --- test/wasm/weak-external.ll +++ test/wasm/weak-external.ll @@ -57,15 +57,15 @@ ; CHECK-NEXT: - Name: memory ; CHECK-NEXT: Kind: MEMORY ; CHECK-NEXT: Index: 0 +; CHECK-NEXT: - Name: _start +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 2 ; CHECK-NEXT: - Name: get_address_of_foo ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 ; CHECK-NEXT: - Name: get_address_of_global_var ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: - Name: _start -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 2 ; CHECK-NEXT: - Type: ELEM ; CHECK-NEXT: Segments: ; CHECK-NEXT: - Offset: Index: wasm/Config.h =================================================================== --- wasm/Config.h +++ wasm/Config.h @@ -23,17 +23,18 @@ namespace wasm { struct Configuration { - bool AllowUndefined = false; - bool Demangle = true; - bool EmitRelocs = false; - bool ImportMemory = false; - bool Relocatable = false; - bool StripDebug = false; - bool StripAll = false; - uint32_t ZStackSize = 0; - uint32_t MaxMemory = 0; - uint32_t GlobalBase = 0; - uint32_t InitialMemory = 0; + bool AllowUndefined; + bool CheckSignatures; + bool Demangle; + bool EmitRelocs; + bool ImportMemory; + bool Relocatable; + bool StripAll; + bool StripDebug; + uint32_t ZStackSize; + uint32_t MaxMemory; + uint32_t GlobalBase; + uint32_t InitialMemory; llvm::StringRef Entry; llvm::StringRef Sysroot; llvm::StringRef OutputFile; Index: wasm/Driver.cpp =================================================================== --- wasm/Driver.cpp +++ wasm/Driver.cpp @@ -203,7 +203,9 @@ // fail unless this symbol can be found. static void addSyntheticUndefinedFunction(StringRef Name) { log("injecting undefined func: " + Name); - Symtab->addUndefinedFunction(Name); + + static WasmSignature Signature = { {}, WASM_TYPE_NORESULT }; + Symtab->addUndefinedFunction(Name, &Signature); } static void printHelp(const char *Argv0) { @@ -291,6 +293,8 @@ } Config->AllowUndefined = Args.hasArg(OPT_allow_undefined); + Config->CheckSignatures = + Args.hasFlag(OPT_check_signatures, OPT_no_check_signatures, false); Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); Config->Entry = Args.getLastArgValue(OPT_entry); Config->ImportMemory = Args.hasArg(OPT_import_memory); Index: wasm/Options.td =================================================================== --- wasm/Options.td +++ wasm/Options.td @@ -29,11 +29,15 @@ def no_color_diagnostics: F<"no-color-diagnostics">, HelpText<"Do not use colors in diagnostics">; +def no_check_signatures: F<"no-check-signatures">, HelpText<"Don't check function signatures">; + def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, HelpText<"Path to file to write output">; def threads: F<"threads">, HelpText<"Run the linker multi-threaded">; +def check_signatures: F<"check-signatures">, HelpText<"Check function signatures">; + def v: Flag<["-"], "v">, HelpText<"Display the version number">; def version: F<"version">, HelpText<"Display the version number and exit">; Index: wasm/SymbolTable.h =================================================================== --- wasm/SymbolTable.h +++ wasm/SymbolTable.h @@ -18,6 +18,7 @@ #include "llvm/Support/raw_ostream.h" using llvm::object::WasmSymbol; +using llvm::wasm::WasmSignature; namespace lld { namespace wasm { @@ -46,12 +47,13 @@ void reportDuplicate(Symbol *Existing, InputFile *NewFile); void reportRemainingUndefines(); + ArrayRef getSymbols() const { return SymVector; } Symbol *find(StringRef Name); Symbol *addDefined(InputFile *F, const WasmSymbol *Sym, const InputSegment *Segment = nullptr); Symbol *addUndefined(InputFile *F, const WasmSymbol *Sym); - Symbol *addUndefinedFunction(StringRef Name); + Symbol *addUndefinedFunction(StringRef Name, const WasmSignature *Type); Symbol *addDefinedGlobal(StringRef Name); void addLazy(ArchiveFile *F, const Archive::Symbol *Sym); @@ -59,6 +61,7 @@ std::pair insert(StringRef Name); llvm::DenseMap SymMap; + std::vector SymVector; }; extern SymbolTable *Symtab; Index: wasm/SymbolTable.cpp =================================================================== --- wasm/SymbolTable.cpp +++ wasm/SymbolTable.cpp @@ -12,6 +12,7 @@ #include "Config.h" #include "Memory.h" #include "Strings.h" +#include "WriterUtils.h" #include "lld/Common/ErrorHandler.h" #include @@ -34,8 +35,7 @@ void SymbolTable::reportRemainingUndefines() { std::unordered_set Undefs; - for (auto &I : SymMap) { - Symbol *Sym = I.second; + for (Symbol *Sym : SymVector) { if (Sym->isUndefined() && !Sym->isWeak() && Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) { Undefs.insert(Sym); @@ -67,6 +67,7 @@ if (Sym) return {Sym, false}; Sym = make(Name, false); + SymVector.emplace_back(Sym); return {Sym, true}; } @@ -76,20 +77,62 @@ toString(NewFile)); } -static void checkSymbolTypes(Symbol *Existing, InputFile *F, - const WasmSymbol *New) { - if (Existing->isLazy()) +// Get the signature for a given function symbol, either by looking +// it up in function sections (for defined functions), of the imports section +// (for imported functions). +static const WasmSignature *getFunctionSig(const ObjFile &Obj, + const WasmSymbol &Sym) { + DEBUG(dbgs() << "getFunctionSig: " << Sym.Name << "\n"); + const WasmObjectFile *WasmObj = Obj.getWasmObj(); + uint32_t FunctionType; + if (Obj.isImportedFunction(Sym.ElementIndex)) { + const WasmImport &Import = WasmObj->imports()[Sym.ImportIndex]; + FunctionType = Import.SigIndex; + } else { + uint32_t FuntionIndex = Sym.ElementIndex - Obj.NumFunctionImports(); + FunctionType = WasmObj->functionTypes()[FuntionIndex]; + } + return &WasmObj->types()[FunctionType]; +} + +// Check the type of new symbol matches that of the symbol is replacing. +// For functions this can also involve verifying that the signatures match. +static void checkSymbolTypes(const Symbol &Existing, const InputFile &F, + const WasmSymbol &New, + const WasmSignature *NewSig) { + if (Existing.isLazy()) + return; + + bool NewIsFunction = New.Type == WasmSymbol::SymbolType::FUNCTION_EXPORT || + New.Type == WasmSymbol::SymbolType::FUNCTION_IMPORT; + + // First check the symbol types match (i.e. either both are function + // symbols or both are data symbols). + if (Existing.isFunction() != NewIsFunction) { + error("symbol type mismatch: " + New.Name + "\n>>> defined as " + + (Existing.isFunction() ? "Function" : "Global") + " in " + + toString(Existing.getFile()) + "\n>>> defined as " + + (NewIsFunction ? "Function" : "Global") + " in " + F.getName()); + return; + } + + // For function symbols, optionally check the function signature matches too. + if (!NewIsFunction || !Config->CheckSignatures) return; - bool NewIsFunction = New->Type == WasmSymbol::SymbolType::FUNCTION_EXPORT || - New->Type == WasmSymbol::SymbolType::FUNCTION_IMPORT; - if (Existing->isFunction() == NewIsFunction) + DEBUG(dbgs() << "checkSymbolTypes: " << New.Name << "\n"); + assert(NewSig); + + const WasmSignature &OldSig = Existing.getFunctionType(); + if (*NewSig == OldSig) return; - error("symbol type mismatch: " + New->Name + "\n>>> defined as " + - (Existing->isFunction() ? "Function" : "Global") + " in " + - toString(Existing->getFile()) + "\n>>> defined as " + - (NewIsFunction ? "Function" : "Global") + " in " + F->getName()); + if (Existing.getName() == Config->Entry) + return; + + error("function signature mismatch: " + New.Name + "\n>>> defined as " + + toString(OldSig) + " in " + toString(Existing.getFile()) + + "\n>>> defined as " + toString(*NewSig) + " in " + F.getName()); } Symbol *SymbolTable::addDefinedGlobal(StringRef Name) { @@ -110,19 +153,27 @@ Symbol *S; bool WasInserted; Symbol::Kind Kind = Symbol::DefinedFunctionKind; + const WasmSignature *NewSig = nullptr; if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT) Kind = Symbol::DefinedGlobalKind; + else + NewSig = getFunctionSig(*cast(F), *Sym); std::tie(S, WasInserted) = insert(Sym->Name); if (WasInserted) { - S->update(Kind, F, Sym, Segment); + S->update(Kind, F, Sym, Segment, NewSig); + } else if (S->isLazy()) { + // The existing symbol is lazy. Replace it without checking types since + // lazy symbols don't have any type information. + DEBUG(dbgs() << "replacing existing lazy symbol: " << Sym->Name << "\n"); + S->update(Kind, F, Sym, Segment, NewSig); } else if (!S->isDefined()) { // The existing symbol table entry is undefined. The new symbol replaces - // it + // it, after checkign the type matches DEBUG(dbgs() << "resolving existing undefined symbol: " << Sym->Name << "\n"); - checkSymbolTypes(S, F, Sym); - S->update(Kind, F, Sym, Segment); + checkSymbolTypes(*S, *F, *Sym, NewSig); + S->update(Kind, F, Sym, Segment, NewSig); } else if (Sym->isWeak()) { // the new symbol is weak we can ignore it DEBUG(dbgs() << "existing symbol takes precensence\n"); @@ -130,7 +181,8 @@ // the new symbol is not weak and the existing symbol is, so we replace // it DEBUG(dbgs() << "replacing existing weak symbol\n"); - S->update(Kind, F, Sym, Segment); + checkSymbolTypes(*S, *F, *Sym, NewSig); + S->update(Kind, F, Sym, Segment, NewSig); } else { // niether symbol is week. They conflict. reportDuplicate(S, F); @@ -138,14 +190,16 @@ return S; } -Symbol *SymbolTable::addUndefinedFunction(StringRef Name) { +Symbol *SymbolTable::addUndefinedFunction(StringRef Name, + const WasmSignature *Type) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(Name); - if (WasInserted) - S->update(Symbol::UndefinedFunctionKind); - else if (!S->isFunction()) + if (WasInserted) { + S->update(Symbol::UndefinedFunctionKind, nullptr, nullptr, nullptr, Type); + } else if (!S->isFunction()) { error("symbol type mismatch: " + Name); + } return S; } @@ -154,18 +208,21 @@ Symbol *S; bool WasInserted; Symbol::Kind Kind = Symbol::UndefinedFunctionKind; + const WasmSignature *NewSig = nullptr; if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_IMPORT) Kind = Symbol::UndefinedGlobalKind; + else + NewSig = getFunctionSig(*cast(F), *Sym); std::tie(S, WasInserted) = insert(Sym->Name); if (WasInserted) { - S->update(Kind, F, Sym); + S->update(Kind, F, Sym, nullptr, NewSig); } else if (S->isLazy()) { DEBUG(dbgs() << "resolved by existing lazy\n"); auto *AF = cast(S->getFile()); AF->addMember(&S->getArchiveSymbol()); } else if (S->isDefined()) { DEBUG(dbgs() << "resolved by existing\n"); - checkSymbolTypes(S, F, Sym); + checkSymbolTypes(*S, *F, *Sym, NewSig); } return S; } Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -16,6 +16,7 @@ using llvm::object::Archive; using llvm::object::WasmSymbol; +using llvm::wasm::WasmSignature; using llvm::wasm::WasmImport; using llvm::wasm::WasmExport; @@ -40,7 +41,7 @@ }; Symbol(StringRef Name, bool IsLocal) - : WrittenToSymtab(0), WrittenToNameSec(0), Name(Name), IsLocal(IsLocal) {} + : WrittenToNameSec(0), IsLocal(IsLocal), Name(Name) {} Kind getKind() const { return SymbolKind; } @@ -66,7 +67,8 @@ uint32_t getGlobalIndex() const; uint32_t getFunctionIndex() const; - uint32_t getFunctionTypeIndex() const; + + const WasmSignature &getFunctionType() const; uint32_t getOutputIndex() const; // Returns the virtual address of a defined global. @@ -81,25 +83,27 @@ void setOutputIndex(uint32_t Index); void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr, - const InputSegment *Segment = nullptr); + const InputSegment *Segment = nullptr, + const WasmSignature *Sig = nullptr); void setArchiveSymbol(const Archive::Symbol &Sym) { ArchiveSymbol = Sym; } const Archive::Symbol &getArchiveSymbol() { return ArchiveSymbol; } // This bit is used by Writer::writeNameSection() to prevent // symbols from being written to the symbol table more than once. - unsigned WrittenToSymtab : 1; unsigned WrittenToNameSec : 1; protected: + unsigned IsLocal : 1; + StringRef Name; - bool IsLocal; Archive::Symbol ArchiveSymbol = {nullptr, 0, 0}; Kind SymbolKind = InvalidKind; InputFile *File = nullptr; const WasmSymbol *Sym = nullptr; const InputSegment *Segment = nullptr; llvm::Optional OutputIndex; + const WasmSignature* FunctionType; }; } // namespace wasm Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -31,19 +31,9 @@ return Sym->ElementIndex; } -uint32_t Symbol::getFunctionTypeIndex() const { - assert(Sym->isFunction()); - ObjFile *Obj = cast(File); - if (Obj->isImportedFunction(Sym->ElementIndex)) { - const WasmImport &Import = Obj->getWasmObj()->imports()[Sym->ImportIndex]; - DEBUG(dbgs() << "getFunctionTypeIndex: import: " << Sym->ImportIndex - << " -> " << Import.SigIndex << "\n"); - return Import.SigIndex; - } - DEBUG(dbgs() << "getFunctionTypeIndex: non import: " << Sym->ElementIndex - << "\n"); - uint32_t FuntionIndex = Sym->ElementIndex - Obj->NumFunctionImports(); - return Obj->getWasmObj()->functionTypes()[FuntionIndex]; +const WasmSignature &Symbol::getFunctionType() const { + assert(FunctionType != nullptr); + return *FunctionType; } uint32_t Symbol::getVirtualAddress() const { @@ -74,11 +64,12 @@ } void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym, - const InputSegment *Seg) { + const InputSegment *Seg, const WasmSignature *Sig) { SymbolKind = K; File = F; Sym = WasmSym; Segment = Seg; + FunctionType = Sig; } bool Symbol::isWeak() const { return Sym && Sym->isWeak(); } Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -35,11 +35,6 @@ namespace { -// Needed for WasmSignatureDenseMapInfo -bool operator==(const WasmSignature &LHS, const WasmSignature &RHS) { - return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes; -} - // Traits for using WasmSignature in a DenseMap. struct WasmSignatureDenseMapInfo { static WasmSignature getEmptyKey() { @@ -72,6 +67,8 @@ private: void openFile(); + uint32_t getTypeIndex(const WasmSignature &Sig); + void writeSymbolExport(raw_ostream &OS, const Symbol &Sym); void assignSymbolIndexes(); void calculateImports(); void calculateOffsets(); @@ -112,6 +109,7 @@ uint32_t NumTableElems = 0; uint32_t NumElements = 0; uint32_t InitialTableOffset = 0; + bool ExportGlobalSymbols = false; std::vector Types; DenseMap TypeIndices; @@ -158,8 +156,8 @@ Import.Module = "env"; Import.Field = Sym->getName(); Import.Kind = WASM_EXTERNAL_FUNCTION; - auto *Obj = cast(Sym->getFile()); - Import.SigIndex = Obj->relocateTypeIndex(Sym->getFunctionTypeIndex()); + assert(TypeIndices.count(Sym->getFunctionType()) > 0); + Import.SigIndex = TypeIndices.lookup(Sym->getFunctionType()); writeImport(OS, Import); } @@ -179,9 +177,6 @@ Import.Field = Sym->getName(); Import.Kind = WASM_EXTERNAL_GLOBAL; Import.Global.Mutable = false; - assert(isa(Sym->getFile())); - // TODO(sbc): Set type of this import - // ObjFile* Obj = dyn_cast(Sym->getFile()); Import.Global.Type = WASM_TYPE_I32; // Sym->getGlobalType(); writeImport(OS, Import); } @@ -233,7 +228,7 @@ writeGlobal(OS, Global); } - if (Config->Relocatable || Config->EmitRelocs) { + if (ExportGlobalSymbols) { for (ObjFile *File : Symtab->ObjectFiles) { uint32_t GlobalIndex = File->NumGlobalImports(); for (const WasmGlobal &Global : File->getWasmObj()->globals()) { @@ -263,29 +258,50 @@ writeUleb128(OS, NumTableElems, "table max size"); } +void Writer::writeSymbolExport(raw_ostream &OS, const Symbol &Sym) { + log("Export: " + Sym.getName()); + WasmExport Export; + Export.Name = Sym.getName(); + Export.Index = Sym.getOutputIndex(); + if (Sym.isFunction()) + Export.Kind = WASM_EXTERNAL_FUNCTION; + else + Export.Kind = WASM_EXTERNAL_GLOBAL; + writeExport(OS, Export); +} + void Writer::createExportSection() { - // Memory is and main function are exported for executables. + // Memory and main function are exported for executables. bool ExportMemory = !Config->Relocatable && !Config->ImportMemory; bool ExportMain = !Config->Relocatable; - bool ExportOther = true; // Config->Relocatable; + + bool ExportFuncs = ExportGlobalSymbols; + bool ExportSymbols = ExportGlobalSymbols; uint32_t NumExports = 0; if (ExportMemory) ++NumExports; - if (ExportMain && !ExportOther) + if (ExportMain && !ExportFuncs) ++NumExports; - if (ExportOther) { - for (ObjFile *File : Symtab->ObjectFiles) { - for (Symbol *Sym : File->getSymbols()) { - if (!Sym->isFunction() || Sym->isLocal() || Sym->isUndefined() || - Sym->WrittenToSymtab) - continue; - Sym->WrittenToSymtab = true; - ++NumExports; - } + if (ExportSymbols) { + for (Symbol *S : Symtab->getSymbols()) { + if (!S->isGlobal() || S->isWeak() || !S->isDefined()) + continue; + // "__stack_pointer" can't be exports becasue it mutable + if (S->getOutputIndex() == 0) + continue; + ++NumExports; + } + } + + if (ExportFuncs) { + for (Symbol *S : Symtab->getSymbols()) { + if (!S->isFunction() || S->isWeak() || !S->isDefined()) + continue; + ++NumExports; } } @@ -311,7 +327,7 @@ if (!Sym->isFunction()) fatal("entry point is not a function: " + Sym->getName()); - if (!ExportOther) { + if (!ExportFuncs) { WasmExport MainExport; MainExport.Name = Config->Entry; MainExport.Kind = WASM_EXTERNAL_FUNCTION; @@ -321,27 +337,23 @@ } } - if (ExportOther) { - for (ObjFile *File : Symtab->ObjectFiles) { - for (Symbol *Sym : File->getSymbols()) { - if (!Sym->isFunction() || Sym->isLocal() | Sym->isUndefined() || - !Sym->WrittenToSymtab) - continue; - Sym->WrittenToSymtab = false; - log("Export: " + Sym->getName()); - WasmExport Export; - Export.Name = Sym->getName(); - Export.Index = Sym->getOutputIndex(); - if (Sym->isFunction()) - Export.Kind = WASM_EXTERNAL_FUNCTION; - else - Export.Kind = WASM_EXTERNAL_GLOBAL; - writeExport(OS, Export); - } + if (ExportFuncs) { + for (Symbol *S : Symtab->getSymbols()) { + if (!S->isFunction() || S->isWeak() || !S->isDefined()) + continue; + writeSymbolExport(OS, *S); } + } - // TODO(sbc): Export local symbols too, Even though they are not part - // of the symbol table? + if (ExportSymbols) { + for (Symbol *S : Symtab->getSymbols()) { + if (!S->isGlobal() || S->isWeak() || !S->isDefined()) + continue; + // "__stack_pointer" can't be exports becasue it mutable + if (S->getOutputIndex() == 0) + continue; + writeSymbolExport(OS, *S); + } } } @@ -577,18 +589,16 @@ NumFunctions += WasmFile->functions().size(); // Global Index - if (Config->Relocatable || Config->EmitRelocs) { + if (ExportGlobalSymbols) { File->GlobalIndexOffset = GlobalImports.size() - File->NumGlobalImports() + NumGlobals; NumGlobals += WasmFile->globals().size(); } // Memory - if (WasmFile->memories().size()) { - if (WasmFile->memories().size() > 1) { + if (WasmFile->memories().size()) + if (WasmFile->memories().size() > 1) fatal(File->getName() + ": contains more than one memory"); - } - } // Table uint32_t TableCount = WasmFile->tables().size(); @@ -618,33 +628,34 @@ } void Writer::calculateImports() { - for (ObjFile *File : Symtab->ObjectFiles) { - for (Symbol *Sym : File->getSymbols()) { - if (Sym->hasOutputIndex() || Sym->isDefined() || Sym->isWeak()) - continue; + for (Symbol *Sym : Symtab->getSymbols()) { + if (Sym->isDefined() || Sym->isWeak() || Sym->isLazy()) + continue; - if (Sym->isFunction()) { - Sym->setOutputIndex(FunctionImports.size()); - FunctionImports.push_back(Sym); - } else { - Sym->setOutputIndex(GlobalImports.size()); - GlobalImports.push_back(Sym); - } + if (Sym->isFunction()) { + log("Func Import :" + Sym->getName()); + Sym->setOutputIndex(FunctionImports.size()); + FunctionImports.push_back(Sym); + } else { + log("Global Import :" + Sym->getName()); + Sym->setOutputIndex(GlobalImports.size()); + GlobalImports.push_back(Sym); } } } +uint32_t Writer::getTypeIndex(const WasmSignature &Sig) { + auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size())); + if (Pair.second) + Types.push_back(&Sig); + return Pair.first->second; +} + void Writer::calculateTypes() { for (ObjFile *File : Symtab->ObjectFiles) { File->TypeMap.reserve(File->getWasmObj()->types().size()); - for (const WasmSignature &Sig : File->getWasmObj()->types()) { - auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size())); - if (Pair.second) - Types.push_back(&Sig); - - // Now we map the input files index to the index in the linked output - File->TypeMap.push_back(Pair.first->second); - } + for (const WasmSignature &Sig : File->getWasmObj()->types()) + File->TypeMap.push_back(getTypeIndex(Sig)); } } @@ -706,6 +717,10 @@ } void Writer::run() { + // For now, default to exporting all global symbols (both functions and + // data symbols). + ExportGlobalSymbols = true; // Config->Relocatable || Config->EmitRelocs; + if (!Config->Relocatable) InitialTableOffset = 1; Index: wasm/WriterUtils.h =================================================================== --- wasm/WriterUtils.h +++ wasm/WriterUtils.h @@ -16,6 +16,17 @@ using llvm::raw_ostream; +// Needed for WasmSignatureDenseMapInfo +inline bool operator==(const llvm::wasm::WasmSignature &LHS, + const llvm::wasm::WasmSignature &RHS) { + return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes; +} + +inline bool operator!=(const llvm::wasm::WasmSignature &LHS, + const llvm::wasm::WasmSignature &RHS) { + return !(LHS == RHS); +} + namespace lld { namespace wasm { @@ -58,6 +69,10 @@ void writeReloc(raw_ostream &OS, const OutputRelocation &Reloc); } // namespace wasm + +std::string toString(const llvm::wasm::ValType Type); +std::string toString(const llvm::wasm::WasmSignature &Sig); + } // namespace lld #endif // LLD_WASM_WRITERUTILS_H Index: wasm/WriterUtils.cpp =================================================================== --- wasm/WriterUtils.cpp +++ wasm/WriterUtils.cpp @@ -187,3 +187,32 @@ } } // namespace lld + +std::string lld::toString(ValType Type) { + switch (Type) { + case ValType::I32: + return "I32"; + case ValType::I64: + return "I64"; + case ValType::F32: + return "F32"; + case ValType::F64: + return "F64"; + } + llvm_unreachable("Invalid wasm::ValType"); +} + +std::string lld::toString(const WasmSignature &Sig) { + SmallString<128> S("("); + for (uint32_t Type : Sig.ParamTypes) { + if (S.size() != 1) + S += ", "; + S += toString(static_cast(Type)); + } + S += ") -> "; + if (Sig.ReturnType == WASM_TYPE_NORESULT) + S += "void"; + else + S += toString(static_cast(Sig.ReturnType)); + return S.str(); +}