diff --git a/lld/test/wasm/alias.s b/lld/test/wasm/alias.s --- a/lld/test/wasm/alias.s +++ b/lld/test/wasm/alias.s @@ -22,14 +22,6 @@ # CHECK-NEXT: ReturnTypes: [] # CHECK-NEXT: - Type: FUNCTION # CHECK-NEXT: FunctionTypes: [ 0 ] -# CHECK-NEXT: - Type: TABLE -# CHECK-NEXT: Tables: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: ElemType: FUNCREF -# CHECK-NEXT: Limits: -# CHECK-NEXT: Flags: [ HAS_MAX ] -# CHECK-NEXT: Initial: 0x1 -# CHECK-NEXT: Maximum: 0x1 # CHECK-NEXT: - Type: MEMORY # CHECK-NEXT: Memories: # CHECK-NEXT: - Initial: 0x2 diff --git a/lld/test/wasm/init-fini.ll b/lld/test/wasm/init-fini.ll --- a/lld/test/wasm/init-fini.ll +++ b/lld/test/wasm/init-fini.ll @@ -139,15 +139,15 @@ ; RELOC-NEXT: InitFunctions [ ; RELOC-NEXT: 0 (priority=101) ; RELOC-NEXT: 1 (priority=101) -; RELOC-NEXT: 14 (priority=101) -; RELOC-NEXT: 10 (priority=101) -; RELOC-NEXT: 20 (priority=101) -; RELOC-NEXT: 10 (priority=202) -; RELOC-NEXT: 22 (priority=202) +; RELOC-NEXT: 15 (priority=101) +; RELOC-NEXT: 11 (priority=101) +; RELOC-NEXT: 21 (priority=101) +; RELOC-NEXT: 11 (priority=202) +; RELOC-NEXT: 23 (priority=202) ; RELOC-NEXT: 0 (priority=1001) -; RELOC-NEXT: 16 (priority=1001) -; RELOC-NEXT: 10 (priority=2002) -; RELOC-NEXT: 24 (priority=2002) +; RELOC-NEXT: 17 (priority=1001) +; RELOC-NEXT: 11 (priority=2002) +; RELOC-NEXT: 25 (priority=2002) ; RELOC-NEXT: 9 (priority=4000) -; RELOC-NEXT: 18 (priority=4000) +; RELOC-NEXT: 19 (priority=4000) ; RELOC-NEXT: ] diff --git a/lld/test/wasm/local-symbols.ll b/lld/test/wasm/local-symbols.ll --- a/lld/test/wasm/local-symbols.ll +++ b/lld/test/wasm/local-symbols.ll @@ -35,14 +35,6 @@ ; CHECK-NEXT: ReturnTypes: [] ; CHECK-NEXT: - Type: FUNCTION ; CHECK-NEXT: FunctionTypes: [ 0, 1 ] -; CHECK-NEXT: - Type: TABLE -; CHECK-NEXT: Tables: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Flags: [ HAS_MAX ] -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: Maximum: 0x1 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x2 diff --git a/lld/test/wasm/locals-duplicate.test b/lld/test/wasm/locals-duplicate.test --- a/lld/test/wasm/locals-duplicate.test +++ b/lld/test/wasm/locals-duplicate.test @@ -254,40 +254,40 @@ ; RELOC-NEXT: - Type: CODE ; RELOC-NEXT: Relocations: ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 18 +; RELOC-NEXT: Index: 19 ; RELOC-NEXT: Offset: 0x13 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB ; RELOC-NEXT: Index: 3 ; RELOC-NEXT: Offset: 0x1C ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 19 +; RELOC-NEXT: Index: 20 ; RELOC-NEXT: Offset: 0x25 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 16 +; RELOC-NEXT: Index: 17 ; RELOC-NEXT: Offset: 0x2E ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB ; RELOC-NEXT: Index: 0 ; RELOC-NEXT: Offset: 0x37 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 17 +; RELOC-NEXT: Index: 18 ; RELOC-NEXT: Offset: 0x40 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 10 +; RELOC-NEXT: Index: 11 ; RELOC-NEXT: Offset: 0x58 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 22 +; RELOC-NEXT: Index: 23 ; RELOC-NEXT: Offset: 0x61 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 23 +; RELOC-NEXT: Index: 24 ; RELOC-NEXT: Offset: 0x6A ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 8 +; RELOC-NEXT: Index: 9 ; RELOC-NEXT: Offset: 0x73 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 20 +; RELOC-NEXT: Index: 21 ; RELOC-NEXT: Offset: 0x7C ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 21 +; RELOC-NEXT: Index: 22 ; RELOC-NEXT: Offset: 0x85 ; RELOC-NEXT: Functions: ; RELOC-NEXT: - Index: 0 @@ -410,87 +410,92 @@ ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 8 ; RELOC-NEXT: - Index: 8 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 +; RELOC-NEXT: - Index: 9 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func1 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 9 -; RELOC-NEXT: - Index: 9 +; RELOC-NEXT: - Index: 10 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global1B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 12 -; RELOC-NEXT: - Index: 10 +; RELOC-NEXT: - Index: 11 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global1 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Offset: 4 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 11 +; RELOC-NEXT: - Index: 12 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global2B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 13 -; RELOC-NEXT: - Index: 12 +; RELOC-NEXT: - Index: 13 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global3B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 14 -; RELOC-NEXT: - Index: 13 +; RELOC-NEXT: - Index: 14 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func1B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 15 -; RELOC-NEXT: - Index: 14 +; RELOC-NEXT: - Index: 15 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func2B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 16 -; RELOC-NEXT: - Index: 15 +; RELOC-NEXT: - Index: 16 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func3B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 17 -; RELOC-NEXT: - Index: 16 +; RELOC-NEXT: - Index: 17 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func1 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 0 -; RELOC-NEXT: - Index: 17 +; RELOC-NEXT: - Index: 18 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 2 -; RELOC-NEXT: - Index: 18 +; RELOC-NEXT: - Index: 19 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global1 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 19 +; RELOC-NEXT: - Index: 20 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 2 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 20 +; RELOC-NEXT: - Index: 21 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func2 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 10 -; RELOC-NEXT: - Index: 21 +; RELOC-NEXT: - Index: 22 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 11 -; RELOC-NEXT: - Index: 22 +; RELOC-NEXT: - Index: 23 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global2 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 1 ; RELOC-NEXT: Offset: 4 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 23 +; RELOC-NEXT: - Index: 24 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] diff --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll --- a/lld/test/wasm/pie.ll +++ b/lld/test/wasm/pie.ll @@ -41,14 +41,6 @@ ; CHECK: - Type: IMPORT ; CHECK-NEXT: Imports: ; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __indirect_function_table -; CHECK-NEXT: Kind: TABLE -; CHECK-NEXT: Table: -; CHECK-NEXT: Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: - Module: env ; CHECK-NEXT: Field: __stack_pointer ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 @@ -63,6 +55,14 @@ ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 ; CHECK-NEXT: GlobalMutable: false +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __indirect_function_table +; CHECK-NEXT: Kind: TABLE +; CHECK-NEXT: Table: +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: ElemType: FUNCREF +; CHECK-NEXT: Limits: +; CHECK-NEXT: Initial: 0x1 ; CHECK: - Type: START ; CHECK-NEXT: StartFunction: 2 diff --git a/lld/test/wasm/section-symbol-relocs.yaml b/lld/test/wasm/section-symbol-relocs.yaml --- a/lld/test/wasm/section-symbol-relocs.yaml +++ b/lld/test/wasm/section-symbol-relocs.yaml @@ -54,8 +54,8 @@ # RELOC-NEXT: - Index: 0 # RELOC-NEXT: Kind: SECTION # RELOC-NEXT: Flags: [ BINDING_LOCAL ] -# RELOC-NEXT: Section: 2 +# RELOC-NEXT: Section: 1 # RELOC-NEXT: - Index: 1 # RELOC-NEXT: Kind: SECTION # RELOC-NEXT: Flags: [ BINDING_LOCAL ] -# RELOC-NEXT: Section: 3 +# RELOC-NEXT: Section: 2 diff --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll --- a/lld/test/wasm/shared.ll +++ b/lld/test/wasm/shared.ll @@ -69,14 +69,6 @@ ; CHECK-NEXT: Memory: ; CHECK-NEXT: Initial: 0x1 ; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __indirect_function_table -; CHECK-NEXT: Kind: TABLE -; CHECK-NEXT: Table: -; CHECK-NEXT: Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Initial: 0x2 -; CHECK-NEXT: - Module: env ; CHECK-NEXT: Field: __stack_pointer ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 @@ -95,6 +87,14 @@ ; CHECK-NEXT: Field: func_external ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: SigIndex: 1 +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __indirect_function_table +; CHECK-NEXT: Kind: TABLE +; CHECK-NEXT: Table: +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: ElemType: FUNCREF +; CHECK-NEXT: Limits: +; CHECK-NEXT: Initial: 0x2 ; CHECK-NEXT: - Module: GOT.mem ; CHECK-NEXT: Field: indirect_func ; CHECK-NEXT: Kind: GLOBAL diff --git a/lld/test/wasm/signature-mismatch.ll b/lld/test/wasm/signature-mismatch.ll --- a/lld/test/wasm/signature-mismatch.ll +++ b/lld/test/wasm/signature-mismatch.ll @@ -80,17 +80,22 @@ ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Size: 4 ; RELOC-NEXT: - Index: 3 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 +; RELOC-NEXT: - Index: 4 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: call_ret32 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 3 -; RELOC-NEXT: - Index: 4 +; RELOC-NEXT: - Index: 5 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: ret32_address ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Segment: 1 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 5 +; RELOC-NEXT: - Index: 6 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: 'signature_mismatch:ret32' ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] diff --git a/lld/test/wasm/stack-pointer.ll b/lld/test/wasm/stack-pointer.ll --- a/lld/test/wasm/stack-pointer.ll +++ b/lld/test/wasm/stack-pointer.ll @@ -30,14 +30,6 @@ ; CHECK-NEXT: GlobalMutable: true ; CHECK-NEXT: - Type: FUNCTION ; CHECK-NEXT: FunctionTypes: [ 0 ] -; CHECK-NEXT: - Type: TABLE -; CHECK-NEXT: Tables: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Flags: [ HAS_MAX ] -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: Maximum: 0x1 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x0 diff --git a/lld/test/wasm/weak-alias.ll b/lld/test/wasm/weak-alias.ll --- a/lld/test/wasm/weak-alias.ll +++ b/lld/test/wasm/weak-alias.ll @@ -276,6 +276,11 @@ ; RELOC-NEXT: Name: call_direct_ptr ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 5 +; RELOC-NEXT: - Index: 8 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 ; RELOC-NEXT: - Type: CUSTOM ; RELOC-NEXT: Name: name ; RELOC-NEXT: FunctionNames: diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include "InputChunks.h" #include "InputGlobal.h" +#include "InputTable.h" #include "MarkLive.h" #include "SymbolTable.h" #include "Writer.h" @@ -787,6 +788,58 @@ symtab->wrap(w.sym, w.real, w.wrap); } +static TableSymbol *provideIndirectFunctionTable(const StringRef &name) { + const uint32_t invalidIndex = -1; + WasmLimits limits{0, 0, 0}; // Set by the writer. + WasmTableType type{uint8_t(ValType::FUNCREF), limits}; + WasmTable desc{invalidIndex, type, name}; + InputTable *table = make(desc, nullptr); + uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN; + TableSymbol *sym = symtab->addSyntheticTable(name, flags, table); + sym->markLive(); + sym->forceExport = config->exportTable; + return sym; +} + +static TableSymbol * +createUndefinedIndirectFunctionTable(const StringRef &name) { + WasmLimits limits{0, 0, 0}; // Set by the writer. + WasmTableType *type = make(); + type->ElemType = uint8_t(ValType::FUNCREF); + type->Limits = limits; + StringRef module(defaultModule); + uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN; + flags |= WASM_SYMBOL_UNDEFINED; + Symbol *sym = + symtab->addUndefinedTable(name, name, module, flags, nullptr, type); + sym->markLive(); + sym->forceExport = config->exportTable; + return cast(sym); +} + +static TableSymbol *resolveIndirectFunctionTable() { + // Even though we may not need a table, if the user explicitly specified + // --import-table or --export-table, ensure a table is residualized. + const StringRef name(functionTableName); + if (config->importTable) + return createUndefinedIndirectFunctionTable(name); + if (config->exportTable) + return provideIndirectFunctionTable(name); + + // Otherwise, check to the symtab to find the indirect function table. + if (Symbol *sym = symtab->find(name)) { + if (sym->isLive()) { + if (auto *t = dyn_cast(sym)) { + return t->isDefined() ? t : provideIndirectFunctionTable(name); + } + } + } + + // An indirect function table will only be present in the symbol table if + // needed by a reloc; if we get here, we don't need one. + return nullptr; +} + void LinkerDriver::linkerMain(ArrayRef argsArr) { WasmOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -976,6 +1029,12 @@ // Do size optimizations: garbage collection markLive(); + // Provide the indirect funciton table if needed. + WasmSym::indirectFunctionTable = resolveIndirectFunctionTable(); + + if (errorCount()) + return; + // Write the result to the file. writeResult(); } diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -157,6 +157,7 @@ Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly); bool isExcludedByComdat(InputChunk *chunk) const; + void synthesizeTableSymbols(); std::unique_ptr wasmObj; }; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -309,6 +309,54 @@ } } +void ObjFile::synthesizeTableSymbols() { + uint32_t tableNumber = 0; + const WasmGlobalType *globalType = nullptr; + const WasmEventType *eventType = nullptr; + const WasmSignature *signature = nullptr; + if (wasmObj->getNumImportedTables()) { + for (const auto &import : wasmObj->imports()) { + if (import.Kind == WASM_EXTERNAL_TABLE) { + auto *info = make(); + // FIXME: Synthesize as Module.Field. Need somewhere to put the + // string backing buffer though. + info->Name = import.Field; + info->Kind = WASM_SYMBOL_TYPE_TABLE; + info->ImportModule = import.Module; + info->ImportName = import.Field; + info->Flags = WASM_SYMBOL_UNDEFINED; + info->Flags |= WASM_SYMBOL_NO_STRIP; + info->ElementIndex = tableNumber++; + LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: " + << info->Name << "\n"); + auto *wasmSym = make(*info, globalType, &import.Table, + eventType, signature); + symbols.push_back(createUndefined(*wasmSym, false)); + // Because there are no TABLE_NUMBER relocs in this case, we can't + // compute accurate liveness info; instead, just mark the symbol as + // always live. + symbols.back()->markLive(); + } + } + } + for (const auto &table : tables) { + auto *info = make(); + // Empty name. + info->Kind = WASM_SYMBOL_TYPE_TABLE; + info->Flags = WASM_SYMBOL_BINDING_LOCAL; + info->Flags |= WASM_SYMBOL_VISIBILITY_HIDDEN; + info->Flags |= WASM_SYMBOL_NO_STRIP; + info->ElementIndex = tableNumber++; + LLVM_DEBUG(dbgs() << "Synthesizing symbol for table definition: " + << info->Name << "\n"); + auto *wasmSym = make(*info, globalType, &table->getType(), + eventType, signature); + symbols.push_back(createDefined(*wasmSym)); + // Mark live, for the same reasons as for imported tables. + symbols.back()->markLive(); + } +} + void ObjFile::parse(bool ignoreComdats) { // Parse a memory buffer as a wasm file. LLVM_DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n"); @@ -424,8 +472,11 @@ // Populate `Symbols` based on the symbols in the object. symbols.reserve(wasmObj->getNumberOfSymbols()); + bool haveTableSymbol = false; for (const SymbolRef &sym : wasmObj->symbols()) { const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl()); + if (wasmSym.isTypeTable()) + haveTableSymbol = true; if (wasmSym.isDefined()) { // createDefined may fail if the symbol is comdat excluded in which case // we fall back to creating an undefined symbol @@ -437,6 +488,13 @@ size_t idx = symbols.size(); symbols.push_back(createUndefined(wasmSym, isCalledDirectly[idx])); } + + // As a stopgap measure while implementing table support, if the object file + // has table definitions or imports but no table symbols, synthesize symbols + // for those tables. Mark as NO_STRIP to ensure they reach the output file, + // even if there are no TABLE_NUMBER relocs against them. + if (!haveTableSymbol) + synthesizeTableSymbols(); } bool ObjFile::isExcludedByComdat(InputChunk *chunk) const { diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -568,6 +568,11 @@ // Used in PIC code for offset of global data static UndefinedGlobal *memoryBase; static DefinedData *definedMemoryBase; + + // __indirect_function_table + // Used as an address space for function pointers, with each function that is + // used as a function pointer being allocated a slot. + static TableSymbol *indirectFunctionTable; }; // A buffer class that is large enough to hold any Symbol-derived diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -91,6 +91,7 @@ DefinedData *WasmSym::definedTableBase; UndefinedGlobal *WasmSym::memoryBase; DefinedData *WasmSym::definedMemoryBase; +TableSymbol *WasmSym::indirectFunctionTable; WasmSymbolType Symbol::getWasmType() const { if (isa(this)) diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -98,7 +98,7 @@ class ImportSection : public SyntheticSection { public: - ImportSection(); + ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {} bool isNeeded() const override { return getNumImports() > 0; } void writeBody() override; void addImport(Symbol *sym); @@ -150,16 +150,7 @@ public: TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {} - bool isNeeded() const override { - // The linker currently always residualizes an indirect function table, so - // unless it is imported, we need a table section. FIXME: Treat - // __indirect_function_table as a normal symbol, and only residualize a - // table section as needed. - if (!config->importTable) - return true; - return inputTables.size() > 0; - } - + bool isNeeded() const override { return inputTables.size() > 0; }; void writeBody() override; void addTable(InputTable *table); diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -92,20 +92,11 @@ writeSig(bodyOutputStream, *sig); } -ImportSection::ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) { - // FIXME: Remove when we treat __indirect_function_table as any other symbol. - if (config->importTable) { - numImportedTables++; - } -} - uint32_t ImportSection::getNumImports() const { assert(isSealed); uint32_t numImports = importedSymbols.size() + gotSymbols.size(); if (config->importMemory) ++numImports; - if (config->importTable) - ++numImports; return numImports; } @@ -154,17 +145,6 @@ writeImport(os, import); } - if (config->importTable) { - uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); - WasmImport import; - import.Module = defaultModule; - import.Field = functionTableName; - import.Kind = WASM_EXTERNAL_TABLE; - import.Table.ElemType = WASM_TYPE_FUNCREF; - import.Table.Limits = {0, tableSize, 0}; - writeImport(os, import); - } - for (const Symbol *sym : importedSymbols) { WasmImport import; if (auto *f = dyn_cast(sym)) { @@ -230,26 +210,9 @@ } void TableSection::writeBody() { - bool hasIndirectFunctionTable = !config->importTable; - - uint32_t tableCount = inputTables.size(); - if (hasIndirectFunctionTable) - tableCount++; - raw_ostream &os = bodyOutputStream; - writeUleb128(os, tableCount, "table count"); - - if (hasIndirectFunctionTable) { - uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); - WasmLimits limits; - if (config->growableTable) - limits = {0, tableSize, 0}; - else - limits = {WASM_LIMITS_FLAG_HAS_MAX, tableSize, tableSize}; - writeTableType(os, WasmTableType{WASM_TYPE_FUNCREF, limits}); - } - + writeUleb128(os, inputTables.size(), "table count"); for (const InputTable *table : inputTables) writeTableType(os, table->getType()); } diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -745,6 +745,19 @@ } } +static void finalizeIndirectFunctionTable() { + if (!WasmSym::indirectFunctionTable) + return; + + uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); + WasmLimits limits = {0, tableSize, 0}; + if (WasmSym::indirectFunctionTable->isDefined() && !config->growableTable) { + limits.Flags |= WASM_LIMITS_FLAG_HAS_MAX; + limits.Maximum = limits.Initial; + } + WasmSym::indirectFunctionTable->setLimits(limits); +} + static void scanRelocations() { for (ObjFile *file : symtab->objectFiles) { LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n"); @@ -755,6 +768,11 @@ for (auto &p : file->customSections) scanRelocations(p); } + // Now that all TABLE_INDEX relocations have been visited and all + // indirectly-callable functions have table indexes assigned, finalize the + // size of the function address space by setting limits on the indirect + // function table. + finalizeIndirectFunctionTable(); } void Writer::assignIndexes() {