@@ -308,69 +308,109 @@ static void setRelocs(const std::vector<T *> &chunks,
308
308
}
309
309
}
310
310
311
- // Since LLVM 12, we expect that if an input file defines or uses a table, it
312
- // declares the tables using symbols and records each use with a relocation.
313
- // This way when the linker combines inputs, it can collate the tables used by
314
- // the inputs, assigning them distinct table numbers, and renumber all the uses
315
- // as appropriate. At the same time, the linker has special logic to build the
311
+ // An object file can have two approaches to tables. With the reference-types
312
+ // feature enabled, input files that define or use tables declare the tables
313
+ // using symbols, and record each use with a relocation. This way when the
314
+ // linker combines inputs, it can collate the tables used by the inputs,
315
+ // assigning them distinct table numbers, and renumber all the uses as
316
+ // appropriate. At the same time, the linker has special logic to build the
316
317
// indirect function table if it is needed.
317
318
//
318
- // However, object files produced by LLVM 11 and earlier neither write table
319
- // symbols nor record relocations, and yet still use tables via call_indirect,
320
- // and via function pointer bitcasts. We can detect these object files, as they
321
- // declare tables as imports or define them locally, but don't have table
322
- // symbols. synthesizeTableSymbols serves as a shim when loading these older
323
- // input files, defining the missing symbols to allow the indirect function
324
- // table to be built.
319
+ // However, MVP object files (those that target WebAssembly 1.0, the "minimum
320
+ // viable product" version of WebAssembly) neither write table symbols nor
321
+ // record relocations. These files can have at most one table, the indirect
322
+ // function table used by call_indirect and which is the address space for
323
+ // function pointers. If this table is present, it is always an import. If we
324
+ // have a file with a table import but no table symbols, it is an MVP object
325
+ // file. synthesizeMVPIndirectFunctionTableSymbolIfNeeded serves as a shim when
326
+ // loading these input files, defining the missing symbol to allow the indirect
327
+ // function table to be built.
325
328
//
326
- // Table uses in these older files won't be relocated, as they have no
327
- // relocations. In practice this isn't a problem, as these object files
328
- // typically just declare a single table named __indirect_function_table and
329
- // having table number 0, so relocation would be idempotent anyway.
330
- void ObjFile::synthesizeTableSymbols () {
331
- uint32_t tableNumber = 0 ;
332
- const WasmGlobalType *globalType = nullptr ;
333
- const WasmEventType *eventType = nullptr ;
334
- const WasmSignature *signature = nullptr ;
335
- if (wasmObj->getNumImportedTables ()) {
336
- for (const auto &import : wasmObj->imports ()) {
337
- if (import .Kind == WASM_EXTERNAL_TABLE) {
338
- auto *info = make<WasmSymbolInfo>();
339
- info->Name = import .Field ;
340
- info->Kind = WASM_SYMBOL_TYPE_TABLE;
341
- info->ImportModule = import .Module ;
342
- info->ImportName = import .Field ;
343
- info->Flags = WASM_SYMBOL_UNDEFINED;
344
- info->Flags |= WASM_SYMBOL_NO_STRIP;
345
- info->ElementIndex = tableNumber++;
346
- LLVM_DEBUG (dbgs () << " Synthesizing symbol for table import: "
347
- << info->Name << " \n " );
348
- auto *wasmSym = make<WasmSymbol>(*info, globalType, &import .Table ,
349
- eventType, signature);
350
- symbols.push_back (createUndefined (*wasmSym, false ));
351
- // Because there are no TABLE_NUMBER relocs in this case, we can't
352
- // compute accurate liveness info; instead, just mark the symbol as
353
- // always live.
354
- symbols.back ()->markLive ();
355
- }
329
+ // As indirect function table table usage in MVP objects cannot be relocated,
330
+ // the linker must ensure that this table gets assigned index zero.
331
+ void ObjFile::addLegacyIndirectFunctionTableIfNeeded (
332
+ uint32_t tableSymbolCount) {
333
+ uint32_t tableCount = wasmObj->getNumImportedTables () + tables.size ();
334
+
335
+ // If there are symbols for all tables, then all is good.
336
+ if (tableCount == tableSymbolCount)
337
+ return ;
338
+
339
+ // It's possible for an input to define tables and also use the indirect
340
+ // function table, but forget to compile with -mattr=+reference-types.
341
+ // For these newer files, we require symbols for all tables, and
342
+ // relocations for all of their uses.
343
+ if (tableSymbolCount != 0 ) {
344
+ error (toString (this ) +
345
+ " : expected one symbol table entry for each of the " +
346
+ Twine (tableCount) + " table(s) present, but got " +
347
+ Twine (tableSymbolCount) + " symbol(s) instead." );
348
+ return ;
349
+ }
350
+
351
+ // An MVP object file can have up to one table import, for the indirect
352
+ // function table, but will have no table definitions.
353
+ if (tables.size ()) {
354
+ error (toString (this ) +
355
+ " : unexpected table definition(s) without corresponding "
356
+ " symbol-table entries." );
357
+ return ;
358
+ }
359
+
360
+ // An MVP object file can have only one table import.
361
+ if (tableCount != 1 ) {
362
+ error (toString (this ) +
363
+ " : multiple table imports, but no corresponding symbol-table "
364
+ " entries." );
365
+ return ;
366
+ }
367
+
368
+ const WasmImport *tableImport = nullptr ;
369
+ for (const auto &import : wasmObj->imports ()) {
370
+ if (import .Kind == WASM_EXTERNAL_TABLE) {
371
+ assert (!tableImport);
372
+ tableImport = &import ;
356
373
}
357
374
}
358
- for (const auto &table : tables) {
359
- auto *info = make<llvm::wasm::WasmSymbolInfo>();
360
- // Empty name.
361
- info->Kind = WASM_SYMBOL_TYPE_TABLE;
362
- info->Flags = WASM_SYMBOL_BINDING_LOCAL;
363
- info->Flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
364
- info->Flags |= WASM_SYMBOL_NO_STRIP;
365
- info->ElementIndex = tableNumber++;
366
- LLVM_DEBUG (dbgs () << " Synthesizing symbol for table definition: "
367
- << info->Name << " \n " );
368
- auto *wasmSym = make<WasmSymbol>(*info, globalType, &table->getType (),
369
- eventType, signature);
370
- symbols.push_back (createDefined (*wasmSym));
371
- // Mark live, for the same reasons as for imported tables.
372
- symbols.back ()->markLive ();
375
+ assert (tableImport);
376
+
377
+ // We can only synthesize a symtab entry for the indirect function table; if
378
+ // it has an unexpected name or type, assume that it's not actually the
379
+ // indirect function table.
380
+ if (tableImport->Field != functionTableName ||
381
+ tableImport->Table .ElemType != uint8_t (ValType::FUNCREF)) {
382
+ error (toString (this ) + " : table import " + Twine (tableImport->Field ) +
383
+ " is missing a symbol table entry." );
384
+ return ;
373
385
}
386
+
387
+ auto *info = make<WasmSymbolInfo>();
388
+ info->Name = tableImport->Field ;
389
+ info->Kind = WASM_SYMBOL_TYPE_TABLE;
390
+ info->ImportModule = tableImport->Module ;
391
+ info->ImportName = tableImport->Field ;
392
+ info->Flags = WASM_SYMBOL_UNDEFINED;
393
+ info->Flags |= WASM_SYMBOL_NO_STRIP;
394
+ info->ElementIndex = 0 ;
395
+ LLVM_DEBUG (dbgs () << " Synthesizing symbol for table import: " << info->Name
396
+ << " \n " );
397
+ const WasmGlobalType *globalType = nullptr ;
398
+ const WasmEventType *eventType = nullptr ;
399
+ const WasmSignature *signature = nullptr ;
400
+ auto *wasmSym = make<WasmSymbol>(*info, globalType, &tableImport->Table ,
401
+ eventType, signature);
402
+ Symbol *sym = createUndefined (*wasmSym, false );
403
+ // We're only sure it's a TableSymbol if the createUndefined succeeded.
404
+ if (errorCount ())
405
+ return ;
406
+ symbols.push_back (sym);
407
+ // Because there are no TABLE_NUMBER relocs, we can't compute accurate
408
+ // liveness info; instead, just mark the symbol as always live.
409
+ sym->markLive ();
410
+
411
+ // We assume that this compilation unit has unrelocatable references to
412
+ // this table.
413
+ config->legacyFunctionTable = true ;
374
414
}
375
415
376
416
void ObjFile::parse (bool ignoreComdats) {
@@ -487,11 +527,11 @@ void ObjFile::parse(bool ignoreComdats) {
487
527
488
528
// Populate `Symbols` based on the symbols in the object.
489
529
symbols.reserve (wasmObj->getNumberOfSymbols ());
490
- bool haveTableSymbol = false ;
530
+ uint32_t tableSymbolCount = 0 ;
491
531
for (const SymbolRef &sym : wasmObj->symbols ()) {
492
532
const WasmSymbol &wasmSym = wasmObj->getWasmSymbol (sym.getRawDataRefImpl ());
493
533
if (wasmSym.isTypeTable ())
494
- haveTableSymbol = true ;
534
+ tableSymbolCount++ ;
495
535
if (wasmSym.isDefined ()) {
496
536
// createDefined may fail if the symbol is comdat excluded in which case
497
537
// we fall back to creating an undefined symbol
@@ -504,12 +544,7 @@ void ObjFile::parse(bool ignoreComdats) {
504
544
symbols.push_back (createUndefined (wasmSym, isCalledDirectly[idx]));
505
545
}
506
546
507
- // As a stopgap measure while implementing table support, if the object file
508
- // has table definitions or imports but no table symbols, synthesize symbols
509
- // for those tables. Mark as NO_STRIP to ensure they reach the output file,
510
- // even if there are no TABLE_NUMBER relocs against them.
511
- if (!haveTableSymbol)
512
- synthesizeTableSymbols ();
547
+ addLegacyIndirectFunctionTableIfNeeded (tableSymbolCount);
513
548
}
514
549
515
550
bool ObjFile::isExcludedByComdat (InputChunk *chunk) const {
0 commit comments