mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-04 00:06:50 +00:00
[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:
parent
ce06475da9
commit
6339382807
@ -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
|
||||
|
31
lld/test/wasm/export-table-explicit.test
Normal file
31
lld/test/wasm/export-table-explicit.test
Normal 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:
|
@ -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: ]
|
||||
|
@ -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
|
||||
|
@ -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 ]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ]
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -157,6 +157,7 @@ private:
|
||||
Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly);
|
||||
|
||||
bool isExcludedByComdat(InputChunk *chunk) const;
|
||||
void synthesizeTableSymbols();
|
||||
|
||||
std::unique_ptr<WasmObjectFile> wasmObj;
|
||||
};
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user