[WebAssembly] Add support for table linking to wasm-ld
authorAndy Wingo <wingo@igalia.com>
Thu, 14 Jan 2021 09:15:56 +0000 (10:15 +0100)
committerAndy Wingo <wingo@igalia.com>
Fri, 15 Jan 2021 08:21:52 +0000 (09:21 +0100)
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

21 files changed:
lld/test/wasm/alias.s
lld/test/wasm/init-fini.ll
lld/test/wasm/local-symbols.ll
lld/test/wasm/locals-duplicate.test
lld/test/wasm/pie.ll
lld/test/wasm/section-symbol-relocs.yaml
lld/test/wasm/shared.ll
lld/test/wasm/signature-mismatch.ll
lld/test/wasm/stack-pointer.ll
lld/test/wasm/weak-alias.ll
lld/wasm/Driver.cpp
lld/wasm/InputFiles.cpp
lld/wasm/InputFiles.h
lld/wasm/MarkLive.cpp
lld/wasm/SymbolTable.cpp
lld/wasm/SymbolTable.h
lld/wasm/Symbols.cpp
lld/wasm/Symbols.h
lld/wasm/SyntheticSections.cpp
lld/wasm/SyntheticSections.h
lld/wasm/Writer.cpp

index 6c99f69..b2ab45e 100644 (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
index 1e7644b..5631d58 100644 (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:  ]
index 13c200d..216aced 100644 (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
index 07abb74..cf9a148 100644 (file)
 ; 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 ]
index 84555a0..1aca4df 100644 (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
index 3248ed6..34126cb 100644 (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
index 98751ad..61337fc 100644 (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
index d5f95b0..65ce739 100644 (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 ]
index 8efb2aa..11ab62f 100644 (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
index 9d903f7..3875019 100644 (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:
index 84fdb77..d349a6a 100644 (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();
 }
index 1d0f016..1101cfb 100644 (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 {
index 4243a44..8a47138 100644 (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;
 };
index c47e095..f21774b 100644 (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));
   }
 }
 
index c5af5ca..d93b2a1 100644 (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.
index 921a6ed..ce7219c 100644 (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);
index a403a47..173ad31 100644 (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))
index 8434f82..c8ee0e1 100644 (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
index 967c2e2..6044601 100644 (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());
 }
index 7af931b..3795b38 100644 (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);
 
index 99cf48c..763fce5 100644 (file)
@@ -745,6 +745,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 +805,9 @@ void Writer::assignIndexes() {
       out.tableSec->addTable(table);
   }
 
+  for (InputTable *table : symtab->syntheticTables)
+    out.tableSec->addTable(table);
+
   out.globalSec->assignIndexes();
 }
 
@@ -1341,6 +1357,8 @@ void Writer::run() {
 
   log("-- scanRelocations");
   scanRelocations();
+  log("-- finalizeIndirectFunctionTable");
+  finalizeIndirectFunctionTable();
   log("-- createSyntheticInitFunctions");
   createSyntheticInitFunctions();
   log("-- assignIndexes");