| ... | @@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, | ... | @@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, |
| 118 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, | 118 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, |
| 119 | /// Output export section | 119 | /// Output export section |
| 120 | exports: std.ArrayListUnmanaged(types.Export) = .{}, | 120 | exports: std.ArrayListUnmanaged(types.Export) = .{}, |
| | 121 | /// List of initialization functions. These must be called in order of priority |
| | 122 | /// by the (synthetic) __wasm_call_ctors function. |
| | 123 | init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{}, |
| 121 | | 124 | |
| 122 | /// Indirect function table, used to call function pointers | 125 | /// Indirect function table, used to call function pointers |
| 123 | /// When this is non-zero, we must emit a table entry, | 126 | /// When this is non-zero, we must emit a table entry, |
| ... | @@ -238,6 +241,34 @@ pub const SymbolLoc = struct { | ... | @@ -238,6 +241,34 @@ pub const SymbolLoc = struct { |
| 238 | } | 241 | } |
| 239 | }; | 242 | }; |
| 240 | | 243 | |
| | 244 | // Contains the location of the function symbol, as well as |
| | 245 | /// the priority itself of the initialization function. |
| | 246 | pub const InitFuncLoc = struct { |
| | 247 | /// object file index in the list of objects. |
| | 248 | /// Unlike `SymbolLoc` this cannot be `null` as we never define |
| | 249 | /// our own ctors. |
| | 250 | file: u16, |
| | 251 | /// Symbol index within the corresponding object file. |
| | 252 | index: u32, |
| | 253 | /// The priority in which the constructor must be called. |
| | 254 | priority: u32, |
| | 255 | |
| | 256 | /// From a given `InitFuncLoc` returns the corresponding function symbol |
| | 257 | fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol { |
| | 258 | return getSymbolLoc(loc).getSymbol(wasm); |
| | 259 | } |
| | 260 | |
| | 261 | /// Turns the given `InitFuncLoc` into a `SymbolLoc` |
| | 262 | fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc { |
| | 263 | return .{ .file = loc.file, .index = loc.index }; |
| | 264 | } |
| | 265 | |
| | 266 | /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`. |
| | 267 | fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool { |
| | 268 | _ = ctx; |
| | 269 | return lhs.priority < rhs.priority; |
| | 270 | } |
| | 271 | }; |
| 241 | /// Generic string table that duplicates strings | 272 | /// Generic string table that duplicates strings |
| 242 | /// and converts them into offsets instead. | 273 | /// and converts them into offsets instead. |
| 243 | pub const StringTable = struct { | 274 | pub const StringTable = struct { |
| ... | @@ -393,6 +424,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -393,6 +424,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 393 | } | 424 | } |
| 394 | } | 425 | } |
| 395 | | 426 | |
| | 427 | // create __wasm_call_ctors |
| | 428 | { |
| | 429 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_call_ctors", .function); |
| | 430 | const symbol = loc.getSymbol(wasm_bin); |
| | 431 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 432 | // we do not know the function index until after we merged all sections. |
| | 433 | // Therefore we set `symbol.index` and create its corresponding references |
| | 434 | // at the end during `initializeCallCtorsFunction`. |
| | 435 | } |
| | 436 | |
| 396 | if (!options.strip and options.module != null) { | 437 | if (!options.strip and options.module != null) { |
| 397 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | 438 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); |
| 398 | try wasm_bin.initDebugSections(); | 439 | try wasm_bin.initDebugSections(); |
| ... | @@ -896,6 +937,7 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -896,6 +937,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 896 | wasm.wasm_globals.deinit(gpa); | 937 | wasm.wasm_globals.deinit(gpa); |
| 897 | wasm.function_table.deinit(gpa); | 938 | wasm.function_table.deinit(gpa); |
| 898 | wasm.tables.deinit(gpa); | 939 | wasm.tables.deinit(gpa); |
| | 940 | wasm.init_funcs.deinit(gpa); |
| 899 | wasm.exports.deinit(gpa); | 941 | wasm.exports.deinit(gpa); |
| 900 | | 942 | |
| 901 | wasm.string_table.deinit(gpa); | 943 | wasm.string_table.deinit(gpa); |
| ... | @@ -1698,6 +1740,130 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -1698,6 +1740,130 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1698 | wasm.data_segments = new_mapping; | 1740 | wasm.data_segments = new_mapping; |
| 1699 | } | 1741 | } |
| 1700 | | 1742 | |
| | 1743 | /// Obtains all initfuncs from each object file, verifies its function signature, |
| | 1744 | /// and then appends it to our final `init_funcs` list. |
| | 1745 | /// After all functions have been inserted, the functions will be ordered based |
| | 1746 | /// on their priority. |
| | 1747 | /// NOTE: This function must be called before we merged any other section. |
| | 1748 | /// This is because all init funcs in the object files contain references to the |
| | 1749 | /// original functions and their types. We need to know the type to verify it doesn't |
| | 1750 | /// contain any parameters. |
| | 1751 | fn setupInitFunctions(wasm: *Wasm) !void { |
| | 1752 | for (wasm.objects.items) |object, file_index| { |
| | 1753 | try wasm.init_funcs.ensureUnusedCapacity(wasm.base.allocator, object.init_funcs.len); |
| | 1754 | for (object.init_funcs) |init_func| { |
| | 1755 | const symbol = object.symtable[init_func.symbol_index]; |
| | 1756 | const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { |
| | 1757 | const imp: types.Import = object.findImport(.function, symbol.index); |
| | 1758 | break :ty object.func_types[imp.kind.function]; |
| | 1759 | } else ty: { |
| | 1760 | const func_index = symbol.index - object.importedCountByKind(.function); |
| | 1761 | const func = object.functions[func_index]; |
| | 1762 | break :ty object.func_types[func.type_index]; |
| | 1763 | }; |
| | 1764 | if (ty.params.len != 0) { |
| | 1765 | log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| | 1766 | return error.InvalidInitFunc; |
| | 1767 | } |
| | 1768 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); |
| | 1769 | wasm.init_funcs.appendAssumeCapacity(.{ |
| | 1770 | .index = init_func.symbol_index, |
| | 1771 | .file = @intCast(u16, file_index), |
| | 1772 | .priority = init_func.priority, |
| | 1773 | }); |
| | 1774 | } |
| | 1775 | } |
| | 1776 | |
| | 1777 | // sort the initfunctions based on their priority |
| | 1778 | std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); |
| | 1779 | } |
| | 1780 | |
| | 1781 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| | 1782 | /// Loops over all constructors found in `init_funcs` and calls them |
| | 1783 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| | 1784 | /// NOTE: This function must be called after we merged all sections to ensure the |
| | 1785 | /// references to the function stored in the symbol have been finalized so we end |
| | 1786 | /// up calling the resolved function. |
| | 1787 | fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| | 1788 | // No code to emit, so also no ctors to call |
| | 1789 | if (wasm.code_section_index == null) { |
| | 1790 | // Make sure to remove it from the resolved symbols so we do not emit |
| | 1791 | // it within any section. TODO: Remove this once we implement garbage collection. |
| | 1792 | const loc = wasm.globals.get(wasm.string_table.getOffset("__wasm_call_ctors").?).?; |
| | 1793 | std.debug.assert(wasm.resolved_symbols.swapRemove(loc)); |
| | 1794 | return; |
| | 1795 | } |
| | 1796 | |
| | 1797 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| | 1798 | defer function_body.deinit(); |
| | 1799 | const writer = function_body.writer(); |
| | 1800 | |
| | 1801 | // Create the function body |
| | 1802 | { |
| | 1803 | // Write locals count (we have none) |
| | 1804 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 1805 | |
| | 1806 | // call constructors |
| | 1807 | for (wasm.init_funcs.items) |init_func_loc| { |
| | 1808 | const symbol = init_func_loc.getSymbol(wasm); |
| | 1809 | if (symbol.isUndefined()) { |
| | 1810 | std.debug.print("Undefined symbol '{s}'\n", .{wasm.string_table.get(symbol.name)}); |
| | 1811 | } |
| | 1812 | std.debug.print("Symbol: {s}\n", .{init_func_loc.getSymbolLoc().getName(wasm)}); |
| | 1813 | std.debug.assert(wasm.resolved_symbols.contains(init_func_loc.getSymbolLoc().finalLoc(wasm))); |
| | 1814 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count]; |
| | 1815 | const ty = wasm.func_types.items[func.type_index]; |
| | 1816 | |
| | 1817 | // Call function by its function index |
| | 1818 | try writer.writeByte(std.wasm.opcode(.call)); |
| | 1819 | try leb.writeULEB128(writer, symbol.index); |
| | 1820 | |
| | 1821 | // drop all returned values from the stack as __wasm_call_ctors has no return value |
| | 1822 | for (ty.returns) |_| { |
| | 1823 | try writer.writeByte(std.wasm.opcode(.drop)); |
| | 1824 | } |
| | 1825 | } |
| | 1826 | |
| | 1827 | // End function body |
| | 1828 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 1829 | } |
| | 1830 | |
| | 1831 | const loc = wasm.globals.get(wasm.string_table.getOffset("__wasm_call_ctors").?).?; |
| | 1832 | const symbol = loc.getSymbol(wasm); |
| | 1833 | // create type (() -> nil) as we do not have any parameters or return value. |
| | 1834 | const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} }); |
| | 1835 | // create function with above type |
| | 1836 | const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count()); |
| | 1837 | try wasm.functions.putNoClobber( |
| | 1838 | wasm.base.allocator, |
| | 1839 | .{ .file = null, .index = func_index }, |
| | 1840 | .{ .type_index = ty_index }, |
| | 1841 | ); |
| | 1842 | symbol.index = func_index; |
| | 1843 | |
| | 1844 | // create the atom that will be output into the final binary |
| | 1845 | const atom = try wasm.base.allocator.create(Atom); |
| | 1846 | errdefer wasm.base.allocator.destroy(atom); |
| | 1847 | atom.* = .{ |
| | 1848 | .size = @intCast(u32, function_body.items.len), |
| | 1849 | .offset = 0, |
| | 1850 | .sym_index = loc.index, |
| | 1851 | .file = null, |
| | 1852 | .alignment = 1, |
| | 1853 | .next = null, |
| | 1854 | .prev = null, |
| | 1855 | .code = function_body.moveToUnmanaged(), |
| | 1856 | .dbg_info_atom = undefined, |
| | 1857 | }; |
| | 1858 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| | 1859 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom); |
| | 1860 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| | 1861 | |
| | 1862 | // `allocateAtoms` has already been called, set the atom's offset manually. |
| | 1863 | // This is fine to do manually as we insert the atom at the very end. |
| | 1864 | atom.offset = atom.prev.?.offset + atom.prev.?.size; |
| | 1865 | } |
| | 1866 | |
| 1701 | fn setupImports(wasm: *Wasm) !void { | 1867 | fn setupImports(wasm: *Wasm) !void { |
| 1702 | log.debug("Merging imports", .{}); | 1868 | log.debug("Merging imports", .{}); |
| 1703 | var discarded_it = wasm.discarded.keyIterator(); | 1869 | var discarded_it = wasm.discarded.keyIterator(); |
| ... | @@ -1870,16 +2036,17 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1870,16 +2036,17 @@ fn setupExports(wasm: *Wasm) !void { |
| 1870 | | 2036 | |
| 1871 | const force_exp_names = wasm.base.options.export_symbol_names; | 2037 | const force_exp_names = wasm.base.options.export_symbol_names; |
| 1872 | if (force_exp_names.len > 0) { | 2038 | if (force_exp_names.len > 0) { |
| 1873 | var failed_exports = try std.ArrayList([]const u8).initCapacity(wasm.base.allocator, force_exp_names.len); | 2039 | var failed_exports = false; |
| 1874 | defer failed_exports.deinit(); | | |
| 1875 | | 2040 | |
| 1876 | for (force_exp_names) |exp_name| { | 2041 | for (force_exp_names) |exp_name| { |
| 1877 | const name_index = wasm.string_table.getOffset(exp_name) orelse { | 2042 | const name_index = wasm.string_table.getOffset(exp_name) orelse { |
| 1878 | failed_exports.appendAssumeCapacity(exp_name); | 2043 | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| | 2044 | failed_exports = true; |
| 1879 | continue; | 2045 | continue; |
| 1880 | }; | 2046 | }; |
| 1881 | const loc = wasm.globals.get(name_index) orelse { | 2047 | const loc = wasm.globals.get(name_index) orelse { |
| 1882 | failed_exports.appendAssumeCapacity(exp_name); | 2048 | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| | 2049 | failed_exports = true; |
| 1883 | continue; | 2050 | continue; |
| 1884 | }; | 2051 | }; |
| 1885 | | 2052 | |
| ... | @@ -1887,10 +2054,7 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1887,10 +2054,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1887 | symbol.setFlag(.WASM_SYM_EXPORTED); | 2054 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1888 | } | 2055 | } |
| 1889 | | 2056 | |
| 1890 | if (failed_exports.items.len > 0) { | 2057 | if (failed_exports) { |
| 1891 | for (failed_exports.items) |exp_name| { | | |
| 1892 | log.err("could not export '{s}', symbol not found", .{exp_name}); | | |
| 1893 | } | | |
| 1894 | return error.MissingSymbol; | 2058 | return error.MissingSymbol; |
| 1895 | } | 2059 | } |
| 1896 | } | 2060 | } |
| ... | @@ -1948,6 +2112,7 @@ fn setupStart(wasm: *Wasm) !void { | ... | @@ -1948,6 +2112,7 @@ fn setupStart(wasm: *Wasm) !void { |
| 1948 | | 2112 | |
| 1949 | const symbol_loc = wasm.globals.get(symbol_name_offset) orelse { | 2113 | const symbol_loc = wasm.globals.get(symbol_name_offset) orelse { |
| 1950 | log.err("Entry symbol '{s}' not found", .{entry_name}); | 2114 | log.err("Entry symbol '{s}' not found", .{entry_name}); |
| | 2115 | return error.MissingSymbol; |
| 1951 | }; | 2116 | }; |
| 1952 | const symbol = symbol_loc.getSymbol(wasm); | 2117 | const symbol = symbol_loc.getSymbol(wasm); |
| 1953 | if (symbol.tag != .function) { | 2118 | if (symbol.tag != .function) { |
| ... | @@ -2503,6 +2668,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2503,6 +2668,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2503 | try wasm.resolveSymbolsInArchives(); | 2668 | try wasm.resolveSymbolsInArchives(); |
| 2504 | try wasm.checkUndefinedSymbols(); | 2669 | try wasm.checkUndefinedSymbols(); |
| 2505 | | 2670 | |
| | 2671 | try wasm.setupInitFunctions(); |
| 2506 | try wasm.setupStart(); | 2672 | try wasm.setupStart(); |
| 2507 | try wasm.setupImports(); | 2673 | try wasm.setupImports(); |
| 2508 | | 2674 | |
| ... | @@ -2515,6 +2681,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2515,6 +2681,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2515 | wasm.mapFunctionTable(); | 2681 | wasm.mapFunctionTable(); |
| 2516 | try wasm.mergeSections(); | 2682 | try wasm.mergeSections(); |
| 2517 | try wasm.mergeTypes(); | 2683 | try wasm.mergeTypes(); |
| | 2684 | try wasm.initializeCallCtorsFunction(); |
| 2518 | try wasm.setupExports(); | 2685 | try wasm.setupExports(); |
| 2519 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 2686 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2520 | | 2687 | |
| ... | @@ -2587,6 +2754,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2587,6 +2754,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2587 | // When we finish/error we reset the state of the linker | 2754 | // When we finish/error we reset the state of the linker |
| 2588 | // So we can rebuild the binary file on each incremental update | 2755 | // So we can rebuild the binary file on each incremental update |
| 2589 | defer wasm.resetState(); | 2756 | defer wasm.resetState(); |
| | 2757 | try wasm.setupInitFunctions(); |
| 2590 | try wasm.setupStart(); | 2758 | try wasm.setupStart(); |
| 2591 | try wasm.setupImports(); | 2759 | try wasm.setupImports(); |
| 2592 | if (wasm.base.options.module) |mod| { | 2760 | if (wasm.base.options.module) |mod| { |
| ... | @@ -2629,6 +2797,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2629,6 +2797,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2629 | wasm.mapFunctionTable(); | 2797 | wasm.mapFunctionTable(); |
| 2630 | try wasm.mergeSections(); | 2798 | try wasm.mergeSections(); |
| 2631 | try wasm.mergeTypes(); | 2799 | try wasm.mergeTypes(); |
| | 2800 | try wasm.initializeCallCtorsFunction(); |
| 2632 | try wasm.setupExports(); | 2801 | try wasm.setupExports(); |
| 2633 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 2802 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2634 | } | 2803 | } |
| ... | @@ -3909,8 +4078,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { | ... | @@ -3909,8 +4078,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 3909 | return null; | 4078 | return null; |
| 3910 | } | 4079 | } |
| 3911 | | 4080 | |
| 3912 | /// Searches for an a matching function signature, when not found | 4081 | /// Searches for a matching function signature. When no matching signature is found, |
| 3913 | /// a new entry will be made. The index of the existing/new signature will be returned. | 4082 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 3914 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { | 4083 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 3915 | if (wasm.getTypeIndex(func_type)) |index| { | 4084 | if (wasm.getTypeIndex(func_type)) |index| { |
| 3916 | return index; | 4085 | return index; |