| ... | ... | @@ -335,41 +335,64 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 335 | 335 | wasm_bin.base.file = file; |
| 336 | 336 | wasm_bin.name = sub_path; |
| 337 | 337 | |
| 338 | | // As sym_index '0' is reserved, we use it for our stack pointer symbol |
| 339 | | const sym_name = try wasm_bin.string_table.put(allocator, "__stack_pointer"); |
| 340 | | const symbol = try wasm_bin.symbols.addOne(allocator); |
| 341 | | symbol.* = .{ |
| 342 | | .name = sym_name, |
| 343 | | .tag = .global, |
| 344 | | .flags = 0, |
| 345 | | .index = 0, |
| 346 | | }; |
| 347 | | const loc: SymbolLoc = .{ .file = null, .index = 0 }; |
| 348 | | try wasm_bin.resolved_symbols.putNoClobber(allocator, loc, {}); |
| 349 | | try wasm_bin.globals.putNoClobber(allocator, sym_name, loc); |
| 350 | | |
| 351 | | // For object files we will import the stack pointer symbol |
| 352 | | if (options.output_mode == .Obj) { |
| 353 | | symbol.setUndefined(true); |
| 354 | | try wasm_bin.imports.putNoClobber( |
| 355 | | allocator, |
| 356 | | .{ .file = null, .index = 0 }, |
| 357 | | .{ |
| 358 | | .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), |
| 359 | | .name = sym_name, |
| 360 | | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, |
| 361 | | }, |
| 362 | | ); |
| 363 | | } else { |
| 364 | | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 365 | | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 366 | | global.* = .{ |
| 367 | | .global_type = .{ |
| 368 | | .valtype = .i32, |
| 369 | | .mutable = true, |
| 370 | | }, |
| 371 | | .init = .{ .i32_const = 0 }, |
| 338 | // create stack pointer symbol |
| 339 | { |
| 340 | const loc = try wasm_bin.createSyntheticSymbol("__stack_pointer", .global); |
| 341 | const symbol = loc.getSymbol(wasm_bin); |
| 342 | // For object files we will import the stack pointer symbol |
| 343 | if (options.output_mode == .Obj) { |
| 344 | symbol.setUndefined(true); |
| 345 | symbol.index = @intCast(u32, wasm_bin.imported_globals_count); |
| 346 | wasm_bin.imported_globals_count += 1; |
| 347 | try wasm_bin.imports.putNoClobber( |
| 348 | allocator, |
| 349 | loc, |
| 350 | .{ |
| 351 | .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), |
| 352 | .name = symbol.name, |
| 353 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, |
| 354 | }, |
| 355 | ); |
| 356 | } else { |
| 357 | symbol.index = @intCast(u32, wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); |
| 358 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 359 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 360 | global.* = .{ |
| 361 | .global_type = .{ |
| 362 | .valtype = .i32, |
| 363 | .mutable = true, |
| 364 | }, |
| 365 | .init = .{ .i32_const = 0 }, |
| 366 | }; |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | // create indirect function pointer symbol |
| 371 | { |
| 372 | const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table); |
| 373 | const symbol = loc.getSymbol(wasm_bin); |
| 374 | const table: std.wasm.Table = .{ |
| 375 | .limits = .{ .min = 0, .max = null }, // will be overwritten during `mapFunctionTable` |
| 376 | .reftype = .funcref, |
| 372 | 377 | }; |
| 378 | if (options.output_mode == .Obj or options.import_table) { |
| 379 | symbol.setUndefined(true); |
| 380 | symbol.index = @intCast(u32, wasm_bin.imported_tables_count); |
| 381 | wasm_bin.imported_tables_count += 1; |
| 382 | try wasm_bin.imports.put(allocator, loc, .{ |
| 383 | .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), |
| 384 | .name = symbol.name, |
| 385 | .kind = .{ .table = table }, |
| 386 | }); |
| 387 | } else { |
| 388 | symbol.index = @intCast(u32, wasm_bin.imported_tables_count + wasm_bin.tables.items.len); |
| 389 | try wasm_bin.tables.append(allocator, table); |
| 390 | if (options.export_table) { |
| 391 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 392 | } else { |
| 393 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 394 | } |
| 395 | } |
| 373 | 396 | } |
| 374 | 397 | |
| 375 | 398 | if (!options.strip and options.module != null) { |
| ... | ... | @@ -400,6 +423,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 400 | 423 | return wasm; |
| 401 | 424 | } |
| 402 | 425 | |
| 426 | /// For a given name, creates a new global synthetic symbol. |
| 427 | /// Leaves index undefined and the default flags (0). |
| 428 | fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc { |
| 429 | const name_offset = try wasm.string_table.put(wasm.base.allocator, name); |
| 430 | const sym_index = @intCast(u32, wasm.symbols.items.len); |
| 431 | const loc: SymbolLoc = .{ .index = sym_index, .file = null }; |
| 432 | try wasm.symbols.append(wasm.base.allocator, .{ |
| 433 | .name = name_offset, |
| 434 | .flags = 0, |
| 435 | .tag = tag, |
| 436 | .index = undefined, |
| 437 | }); |
| 438 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); |
| 439 | try wasm.globals.putNoClobber(wasm.base.allocator, name_offset, loc); |
| 440 | return loc; |
| 441 | } |
| 403 | 442 | /// Initializes symbols and atoms for the debug sections |
| 404 | 443 | /// Initialization is only done when compiling Zig code. |
| 405 | 444 | /// When Zig is invoked as a linker instead, the atoms |
| ... | ... | @@ -1249,7 +1288,7 @@ pub fn updateDeclExports( |
| 1249 | 1288 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; |
| 1250 | 1289 | |
| 1251 | 1290 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; |
| 1252 | | // When both the to-bo-exported symbol and the already existing symbol |
| 1291 | // When both the to-be-exported symbol and the already existing symbol |
| 1253 | 1292 | // are strong symbols, we have a linker error. |
| 1254 | 1293 | // In the other case we replace one with the other. |
| 1255 | 1294 | if (!exp_is_weak and !existing_sym.isWeak()) { |
| ... | ... | @@ -1361,6 +1400,19 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1361 | 1400 | while (it.next()) |value_ptr| : (index += 1) { |
| 1362 | 1401 | value_ptr.* = index; |
| 1363 | 1402 | } |
| 1403 | |
| 1404 | if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { |
| 1405 | const sym_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 1406 | const import = wasm.imports.getPtr(sym_loc).?; |
| 1407 | import.kind.table.limits.min = index - 1; // we start at index 1. |
| 1408 | } else if (index > 1) { |
| 1409 | log.debug("Appending indirect function table", .{}); |
| 1410 | const offset = wasm.string_table.getOffset("__indirect_function_table").?; |
| 1411 | const sym_with_loc = wasm.globals.get(offset).?; |
| 1412 | const symbol = sym_with_loc.getSymbol(wasm); |
| 1413 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; |
| 1414 | table.limits = .{ .min = index, .max = index }; |
| 1415 | } |
| 1364 | 1416 | } |
| 1365 | 1417 | |
| 1366 | 1418 | /// Either creates a new import, or updates one if existing. |
| ... | ... | @@ -1700,17 +1752,6 @@ fn setupImports(wasm: *Wasm) !void { |
| 1700 | 1752 | /// Takes the global, function and table section from each linked object file |
| 1701 | 1753 | /// and merges it into a single section for each. |
| 1702 | 1754 | fn mergeSections(wasm: *Wasm) !void { |
| 1703 | | // append the indirect function table if initialized |
| 1704 | | if (wasm.string_table.getOffset("__indirect_function_table")) |offset| { |
| 1705 | | const sym_loc = wasm.globals.get(offset).?; |
| 1706 | | const table: std.wasm.Table = .{ |
| 1707 | | .limits = .{ .min = @intCast(u32, wasm.function_table.count()), .max = null }, |
| 1708 | | .reftype = .funcref, |
| 1709 | | }; |
| 1710 | | sym_loc.getSymbol(wasm).index = @intCast(u32, wasm.tables.items.len) + wasm.imported_tables_count; |
| 1711 | | try wasm.tables.append(wasm.base.allocator, table); |
| 1712 | | } |
| 1713 | | |
| 1714 | 1755 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 1715 | 1756 | if (sym_loc.file == null) { |
| 1716 | 1757 | // Zig code-generated symbols are already within the sections and do not |
| ... | ... | @@ -2613,28 +2654,9 @@ fn writeToFile( |
| 2613 | 2654 | |
| 2614 | 2655 | // Import section |
| 2615 | 2656 | const import_memory = wasm.base.options.import_memory or is_obj; |
| 2616 | | const import_table = wasm.base.options.import_table or is_obj; |
| 2617 | | if (wasm.imports.count() != 0 or import_memory or import_table) { |
| 2657 | if (wasm.imports.count() != 0 or import_memory) { |
| 2618 | 2658 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2619 | 2659 | |
| 2620 | | // import table is always first table so emit that first |
| 2621 | | if (import_table) { |
| 2622 | | const table_imp: types.Import = .{ |
| 2623 | | .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name), |
| 2624 | | .name = try wasm.string_table.put(wasm.base.allocator, "__indirect_function_table"), |
| 2625 | | .kind = .{ |
| 2626 | | .table = .{ |
| 2627 | | .limits = .{ |
| 2628 | | .min = @intCast(u32, wasm.function_table.count()), |
| 2629 | | .max = null, |
| 2630 | | }, |
| 2631 | | .reftype = .funcref, |
| 2632 | | }, |
| 2633 | | }, |
| 2634 | | }; |
| 2635 | | try wasm.emitImport(binary_writer, table_imp); |
| 2636 | | } |
| 2637 | | |
| 2638 | 2660 | var it = wasm.imports.iterator(); |
| 2639 | 2661 | while (it.next()) |entry| { |
| 2640 | 2662 | assert(entry.key_ptr.*.getSymbol(wasm).isUndefined()); |
| ... | ... | @@ -2657,7 +2679,7 @@ fn writeToFile( |
| 2657 | 2679 | header_offset, |
| 2658 | 2680 | .import, |
| 2659 | 2681 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2660 | | @intCast(u32, wasm.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), |
| 2682 | @intCast(u32, wasm.imports.count() + @boolToInt(import_memory)), |
| 2661 | 2683 | ); |
| 2662 | 2684 | section_count += 1; |
| 2663 | 2685 | } |
| ... | ... | @@ -2680,22 +2702,20 @@ fn writeToFile( |
| 2680 | 2702 | } |
| 2681 | 2703 | |
| 2682 | 2704 | // Table section |
| 2683 | | const export_table = wasm.base.options.export_table; |
| 2684 | | if (!import_table and wasm.function_table.count() != 0) { |
| 2705 | if (wasm.tables.items.len > 0) { |
| 2685 | 2706 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2686 | 2707 | |
| 2687 | | try leb.writeULEB128(binary_writer, std.wasm.reftype(.funcref)); |
| 2688 | | try emitLimits(binary_writer, .{ |
| 2689 | | .min = @intCast(u32, wasm.function_table.count()) + 1, |
| 2690 | | .max = null, |
| 2691 | | }); |
| 2708 | for (wasm.tables.items) |table| { |
| 2709 | try leb.writeULEB128(binary_writer, std.wasm.reftype(table.reftype)); |
| 2710 | try emitLimits(binary_writer, table.limits); |
| 2711 | } |
| 2692 | 2712 | |
| 2693 | 2713 | try writeVecSectionHeader( |
| 2694 | 2714 | binary_bytes.items, |
| 2695 | 2715 | header_offset, |
| 2696 | 2716 | .table, |
| 2697 | 2717 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2698 | | @as(u32, 1), |
| 2718 | @intCast(u32, wasm.tables.items.len), |
| 2699 | 2719 | ); |
| 2700 | 2720 | section_count += 1; |
| 2701 | 2721 | } |
| ... | ... | @@ -2748,7 +2768,7 @@ fn writeToFile( |
| 2748 | 2768 | } |
| 2749 | 2769 | |
| 2750 | 2770 | // Export section |
| 2751 | | if (wasm.exports.items.len != 0 or export_table or !import_memory) { |
| 2771 | if (wasm.exports.items.len != 0 or !import_memory) { |
| 2752 | 2772 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2753 | 2773 | |
| 2754 | 2774 | for (wasm.exports.items) |exp| { |
| ... | ... | @@ -2759,13 +2779,6 @@ fn writeToFile( |
| 2759 | 2779 | try leb.writeULEB128(binary_writer, exp.index); |
| 2760 | 2780 | } |
| 2761 | 2781 | |
| 2762 | | if (export_table) { |
| 2763 | | try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len)); |
| 2764 | | try binary_writer.writeAll("__indirect_function_table"); |
| 2765 | | try binary_writer.writeByte(std.wasm.externalKind(.table)); |
| 2766 | | try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table |
| 2767 | | } |
| 2768 | | |
| 2769 | 2782 | if (!import_memory) { |
| 2770 | 2783 | try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); |
| 2771 | 2784 | try binary_writer.writeAll("memory"); |
| ... | ... | @@ -2778,7 +2791,7 @@ fn writeToFile( |
| 2778 | 2791 | header_offset, |
| 2779 | 2792 | .@"export", |
| 2780 | 2793 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2781 | | @intCast(u32, wasm.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), |
| 2794 | @intCast(u32, wasm.exports.items.len) + @boolToInt(!import_memory), |
| 2782 | 2795 | ); |
| 2783 | 2796 | section_count += 1; |
| 2784 | 2797 | } |
| ... | ... | @@ -2787,11 +2800,18 @@ fn writeToFile( |
| 2787 | 2800 | if (wasm.function_table.count() > 0) { |
| 2788 | 2801 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2789 | 2802 | |
| 2790 | | var flags: u32 = 0x2; // Yes we have a table |
| 2803 | const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 2804 | const table_sym = table_loc.getSymbol(wasm); |
| 2805 | |
| 2806 | var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually |
| 2791 | 2807 | try leb.writeULEB128(binary_writer, flags); |
| 2792 | | try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols |
| 2808 | if (flags == 0x02) { |
| 2809 | try leb.writeULEB128(binary_writer, table_sym.index); |
| 2810 | } |
| 2793 | 2811 | try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid |
| 2794 | | try leb.writeULEB128(binary_writer, @as(u8, 0)); |
| 2812 | if (flags == 0x02) { |
| 2813 | try leb.writeULEB128(binary_writer, @as(u8, 0)); // represents funcref |
| 2814 | } |
| 2795 | 2815 | try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count())); |
| 2796 | 2816 | var symbol_it = wasm.function_table.keyIterator(); |
| 2797 | 2817 | while (symbol_it.next()) |symbol_loc_ptr| { |