| ... | ... | @@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, |
| 118 | 118 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, |
| 119 | 119 | /// Output export section |
| 120 | 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 | 125 | /// Indirect function table, used to call function pointers |
| 123 | 126 | /// When this is non-zero, we must emit a table entry, |
| ... | ... | @@ -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 | 272 | /// Generic string table that duplicates strings |
| 242 | 273 | /// and converts them into offsets instead. |
| 243 | 274 | pub const StringTable = struct { |
| ... | ... | @@ -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 | 437 | if (!options.strip and options.module != null) { |
| 397 | 438 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); |
| 398 | 439 | try wasm_bin.initDebugSections(); |
| ... | ... | @@ -896,6 +937,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 896 | 937 | wasm.wasm_globals.deinit(gpa); |
| 897 | 938 | wasm.function_table.deinit(gpa); |
| 898 | 939 | wasm.tables.deinit(gpa); |
| 940 | wasm.init_funcs.deinit(gpa); |
| 899 | 941 | wasm.exports.deinit(gpa); |
| 900 | 942 | |
| 901 | 943 | wasm.string_table.deinit(gpa); |
| ... | ... | @@ -1698,6 +1740,130 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1698 | 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 | 1867 | fn setupImports(wasm: *Wasm) !void { |
| 1702 | 1868 | log.debug("Merging imports", .{}); |
| 1703 | 1869 | var discarded_it = wasm.discarded.keyIterator(); |
| ... | ... | @@ -1870,16 +2036,17 @@ fn setupExports(wasm: *Wasm) !void { |
| 1870 | 2036 | |
| 1871 | 2037 | const force_exp_names = wasm.base.options.export_symbol_names; |
| 1872 | 2038 | if (force_exp_names.len > 0) { |
| 1873 | | var failed_exports = try std.ArrayList([]const u8).initCapacity(wasm.base.allocator, force_exp_names.len); |
| 1874 | | defer failed_exports.deinit(); |
| 2039 | var failed_exports = false; |
| 1875 | 2040 | |
| 1876 | 2041 | for (force_exp_names) |exp_name| { |
| 1877 | 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 | 2045 | continue; |
| 1880 | 2046 | }; |
| 1881 | 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 | 2050 | continue; |
| 1884 | 2051 | }; |
| 1885 | 2052 | |
| ... | ... | @@ -1887,10 +2054,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1887 | 2054 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1888 | 2055 | } |
| 1889 | 2056 | |
| 1890 | | if (failed_exports.items.len > 0) { |
| 1891 | | for (failed_exports.items) |exp_name| { |
| 1892 | | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 1893 | | } |
| 2057 | if (failed_exports) { |
| 1894 | 2058 | return error.MissingSymbol; |
| 1895 | 2059 | } |
| 1896 | 2060 | } |
| ... | ... | @@ -1948,6 +2112,7 @@ fn setupStart(wasm: *Wasm) !void { |
| 1948 | 2112 | |
| 1949 | 2113 | const symbol_loc = wasm.globals.get(symbol_name_offset) orelse { |
| 1950 | 2114 | log.err("Entry symbol '{s}' not found", .{entry_name}); |
| 2115 | return error.MissingSymbol; |
| 1951 | 2116 | }; |
| 1952 | 2117 | const symbol = symbol_loc.getSymbol(wasm); |
| 1953 | 2118 | if (symbol.tag != .function) { |
| ... | ... | @@ -2503,6 +2668,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2503 | 2668 | try wasm.resolveSymbolsInArchives(); |
| 2504 | 2669 | try wasm.checkUndefinedSymbols(); |
| 2505 | 2670 | |
| 2671 | try wasm.setupInitFunctions(); |
| 2506 | 2672 | try wasm.setupStart(); |
| 2507 | 2673 | try wasm.setupImports(); |
| 2508 | 2674 | |
| ... | ... | @@ -2515,6 +2681,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2515 | 2681 | wasm.mapFunctionTable(); |
| 2516 | 2682 | try wasm.mergeSections(); |
| 2517 | 2683 | try wasm.mergeTypes(); |
| 2684 | try wasm.initializeCallCtorsFunction(); |
| 2518 | 2685 | try wasm.setupExports(); |
| 2519 | 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 | 2754 | // When we finish/error we reset the state of the linker |
| 2588 | 2755 | // So we can rebuild the binary file on each incremental update |
| 2589 | 2756 | defer wasm.resetState(); |
| 2757 | try wasm.setupInitFunctions(); |
| 2590 | 2758 | try wasm.setupStart(); |
| 2591 | 2759 | try wasm.setupImports(); |
| 2592 | 2760 | if (wasm.base.options.module) |mod| { |
| ... | ... | @@ -2629,6 +2797,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2629 | 2797 | wasm.mapFunctionTable(); |
| 2630 | 2798 | try wasm.mergeSections(); |
| 2631 | 2799 | try wasm.mergeTypes(); |
| 2800 | try wasm.initializeCallCtorsFunction(); |
| 2632 | 2801 | try wasm.setupExports(); |
| 2633 | 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 | 4078 | return null; |
| 3910 | 4079 | } |
| 3911 | 4080 | |
| 3912 | | /// Searches for an a matching function signature, when not found |
| 3913 | | /// a new entry will be made. The index of the existing/new signature will be returned. |
| 4081 | /// Searches for a matching function signature. When no matching signature is found, |
| 4082 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 3914 | 4083 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 3915 | 4084 | if (wasm.getTypeIndex(func_type)) |index| { |
| 3916 | 4085 | return index; |