# 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
; 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: ]
; 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
; 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
; 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 ]
; 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
; 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
# 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
; 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
; 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
; 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 ]
; 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
; 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:
#include "Config.h"
#include "InputChunks.h"
#include "InputGlobal.h"
+#include "InputTable.h"
#include "MarkLive.h"
#include "SymbolTable.h"
#include "Writer.h"
symtab->wrap(w.sym, w.real, w.wrap);
}
+static TableSymbol *createDefinedIndirectFunctionTable(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<InputTable>(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(StringRef name) {
+ WasmLimits limits{0, 0, 0}; // Set by the writer.
+ WasmTableType *type = make<WasmTableType>();
+ 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<TableSymbol>(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.
+ if (config->importTable)
+ return createUndefinedIndirectFunctionTable(functionTableName);
+ if (config->exportTable)
+ return createDefinedIndirectFunctionTable(functionTableName);
+
+ // Otherwise, check to the symtab to find the indirect function table.
+ if (Symbol *sym = symtab->find(functionTableName)) {
+ if (sym->isLive()) {
+ if (auto *t = dyn_cast<TableSymbol>(sym)) {
+ return t->isDefined()
+ ? t
+ : createDefinedIndirectFunctionTable(functionTableName);
+ }
+ }
+ }
+
+ // 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<const char *> argsArr) {
WasmOptTable parser;
opt::InputArgList args = parser.parse(argsArr.slice(1));
// 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();
}
}
}
+// Since LLVM 12, we expect that if an input file defines or uses a table, it
+// declares the tables using symbols and records each use with a relocation.
+// This way when the linker combines inputs, it can collate the tables used by
+// the inputs, assigning them distinct table numbers, and renumber all the uses
+// as appropriate. At the same time, the linker has special logic to build the
+// indirect function table if it is needed.
+//
+// However, object files produced by LLVM 11 and earlier neither write table
+// symbols nor record relocations, and yet still use tables via call_indirect,
+// and via function pointer bitcasts. We can detect these object files, as they
+// declare tables as imports or define them locally, but don't have table
+// symbols. synthesizeTableSymbols serves as a shim when loading these older
+// input files, defining the missing symbols to allow the indirect function
+// table to be built.
+//
+// Table uses in these older files won't be relocated, as they have no
+// relocations. In practice this isn't a problem, as these object files
+// typically just declare a single table named __indirect_function_table and
+// having table number 0, so relocation would be idempotent anyway.
+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<WasmSymbolInfo>();
+ 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<WasmSymbol>(*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<llvm::wasm::WasmSymbolInfo>();
+ // 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<WasmSymbol>(*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");
// 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
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 {
Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly);
bool isExcludedByComdat(InputChunk *chunk) const;
+ void synthesizeTableSymbols();
std::unique_ptr<WasmObjectFile> wasmObj;
};
for (InputGlobal *g : symtab->syntheticGlobals)
if (!g->live)
message("removing unused section " + toString(g));
+ for (InputTable *t : symtab->syntheticTables)
+ if (!t->live)
+ message("removing unused section " + toString(t));
}
}
return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global);
}
+DefinedTable *SymbolTable::addSyntheticTable(StringRef name, uint32_t flags,
+ InputTable *table) {
+ LLVM_DEBUG(dbgs() << "addSyntheticTable: " << name << " -> " << table
+ << "\n");
+ Symbol *s = find(name);
+ assert(!s || s->isUndefined());
+ if (!s)
+ s = insertName(name).first;
+ syntheticTables.emplace_back(table);
+ return replaceSymbol<DefinedTable>(s, name, flags, nullptr, table);
+}
+
static bool shouldReplace(const Symbol *existing, InputFile *newFile,
uint32_t newFlags) {
// If existing symbol is undefined, replace it.
DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags,
InputGlobal *global);
+ DefinedTable *addSyntheticTable(StringRef name, uint32_t flags,
+ InputTable *global);
void handleSymbolVariants();
void handleWeakUndefines();
std::vector<BitcodeFile *> bitcodeFiles;
std::vector<InputFunction *> syntheticFunctions;
std::vector<InputGlobal *> syntheticGlobals;
+ std::vector<InputTable *> syntheticTables;
private:
std::pair<Symbol *, bool> insert(StringRef name, const InputFile *file);
DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::memoryBase;
DefinedData *WasmSym::definedMemoryBase;
+TableSymbol *WasmSym::indirectFunctionTable;
WasmSymbolType Symbol::getWasmType() const {
if (isa<FunctionSymbol>(this))
// 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
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;
}
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<UndefinedFunction>(sym)) {
}
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());
}
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);
public:
TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {}
- bool isNeeded() const override {
- // The linker currently always writes an indirect function table to the
- // output, so unless the indirect function table 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);
}
}
+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");
out.tableSec->addTable(table);
}
+ for (InputTable *table : symtab->syntheticTables)
+ out.tableSec->addTable(table);
+
out.globalSec->assignIndexes();
}
log("-- scanRelocations");
scanRelocations();
+ log("-- finalizeIndirectFunctionTable");
+ finalizeIndirectFunctionTable();
log("-- createSyntheticInitFunctions");
createSyntheticInitFunctions();
log("-- assignIndexes");