| ... | @@ -335,41 +335,64 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -335,41 +335,64 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 335 | wasm_bin.base.file = file; | 335 | wasm_bin.base.file = file; |
| 336 | wasm_bin.name = sub_path; | 336 | wasm_bin.name = sub_path; |
| 337 | | 337 | |
| 338 | // As sym_index '0' is reserved, we use it for our stack pointer symbol | 338 | // create stack pointer symbol |
| 339 | const sym_name = try wasm_bin.string_table.put(allocator, "__stack_pointer"); | 339 | { |
| 340 | const symbol = try wasm_bin.symbols.addOne(allocator); | 340 | const loc = try wasm_bin.createSyntheticSymbol("__stack_pointer", .global); |
| 341 | symbol.* = .{ | 341 | const symbol = loc.getSymbol(wasm_bin); |
| 342 | .name = sym_name, | 342 | // For object files we will import the stack pointer symbol |
| 343 | .tag = .global, | 343 | if (options.output_mode == .Obj) { |
| 344 | .flags = 0, | 344 | symbol.setUndefined(true); |
| 345 | .index = 0, | 345 | symbol.index = @intCast(u32, wasm_bin.imported_globals_count); |
| 346 | }; | 346 | wasm_bin.imported_globals_count += 1; |
| 347 | const loc: SymbolLoc = .{ .file = null, .index = 0 }; | 347 | try wasm_bin.imports.putNoClobber( |
| 348 | try wasm_bin.resolved_symbols.putNoClobber(allocator, loc, {}); | 348 | allocator, |
| 349 | try wasm_bin.globals.putNoClobber(allocator, sym_name, loc); | 349 | loc, |
| 350 | | 350 | .{ |
| 351 | // For object files we will import the stack pointer symbol | 351 | .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), |
| 352 | if (options.output_mode == .Obj) { | 352 | .name = symbol.name, |
| 353 | symbol.setUndefined(true); | 353 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, |
| 354 | try wasm_bin.imports.putNoClobber( | 354 | }, |
| 355 | allocator, | 355 | ); |
| 356 | .{ .file = null, .index = 0 }, | 356 | } else { |
| 357 | .{ | 357 | symbol.index = @intCast(u32, wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); |
| 358 | .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), | 358 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 359 | .name = sym_name, | 359 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 360 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, | 360 | global.* = .{ |
| 361 | }, | 361 | .global_type = .{ |
| 362 | ); | 362 | .valtype = .i32, |
| 363 | } else { | 363 | .mutable = true, |
| 364 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 364 | }, |
| 365 | const global = try wasm_bin.wasm_globals.addOne(allocator); | 365 | .init = .{ .i32_const = 0 }, |
| 366 | global.* = .{ | 366 | }; |
| 367 | .global_type = .{ | 367 | } |
| 368 | .valtype = .i32, | 368 | } |
| 369 | .mutable = true, | 369 | |
| 370 | }, | 370 | // create indirect function pointer symbol |
| 371 | .init = .{ .i32_const = 0 }, | 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 | if (!options.strip and options.module != null) { | 398 | if (!options.strip and options.module != null) { |
| ... | @@ -400,6 +423,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { | ... | @@ -400,6 +423,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 400 | return wasm; | 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 | /// Initializes symbols and atoms for the debug sections | 442 | /// Initializes symbols and atoms for the debug sections |
| 404 | /// Initialization is only done when compiling Zig code. | 443 | /// Initialization is only done when compiling Zig code. |
| 405 | /// When Zig is invoked as a linker instead, the atoms | 444 | /// When Zig is invoked as a linker instead, the atoms |
| ... | @@ -1249,7 +1288,7 @@ pub fn updateDeclExports( | ... | @@ -1249,7 +1288,7 @@ pub fn updateDeclExports( |
| 1249 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; | 1288 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; |
| 1250 | | 1289 | |
| 1251 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; | 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 | // are strong symbols, we have a linker error. | 1292 | // are strong symbols, we have a linker error. |
| 1254 | // In the other case we replace one with the other. | 1293 | // In the other case we replace one with the other. |
| 1255 | if (!exp_is_weak and !existing_sym.isWeak()) { | 1294 | if (!exp_is_weak and !existing_sym.isWeak()) { |
| ... | @@ -1361,6 +1400,19 @@ fn mapFunctionTable(wasm: *Wasm) void { | ... | @@ -1361,6 +1400,19 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1361 | while (it.next()) |value_ptr| : (index += 1) { | 1400 | while (it.next()) |value_ptr| : (index += 1) { |
| 1362 | value_ptr.* = index; | 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 | /// Either creates a new import, or updates one if existing. | 1418 | /// Either creates a new import, or updates one if existing. |
| ... | @@ -1700,17 +1752,6 @@ fn setupImports(wasm: *Wasm) !void { | ... | @@ -1700,17 +1752,6 @@ fn setupImports(wasm: *Wasm) !void { |
| 1700 | /// Takes the global, function and table section from each linked object file | 1752 | /// Takes the global, function and table section from each linked object file |
| 1701 | /// and merges it into a single section for each. | 1753 | /// and merges it into a single section for each. |
| 1702 | fn mergeSections(wasm: *Wasm) !void { | 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 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 1755 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 1715 | if (sym_loc.file == null) { | 1756 | if (sym_loc.file == null) { |
| 1716 | // Zig code-generated symbols are already within the sections and do not | 1757 | // Zig code-generated symbols are already within the sections and do not |
| ... | @@ -2613,28 +2654,9 @@ fn writeToFile( | ... | @@ -2613,28 +2654,9 @@ fn writeToFile( |
| 2613 | | 2654 | |
| 2614 | // Import section | 2655 | // Import section |
| 2615 | const import_memory = wasm.base.options.import_memory or is_obj; | 2656 | const import_memory = wasm.base.options.import_memory or is_obj; |
| 2616 | const import_table = wasm.base.options.import_table or is_obj; | 2657 | if (wasm.imports.count() != 0 or import_memory) { |
| 2617 | if (wasm.imports.count() != 0 or import_memory or import_table) { | | |
| 2618 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 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 | var it = wasm.imports.iterator(); | 2660 | var it = wasm.imports.iterator(); |
| 2639 | while (it.next()) |entry| { | 2661 | while (it.next()) |entry| { |
| 2640 | assert(entry.key_ptr.*.getSymbol(wasm).isUndefined()); | 2662 | assert(entry.key_ptr.*.getSymbol(wasm).isUndefined()); |
| ... | @@ -2657,7 +2679,7 @@ fn writeToFile( | ... | @@ -2657,7 +2679,7 @@ fn writeToFile( |
| 2657 | header_offset, | 2679 | header_offset, |
| 2658 | .import, | 2680 | .import, |
| 2659 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), | 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 | section_count += 1; | 2684 | section_count += 1; |
| 2663 | } | 2685 | } |
| ... | @@ -2680,22 +2702,20 @@ fn writeToFile( | ... | @@ -2680,22 +2702,20 @@ fn writeToFile( |
| 2680 | } | 2702 | } |
| 2681 | | 2703 | |
| 2682 | // Table section | 2704 | // Table section |
| 2683 | const export_table = wasm.base.options.export_table; | 2705 | if (wasm.tables.items.len > 0) { |
| 2684 | if (!import_table and wasm.function_table.count() != 0) { | | |
| 2685 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 2706 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2686 | | 2707 | |
| 2687 | try leb.writeULEB128(binary_writer, std.wasm.reftype(.funcref)); | 2708 | for (wasm.tables.items) |table| { |
| 2688 | try emitLimits(binary_writer, .{ | 2709 | try leb.writeULEB128(binary_writer, std.wasm.reftype(table.reftype)); |
| 2689 | .min = @intCast(u32, wasm.function_table.count()) + 1, | 2710 | try emitLimits(binary_writer, table.limits); |
| 2690 | .max = null, | 2711 | } |
| 2691 | }); | | |
| 2692 | | 2712 | |
| 2693 | try writeVecSectionHeader( | 2713 | try writeVecSectionHeader( |
| 2694 | binary_bytes.items, | 2714 | binary_bytes.items, |
| 2695 | header_offset, | 2715 | header_offset, |
| 2696 | .table, | 2716 | .table, |
| 2697 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), | 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 | section_count += 1; | 2720 | section_count += 1; |
| 2701 | } | 2721 | } |
| ... | @@ -2748,7 +2768,7 @@ fn writeToFile( | ... | @@ -2748,7 +2768,7 @@ fn writeToFile( |
| 2748 | } | 2768 | } |
| 2749 | | 2769 | |
| 2750 | // Export section | 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 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 2772 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2753 | | 2773 | |
| 2754 | for (wasm.exports.items) |exp| { | 2774 | for (wasm.exports.items) |exp| { |
| ... | @@ -2759,13 +2779,6 @@ fn writeToFile( | ... | @@ -2759,13 +2779,6 @@ fn writeToFile( |
| 2759 | try leb.writeULEB128(binary_writer, exp.index); | 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 | if (!import_memory) { | 2782 | if (!import_memory) { |
| 2770 | try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); | 2783 | try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); |
| 2771 | try binary_writer.writeAll("memory"); | 2784 | try binary_writer.writeAll("memory"); |
| ... | @@ -2778,7 +2791,7 @@ fn writeToFile( | ... | @@ -2778,7 +2791,7 @@ fn writeToFile( |
| 2778 | header_offset, | 2791 | header_offset, |
| 2779 | .@"export", | 2792 | .@"export", |
| 2780 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), | 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 | section_count += 1; | 2796 | section_count += 1; |
| 2784 | } | 2797 | } |
| ... | @@ -2787,11 +2800,18 @@ fn writeToFile( | ... | @@ -2787,11 +2800,18 @@ fn writeToFile( |
| 2787 | if (wasm.function_table.count() > 0) { | 2800 | if (wasm.function_table.count() > 0) { |
| 2788 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 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 | try leb.writeULEB128(binary_writer, flags); | 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 | try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid | 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 | try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count())); | 2815 | try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count())); |
| 2796 | var symbol_it = wasm.function_table.keyIterator(); | 2816 | var symbol_it = wasm.function_table.keyIterator(); |
| 2797 | while (symbol_it.next()) |symbol_loc_ptr| { | 2817 | while (symbol_it.next()) |symbol_loc_ptr| { |