[WebAssembly] Add support for table linking to wasm-ld

This patch adds support to wasm-ld for linking multiple table references
together, in a manner similar to wasm globals. The indirect function
table is synthesized as needed.

To manage the transitional period in which the compiler doesn't yet
produce TABLE_NUMBER relocations and doesn't residualize table symbols,
the linker will detect object files which have table imports or
definitions, but no table symbols. In that case it will synthesize
symbols for the defined and imported tables.

As a change, relocatable objects are now written with table symbols,
which can cause symbol renumbering in some of the tests. If no object
file requires an indirect function table, none will be written to the
file. Note that for legacy ObjFile inputs, this test is conservative: as
we don't have relocs for each use of the indirecy function table, we
just assume that any incoming indirect function table should be
propagated to the output.

Differential Revision: https://reviews.llvm.org/D91870
This commit is contained in:
Andy Wingo 2021-01-14 10:15:56 +01:00
parent ce06475da9
commit 6339382807
22 changed files with 280 additions and 131 deletions

View File

@ -22,14 +22,6 @@ _start:
# 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

View File

@ -0,0 +1,31 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %t.start.o
# RUN: wasm-ld --export-table -o %t.wasm %t.start.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# Verify the interaction between --export-table and declared tables
.globl __indirect_function_table
.tabletype __indirect_function_table,externref
# CHECK: - 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:
# CHECK: - 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: 0
# CHECK-NEXT: - Name: __indirect_function_table
# CHECK-NEXT: Kind: TABLE
# CHECK-NEXT: Index: 0
# CHECK-NEXT: - Type:

View File

@ -139,15 +139,15 @@ entry:
; 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: ]

View File

@ -35,14 +35,6 @@ entry:
; 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

View File

@ -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 ]

View File

@ -41,14 +41,6 @@ define void @_start() {
; 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 @@ define void @_start() {
; 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

View File

@ -54,8 +54,8 @@ Sections:
# 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

View File

@ -69,14 +69,6 @@ declare void @func_external()
; 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 @@ declare void @func_external()
; 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

View File

@ -80,17 +80,22 @@ declare i32 @ret32(i32, i64, i32) local_unnamed_addr
; 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 ]

View File

@ -30,14 +30,6 @@ entry:
; 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

View File

@ -276,6 +276,11 @@ entry:
; 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:

View File

@ -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 @@ static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
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));
@ -976,6 +1029,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// 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();
}

View File

@ -310,6 +310,71 @@ static void setRelocs(const std::vector<T *> &chunks,
}
}
// 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");
@ -424,8 +489,11 @@ void ObjFile::parse(bool ignoreComdats) {
// 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 +505,13 @@ void ObjFile::parse(bool ignoreComdats) {
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 {

View File

@ -157,6 +157,7 @@ private:
Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly);
bool isExcludedByComdat(InputChunk *chunk) const;
void synthesizeTableSymbols();
std::unique_ptr<WasmObjectFile> wasmObj;
};

View File

@ -177,6 +177,9 @@ void markLive() {
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));
}
}

View File

@ -270,6 +270,18 @@ DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name,
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.

View File

@ -93,6 +93,8 @@ public:
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();
@ -103,6 +105,7 @@ public:
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);

View File

@ -91,6 +91,7 @@ UndefinedGlobal *WasmSym::tableBase;
DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::memoryBase;
DefinedData *WasmSym::definedMemoryBase;
TableSymbol *WasmSym::indirectFunctionTable;
WasmSymbolType Symbol::getWasmType() const {
if (isa<FunctionSymbol>(this))

View File

@ -568,6 +568,11 @@ struct WasmSym {
// 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

View File

@ -92,20 +92,11 @@ void TypeSection::writeBody() {
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 @@ void ImportSection::writeBody() {
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)) {
@ -230,26 +210,9 @@ void FunctionSection::addFunction(InputFunction *func) {
}
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());
}

View File

@ -98,7 +98,7 @@ protected:
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 @@ class TableSection : public SyntheticSection {
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);

View File

@ -606,10 +606,6 @@ void Writer::calculateExports() {
out.exportSec->exports.push_back(
WasmExport{"memory", WASM_EXTERNAL_MEMORY, 0});
if (!config->relocatable && config->exportTable)
out.exportSec->exports.push_back(
WasmExport{functionTableName, WASM_EXTERNAL_TABLE, 0});
unsigned globalIndex =
out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals();
@ -745,6 +741,19 @@ void Writer::createCommandExportWrappers() {
}
}
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");
@ -792,6 +801,9 @@ void Writer::assignIndexes() {
out.tableSec->addTable(table);
}
for (InputTable *table : symtab->syntheticTables)
out.tableSec->addTable(table);
out.globalSec->assignIndexes();
}
@ -1341,6 +1353,8 @@ void Writer::run() {
log("-- scanRelocations");
scanRelocations();
log("-- finalizeIndirectFunctionTable");
finalizeIndirectFunctionTable();
log("-- createSyntheticInitFunctions");
createSyntheticInitFunctions();
log("-- assignIndexes");