Index: lld/trunk/test/wasm/call-indirect.ll =================================================================== --- lld/trunk/test/wasm/call-indirect.ll +++ lld/trunk/test/wasm/call-indirect.ll @@ -43,8 +43,8 @@ ; CHECK-NEXT: - ElemType: ANYFUNC ; CHECK-NEXT: Limits: ; CHECK-NEXT: Flags: 0x00000001 -; CHECK-NEXT: Initial: 0x00000004 -; CHECK-NEXT: Maximum: 0x00000004 +; CHECK-NEXT: Initial: 0x00000003 +; CHECK-NEXT: Maximum: 0x00000003 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x00000002 @@ -77,7 +77,7 @@ ; CHECK-NEXT: - Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 1 -; CHECK-NEXT: Functions: [ 0, 2, 2 ] +; CHECK-NEXT: Functions: [ 0, 2 ] ; CHECK-NEXT: - Type: CODE ; CHECK-NEXT: Functions: ; CHECK: - Locals: @@ -90,7 +90,7 @@ ; CHECK-NEXT: Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 1024 -; CHECK-NEXT: Content: '010000000200000003000000' +; CHECK-NEXT: Content: '010000000200000002000000' ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: linking ; CHECK-NEXT: DataSize: 12 Index: lld/trunk/test/wasm/weak-symbols.ll =================================================================== --- lld/trunk/test/wasm/weak-symbols.ll +++ lld/trunk/test/wasm/weak-symbols.ll @@ -31,8 +31,8 @@ ; CHECK-NEXT: - ElemType: ANYFUNC ; CHECK-NEXT: Limits: ; CHECK-NEXT: Flags: 0x00000001 -; CHECK-NEXT: Initial: 0x00000003 -; CHECK-NEXT: Maximum: 0x00000003 +; CHECK-NEXT: Initial: 0x00000002 +; CHECK-NEXT: Maximum: 0x00000002 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x00000002 @@ -65,7 +65,7 @@ ; CHECK-NEXT: - Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 1 -; CHECK-NEXT: Functions: [ 1, 1 ] +; CHECK-NEXT: Functions: [ 1 ] ; CHECK-NEXT: - Type: CODE ; CHECK-NEXT: Functions: ; CHECK-NEXT: - Locals: @@ -77,7 +77,7 @@ ; CHECK-NEXT: - Locals: ; CHECK-NEXT: Body: 41020B ; CHECK-NEXT: - Locals: -; CHECK-NEXT: Body: 4182808080000B +; CHECK-NEXT: Body: 4181808080000B ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: linking ; CHECK-NEXT: DataSize: 0 Index: lld/trunk/wasm/InputFiles.h =================================================================== --- lld/trunk/wasm/InputFiles.h +++ lld/trunk/wasm/InputFiles.h @@ -103,7 +103,6 @@ size_t NumGlobalImports() const { return GlobalImports; } int32_t FunctionIndexOffset = 0; - int32_t TableIndexOffset = 0; const WasmSection *CodeSection = nullptr; std::vector CodeRelocations; int32_t CodeOffset = 0; @@ -113,6 +112,7 @@ std::vector Segments; ArrayRef getSymbols() { return Symbols; } + ArrayRef getTableSymbols() { return TableSymbols; } private: Symbol *createDefined(const WasmSymbol &Sym, @@ -120,17 +120,21 @@ Symbol *createUndefined(const WasmSymbol &Sym); void initializeSymbols(); InputSegment *getSegment(const WasmSymbol &WasmSym); - const Symbol *getFunctionSymbol(uint32_t Index) const; - const Symbol *getGlobalSymbol(uint32_t Index) const; + Symbol *getFunctionSymbol(uint32_t FunctionIndex) const; + Symbol *getTableSymbol(uint32_t TableIndex) const; + Symbol *getGlobalSymbol(uint32_t GlobalIndex) const; // List of all symbols referenced or defined by this file. std::vector Symbols; // List of all function symbols indexed by the function index space - std::vector FunctionSymbols; + std::vector FunctionSymbols; // List of all global symbols indexed by the global index space - std::vector GlobalSymbols; + std::vector GlobalSymbols; + + // List of all indirect symbols indexed by table index space. + std::vector TableSymbols; uint32_t GlobalImports = 0; uint32_t FunctionImports = 0; Index: lld/trunk/wasm/InputFiles.cpp =================================================================== --- lld/trunk/wasm/InputFiles.cpp +++ lld/trunk/wasm/InputFiles.cpp @@ -46,7 +46,6 @@ log("reloc info for: " + getName() + "\n" + " FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" + " NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" + - " TableIndexOffset : " + Twine(TableIndexOffset) + "\n" + " NumGlobalImports : " + Twine(NumGlobalImports()) + "\n"); } @@ -54,11 +53,15 @@ return Index < NumFunctionImports(); } -const Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const { +Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const { return FunctionSymbols[Index]; } -const Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const { +Symbol *ObjFile::getTableSymbol(uint32_t Index) const { + return TableSymbols[Index]; +} + +Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const { return GlobalSymbols[Index]; } @@ -67,7 +70,7 @@ } uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const { - const Symbol *Sym = getFunctionSymbol(Original); + Symbol *Sym = getFunctionSymbol(Original); uint32_t Index = Sym->getOutputIndex(); DEBUG(dbgs() << "relocateFunctionIndex: " << toString(*Sym) << ": " << Original << " -> " << Index << "\n"); @@ -79,11 +82,15 @@ } uint32_t ObjFile::relocateTableIndex(uint32_t Original) const { - return Original + TableIndexOffset; + Symbol *Sym = getTableSymbol(Original); + uint32_t Index = Sym->getTableIndex(); + DEBUG(dbgs() << "relocateTableIndex: " << toString(*Sym) << ": " << Original + << " -> " << Index << "\n"); + return Index; } uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const { - const Symbol *Sym = getGlobalSymbol(Original); + Symbol *Sym = getGlobalSymbol(Original); uint32_t Index = Sym->getOutputIndex(); DEBUG(dbgs() << "relocateGlobalIndex: " << toString(*Sym) << ": " << Original << " -> " << Index << "\n"); @@ -152,9 +159,11 @@ for (const WasmSegment &Seg : WasmObj->dataSegments()) Segments.emplace_back(make(&Seg, this)); - Symbol *S; + // Populate `FunctionSymbols` and `GlobalSymbols` based on the WasmSymbols + // in the object for (const SymbolRef &Sym : WasmObj->symbols()) { const WasmSymbol &WasmSym = WasmObj->getWasmSymbol(Sym.getRawDataRefImpl()); + Symbol *S; switch (WasmSym.Type) { case WasmSymbol::SymbolType::FUNCTION_IMPORT: case WasmSymbol::SymbolType::GLOBAL_IMPORT: @@ -183,8 +192,26 @@ } } - DEBUG(dbgs() << "Functions: " << FunctionSymbols.size() << "\n"); - DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n"); + // Populate `TableSymbols` with all symbols that are called indirectly + uint32_t SegmentCount = WasmObj->elements().size(); + if (SegmentCount) { + if (SegmentCount > 1) + fatal(getName() + ": contains more than one element segment"); + const WasmElemSegment &Segment = WasmObj->elements()[0]; + if (Segment.Offset.Opcode != WASM_OPCODE_I32_CONST) + fatal(getName() + ": unsupported element segment"); + if (Segment.TableIndex != 0) + fatal(getName() + ": unsupported table index in elem segment"); + if (Segment.Offset.Value.Int32 != 0) + fatal(getName() + ": unsupported element segment offset"); + TableSymbols.reserve(Segment.Functions.size()); + for (uint64_t FunctionIndex : Segment.Functions) + TableSymbols.push_back(getFunctionSymbol(FunctionIndex)); + } + + DEBUG(dbgs() << "TableSymbols: " << TableSymbols.size() << "\n"); + DEBUG(dbgs() << "Functions : " << FunctionSymbols.size() << "\n"); + DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n"); } Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) { Index: lld/trunk/wasm/Symbols.h =================================================================== --- lld/trunk/wasm/Symbols.h +++ lld/trunk/wasm/Symbols.h @@ -72,6 +72,7 @@ bool hasFunctionType() const { return FunctionType != nullptr; } const WasmSignature &getFunctionType() const; uint32_t getOutputIndex() const; + uint32_t getTableIndex() const { return TableIndex.getValue(); } // Returns the virtual address of a defined global. // Only works for globals, not functions. @@ -84,6 +85,12 @@ // space of the output object. void setOutputIndex(uint32_t Index); + // Returns true if a table index has been set for this symbol + bool hasTableIndex() const { return TableIndex.hasValue(); } + + // Set the table index of the symbol + void setTableIndex(uint32_t Index); + void setVirtualAddress(uint32_t VA); void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr, @@ -108,6 +115,7 @@ const WasmSymbol *Sym = nullptr; const InputSegment *Segment = nullptr; llvm::Optional OutputIndex; + llvm::Optional TableIndex; llvm::Optional VirtualAddress; const WasmSignature *FunctionType; }; Index: lld/trunk/wasm/Symbols.cpp =================================================================== --- lld/trunk/wasm/Symbols.cpp +++ lld/trunk/wasm/Symbols.cpp @@ -67,10 +67,16 @@ void Symbol::setOutputIndex(uint32_t Index) { DEBUG(dbgs() << "setOutputIndex " << Name << " -> " << Index << "\n"); - assert(!hasOutputIndex()); + assert(!OutputIndex.hasValue()); OutputIndex = Index; } +void Symbol::setTableIndex(uint32_t Index) { + DEBUG(dbgs() << "setTableIndex " << Name << " -> " << Index << "\n"); + assert(!TableIndex.hasValue()); + TableIndex = Index; +} + void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym, const InputSegment *Seg, const WasmSignature *Sig) { SymbolKind = K; Index: lld/trunk/wasm/Writer.cpp =================================================================== --- lld/trunk/wasm/Writer.cpp +++ lld/trunk/wasm/Writer.cpp @@ -103,10 +103,7 @@ uint64_t FileSize = 0; uint32_t DataSize = 0; uint32_t NumFunctions = 0; - uint32_t NumGlobals = 0; uint32_t NumMemoryPages = 0; - uint32_t NumTableElems = 0; - uint32_t NumElements = 0; uint32_t InitialTableOffset = 0; std::vector Types; @@ -114,6 +111,7 @@ std::vector FunctionImports; std::vector GlobalImports; std::vector DefinedGlobals; + std::vector IndirectFunctions; // Elements that are used to construct the final output std::string Header; @@ -236,14 +234,24 @@ } void Writer::createTableSection() { + // Always output a table section, even if there are no indirect calls. + // There are two reasons for this: + // 1. For executables it is useful to have an empty table slot at 0 + // which can be filled with a null function call handler. + // 2. If we don't do this, any program that contains a call_indirect but + // no address-taken function will fail at validation time since it is + // a validation error to include a call_indirect instruction if there + // is not table. + uint32_t TableSize = InitialTableOffset + IndirectFunctions.size(); + SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE); raw_ostream &OS = Section->getStream(); writeUleb128(OS, 1, "table count"); writeSleb128(OS, WASM_TYPE_ANYFUNC, "table type"); writeUleb128(OS, WASM_LIMITS_FLAG_HAS_MAX, "table flags"); - writeUleb128(OS, NumTableElems, "table initial size"); - writeUleb128(OS, NumTableElems, "table max size"); + writeUleb128(OS, TableSize, "table initial size"); + writeUleb128(OS, TableSize, "table max size"); } void Writer::createExportSection() { @@ -309,7 +317,7 @@ void Writer::createStartSection() {} void Writer::createElemSection() { - if (!NumElements) + if (IndirectFunctions.empty()) return; SyntheticSection *Section = createSyntheticSection(WASM_SEC_ELEM); @@ -321,13 +329,14 @@ InitExpr.Opcode = WASM_OPCODE_I32_CONST; InitExpr.Value.Int32 = InitialTableOffset; writeInitExpr(OS, InitExpr); - writeUleb128(OS, NumElements, "elem count"); + writeUleb128(OS, IndirectFunctions.size(), "elem count"); - for (ObjFile *File : Symtab->ObjectFiles) - for (const WasmElemSegment &Segment : File->getWasmObj()->elements()) - for (uint64_t FunctionIndex : Segment.Functions) - writeUleb128(OS, File->relocateFunctionIndex(FunctionIndex), - "function index"); + uint32_t TableIndex = InitialTableOffset; + for (const Symbol *Sym : IndirectFunctions) { + assert(Sym->getTableIndex() == TableIndex); + writeUleb128(OS, Sym->getOutputIndex(), "function index"); + ++TableIndex; + } } void Writer::createCodeSection() { @@ -526,8 +535,6 @@ } void Writer::calculateOffsets() { - NumTableElems = InitialTableOffset; - for (ObjFile *File : Symtab->ObjectFiles) { const WasmObjectFile *WasmFile = File->getWasmObj(); @@ -537,34 +544,8 @@ NumFunctions += WasmFile->functions().size(); // Memory - if (WasmFile->memories().size()) { - if (WasmFile->memories().size() > 1) { - fatal(File->getName() + ": contains more than one memory"); - } - } - - // Table - uint32_t TableCount = WasmFile->tables().size(); - if (TableCount) { - if (TableCount > 1) - fatal(File->getName() + ": contains more than one table"); - File->TableIndexOffset = NumTableElems; - NumTableElems += WasmFile->tables()[0].Limits.Initial; - } - - // Elem - uint32_t SegmentCount = WasmFile->elements().size(); - if (SegmentCount) { - if (SegmentCount > 1) - fatal(File->getName() + ": contains more than element segment"); - - const WasmElemSegment &Segment = WasmFile->elements()[0]; - if (Segment.TableIndex != 0) - fatal(File->getName() + ": unsupported table index"); - if (Segment.Offset.Value.Int32 != 0) - fatal(File->getName() + ": unsupported segment offset"); - NumElements += Segment.Functions.size(); - } + if (WasmFile->memories().size() > 1) + fatal(File->getName() + ": contains more than one memory"); } } @@ -611,8 +592,11 @@ if (Config->EmitRelocs) DefinedGlobals.reserve(Symtab->getSymbols().size()); + uint32_t TableIndex = InitialTableOffset; + for (ObjFile *File : Symtab->ObjectFiles) { DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n"); + for (Symbol *Sym : File->getSymbols()) { // Assign indexes for symbols defined with this file. if (!Sym->isDefined() || File != Sym->getFile()) @@ -626,6 +610,13 @@ Sym->setOutputIndex(GlobalIndex++); } } + + for (Symbol *Sym : File->getTableSymbols()) { + if (!Sym->hasTableIndex()) { + Sym->setTableIndex(TableIndex++); + IndirectFunctions.emplace_back(Sym); + } + } } } @@ -679,12 +670,12 @@ calculateOffsets(); if (errorHandler().Verbose) { - log("NumFunctions : " + Twine(NumFunctions)); - log("NumGlobals : " + Twine(NumGlobals)); - log("NumImports : " + + log("Defined Functions: " + Twine(NumFunctions)); + log("Defined Globals : " + Twine(DefinedGlobals.size())); + log("Function Imports : " + Twine(FunctionImports.size())); + log("Global Imports : " + Twine(GlobalImports.size())); + log("Total Imports : " + Twine(FunctionImports.size() + GlobalImports.size())); - log("FunctionImports : " + Twine(FunctionImports.size())); - log("GlobalImports : " + Twine(GlobalImports.size())); for (ObjFile *File : Symtab->ObjectFiles) File->dumpInfo(); }