| ... | ... | @@ -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(); |
| ... | ... | @@ -434,7 +475,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 434 | 475 | .index = undefined, |
| 435 | 476 | }); |
| 436 | 477 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); |
| 437 | | try wasm.globals.putNoClobber(wasm.base.allocator, name_offset, loc); |
| 478 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| 438 | 479 | return loc; |
| 439 | 480 | } |
| 440 | 481 | /// Initializes symbols and atoms for the debug sections |
| ... | ... | @@ -600,27 +641,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 600 | 641 | } |
| 601 | 642 | |
| 602 | 643 | if (existing_sym.isUndefined() and symbol.isUndefined()) { |
| 603 | | const existing_name = if (existing_loc.file) |file_index| blk: { |
| 604 | | const obj = wasm.objects.items[file_index]; |
| 605 | | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; |
| 606 | | break :blk obj.string_table.get(name_index); |
| 607 | | } else blk: { |
| 608 | | const name_index = wasm.imports.get(existing_loc).?.module_name; |
| 609 | | break :blk wasm.string_table.get(name_index); |
| 610 | | }; |
| 644 | // only verify module/import name for function symbols |
| 645 | if (symbol.tag == .function) { |
| 646 | const existing_name = if (existing_loc.file) |file_index| blk: { |
| 647 | const obj = wasm.objects.items[file_index]; |
| 648 | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; |
| 649 | break :blk obj.string_table.get(name_index); |
| 650 | } else blk: { |
| 651 | const name_index = wasm.imports.get(existing_loc).?.module_name; |
| 652 | break :blk wasm.string_table.get(name_index); |
| 653 | }; |
| 611 | 654 | |
| 612 | | const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; |
| 613 | | const module_name = object.string_table.get(module_index); |
| 614 | | if (!mem.eql(u8, existing_name, module_name)) { |
| 615 | | log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ |
| 616 | | sym_name, |
| 617 | | existing_name, |
| 618 | | module_name, |
| 619 | | }); |
| 620 | | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 621 | | log.err(" next definition in '{s}'", .{object.name}); |
| 622 | | return error.ModuleNameMismatch; |
| 655 | const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; |
| 656 | const module_name = object.string_table.get(module_index); |
| 657 | if (!mem.eql(u8, existing_name, module_name)) { |
| 658 | log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ |
| 659 | sym_name, |
| 660 | existing_name, |
| 661 | module_name, |
| 662 | }); |
| 663 | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 664 | log.err(" next definition in '{s}'", .{object.name}); |
| 665 | return error.ModuleNameMismatch; |
| 666 | } |
| 623 | 667 | } |
| 668 | |
| 669 | // both undefined so skip overwriting existing symbol and discard the new symbol |
| 670 | try wasm.discarded.put(wasm.base.allocator, location, existing_loc); |
| 671 | continue; |
| 624 | 672 | } |
| 625 | 673 | |
| 626 | 674 | if (existing_sym.tag == .global) { |
| ... | ... | @@ -646,8 +694,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 646 | 694 | } |
| 647 | 695 | } |
| 648 | 696 | |
| 649 | | // when both symbols are weak, we skip overwriting |
| 650 | | if (existing_sym.isWeak() and symbol.isWeak()) { |
| 697 | // when both symbols are weak, we skip overwriting unless the existing |
| 698 | // symbol is weak and the new one isn't, in which case we *do* overwrite it. |
| 699 | if (existing_sym.isWeak() and symbol.isWeak()) blk: { |
| 700 | if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk; |
| 651 | 701 | try wasm.discarded.put(wasm.base.allocator, location, existing_loc); |
| 652 | 702 | continue; |
| 653 | 703 | } |
| ... | ... | @@ -801,6 +851,51 @@ fn validateFeatures( |
| 801 | 851 | to_emit.* = allowed; |
| 802 | 852 | } |
| 803 | 853 | |
| 854 | /// Creates synthetic linker-symbols, but only if they are being referenced from |
| 855 | /// any object file. For instance, the `__heap_base` symbol will only be created, |
| 856 | /// if one or multiple undefined references exist. When none exist, the symbol will |
| 857 | /// not be created, ensuring we don't unneccesarily emit unreferenced symbols. |
| 858 | fn resolveLazySymbols(wasm: *Wasm) !void { |
| 859 | if (wasm.undefs.fetchSwapRemove("__heap_base")) |kv| { |
| 860 | const loc = try wasm.createSyntheticSymbol("__heap_base", .data); |
| 861 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 862 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. |
| 863 | |
| 864 | const atom = try wasm.base.allocator.create(Atom); |
| 865 | errdefer wasm.base.allocator.destroy(atom); |
| 866 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| 867 | atom.* = Atom.empty; |
| 868 | atom.sym_index = loc.index; |
| 869 | atom.alignment = 1; |
| 870 | |
| 871 | try wasm.parseAtom(atom, .{ .data = .synthetic }); |
| 872 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| 873 | } |
| 874 | |
| 875 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { |
| 876 | const loc = try wasm.createSyntheticSymbol("__heap_end", .data); |
| 877 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 878 | _ = wasm.resolved_symbols.swapRemove(loc); |
| 879 | |
| 880 | const atom = try wasm.base.allocator.create(Atom); |
| 881 | errdefer wasm.base.allocator.destroy(atom); |
| 882 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| 883 | atom.* = Atom.empty; |
| 884 | atom.sym_index = loc.index; |
| 885 | atom.alignment = 1; |
| 886 | |
| 887 | try wasm.parseAtom(atom, .{ .data = .synthetic }); |
| 888 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| 889 | } |
| 890 | } |
| 891 | |
| 892 | // Tries to find a global symbol by its name. Returns null when not found, |
| 893 | /// and its location when it is found. |
| 894 | fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { |
| 895 | const offset = wasm.string_table.getOffset(name) orelse return null; |
| 896 | return wasm.globals.get(offset); |
| 897 | } |
| 898 | |
| 804 | 899 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 805 | 900 | if (wasm.base.options.output_mode == .Obj) return; |
| 806 | 901 | if (wasm.base.options.import_symbols) return; |
| ... | ... | @@ -813,12 +908,8 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 813 | 908 | const file_name = if (undef.file) |file_index| name: { |
| 814 | 909 | break :name wasm.objects.items[file_index].name; |
| 815 | 910 | } else wasm.name; |
| 816 | | const import_name = if (undef.file) |file_index| name: { |
| 817 | | const obj = wasm.objects.items[file_index]; |
| 818 | | const name_index = obj.findImport(symbol.tag.externalType(), symbol.index).name; |
| 819 | | break :name obj.string_table.get(name_index); |
| 820 | | } else wasm.string_table.get(wasm.imports.get(undef).?.name); |
| 821 | | log.err("could not resolve undefined symbol '{s}'", .{import_name}); |
| 911 | const symbol_name = undef.getName(wasm); |
| 912 | log.err("could not resolve undefined symbol '{s}'", .{symbol_name}); |
| 822 | 913 | log.err(" defined in '{s}'", .{file_name}); |
| 823 | 914 | } |
| 824 | 915 | } |
| ... | ... | @@ -885,6 +976,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 885 | 976 | wasm.wasm_globals.deinit(gpa); |
| 886 | 977 | wasm.function_table.deinit(gpa); |
| 887 | 978 | wasm.tables.deinit(gpa); |
| 979 | wasm.init_funcs.deinit(gpa); |
| 888 | 980 | wasm.exports.deinit(gpa); |
| 889 | 981 | |
| 890 | 982 | wasm.string_table.deinit(gpa); |
| ... | ... | @@ -1405,14 +1497,13 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1405 | 1497 | } |
| 1406 | 1498 | |
| 1407 | 1499 | if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { |
| 1408 | | const sym_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 1500 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1409 | 1501 | const import = wasm.imports.getPtr(sym_loc).?; |
| 1410 | 1502 | import.kind.table.limits.min = index - 1; // we start at index 1. |
| 1411 | 1503 | } else if (index > 1) { |
| 1412 | 1504 | log.debug("Appending indirect function table", .{}); |
| 1413 | | const offset = wasm.string_table.getOffset("__indirect_function_table").?; |
| 1414 | | const sym_with_loc = wasm.globals.get(offset).?; |
| 1415 | | const symbol = sym_with_loc.getSymbol(wasm); |
| 1505 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1506 | const symbol = sym_loc.getSymbol(wasm); |
| 1416 | 1507 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; |
| 1417 | 1508 | table.limits = .{ .min = index, .max = index }; |
| 1418 | 1509 | } |
| ... | ... | @@ -1491,6 +1582,7 @@ const Kind = union(enum) { |
| 1491 | 1582 | read_only, |
| 1492 | 1583 | uninitialized, |
| 1493 | 1584 | initialized, |
| 1585 | synthetic, |
| 1494 | 1586 | }, |
| 1495 | 1587 | function: FnData, |
| 1496 | 1588 | |
| ... | ... | @@ -1501,6 +1593,7 @@ const Kind = union(enum) { |
| 1501 | 1593 | .read_only => return ".rodata.", |
| 1502 | 1594 | .uninitialized => return ".bss.", |
| 1503 | 1595 | .initialized => return ".data.", |
| 1596 | .synthetic => return ".synthetic", |
| 1504 | 1597 | } |
| 1505 | 1598 | } |
| 1506 | 1599 | }; |
| ... | ... | @@ -1637,9 +1730,14 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1637 | 1730 | var offset: u32 = 0; |
| 1638 | 1731 | while (true) { |
| 1639 | 1732 | const symbol_loc = atom.symbolLoc(); |
| 1640 | | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1641 | | atom = atom.next orelse break; |
| 1642 | | continue; |
| 1733 | if (wasm.code_section_index) |index| { |
| 1734 | if (index == entry.key_ptr.*) { |
| 1735 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1736 | // only allocate resolved function body's. |
| 1737 | atom = atom.next orelse break; |
| 1738 | continue; |
| 1739 | } |
| 1740 | } |
| 1643 | 1741 | } |
| 1644 | 1742 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); |
| 1645 | 1743 | atom.offset = offset; |
| ... | ... | @@ -1674,6 +1772,7 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1674 | 1772 | if (mem.startsWith(u8, name, ".rodata")) return 0; |
| 1675 | 1773 | if (mem.startsWith(u8, name, ".data")) return 1; |
| 1676 | 1774 | if (mem.startsWith(u8, name, ".text")) return 2; |
| 1775 | if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end |
| 1677 | 1776 | return 3; |
| 1678 | 1777 | } |
| 1679 | 1778 | }; |
| ... | ... | @@ -1687,6 +1786,125 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1687 | 1786 | wasm.data_segments = new_mapping; |
| 1688 | 1787 | } |
| 1689 | 1788 | |
| 1789 | /// Obtains all initfuncs from each object file, verifies its function signature, |
| 1790 | /// and then appends it to our final `init_funcs` list. |
| 1791 | /// After all functions have been inserted, the functions will be ordered based |
| 1792 | /// on their priority. |
| 1793 | /// NOTE: This function must be called before we merged any other section. |
| 1794 | /// This is because all init funcs in the object files contain references to the |
| 1795 | /// original functions and their types. We need to know the type to verify it doesn't |
| 1796 | /// contain any parameters. |
| 1797 | fn setupInitFunctions(wasm: *Wasm) !void { |
| 1798 | for (wasm.objects.items) |object, file_index| { |
| 1799 | try wasm.init_funcs.ensureUnusedCapacity(wasm.base.allocator, object.init_funcs.len); |
| 1800 | for (object.init_funcs) |init_func| { |
| 1801 | const symbol = object.symtable[init_func.symbol_index]; |
| 1802 | const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { |
| 1803 | const imp: types.Import = object.findImport(.function, symbol.index); |
| 1804 | break :ty object.func_types[imp.kind.function]; |
| 1805 | } else ty: { |
| 1806 | const func_index = symbol.index - object.importedCountByKind(.function); |
| 1807 | const func = object.functions[func_index]; |
| 1808 | break :ty object.func_types[func.type_index]; |
| 1809 | }; |
| 1810 | if (ty.params.len != 0) { |
| 1811 | log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| 1812 | return error.InvalidInitFunc; |
| 1813 | } |
| 1814 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); |
| 1815 | wasm.init_funcs.appendAssumeCapacity(.{ |
| 1816 | .index = init_func.symbol_index, |
| 1817 | .file = @intCast(u16, file_index), |
| 1818 | .priority = init_func.priority, |
| 1819 | }); |
| 1820 | } |
| 1821 | } |
| 1822 | |
| 1823 | // sort the initfunctions based on their priority |
| 1824 | std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); |
| 1825 | } |
| 1826 | |
| 1827 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| 1828 | /// Loops over all constructors found in `init_funcs` and calls them |
| 1829 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| 1830 | /// NOTE: This function must be called after we merged all sections to ensure the |
| 1831 | /// references to the function stored in the symbol have been finalized so we end |
| 1832 | /// up calling the resolved function. |
| 1833 | fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1834 | // No code to emit, so also no ctors to call |
| 1835 | if (wasm.code_section_index == null) { |
| 1836 | // Make sure to remove it from the resolved symbols so we do not emit |
| 1837 | // it within any section. TODO: Remove this once we implement garbage collection. |
| 1838 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| 1839 | std.debug.assert(wasm.resolved_symbols.swapRemove(loc)); |
| 1840 | return; |
| 1841 | } |
| 1842 | |
| 1843 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| 1844 | defer function_body.deinit(); |
| 1845 | const writer = function_body.writer(); |
| 1846 | |
| 1847 | // Create the function body |
| 1848 | { |
| 1849 | // Write locals count (we have none) |
| 1850 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1851 | |
| 1852 | // call constructors |
| 1853 | for (wasm.init_funcs.items) |init_func_loc| { |
| 1854 | const symbol = init_func_loc.getSymbol(wasm); |
| 1855 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count]; |
| 1856 | const ty = wasm.func_types.items[func.type_index]; |
| 1857 | |
| 1858 | // Call function by its function index |
| 1859 | try writer.writeByte(std.wasm.opcode(.call)); |
| 1860 | try leb.writeULEB128(writer, symbol.index); |
| 1861 | |
| 1862 | // drop all returned values from the stack as __wasm_call_ctors has no return value |
| 1863 | for (ty.returns) |_| { |
| 1864 | try writer.writeByte(std.wasm.opcode(.drop)); |
| 1865 | } |
| 1866 | } |
| 1867 | |
| 1868 | // End function body |
| 1869 | try writer.writeByte(std.wasm.opcode(.end)); |
| 1870 | } |
| 1871 | |
| 1872 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| 1873 | const symbol = loc.getSymbol(wasm); |
| 1874 | // create type (() -> nil) as we do not have any parameters or return value. |
| 1875 | const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} }); |
| 1876 | // create function with above type |
| 1877 | const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count()); |
| 1878 | try wasm.functions.putNoClobber( |
| 1879 | wasm.base.allocator, |
| 1880 | .{ .file = null, .index = func_index }, |
| 1881 | .{ .type_index = ty_index }, |
| 1882 | ); |
| 1883 | symbol.index = func_index; |
| 1884 | |
| 1885 | // create the atom that will be output into the final binary |
| 1886 | const atom = try wasm.base.allocator.create(Atom); |
| 1887 | errdefer wasm.base.allocator.destroy(atom); |
| 1888 | atom.* = .{ |
| 1889 | .size = @intCast(u32, function_body.items.len), |
| 1890 | .offset = 0, |
| 1891 | .sym_index = loc.index, |
| 1892 | .file = null, |
| 1893 | .alignment = 1, |
| 1894 | .next = null, |
| 1895 | .prev = null, |
| 1896 | .code = function_body.moveToUnmanaged(), |
| 1897 | .dbg_info_atom = undefined, |
| 1898 | }; |
| 1899 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| 1900 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom); |
| 1901 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| 1902 | |
| 1903 | // `allocateAtoms` has already been called, set the atom's offset manually. |
| 1904 | // This is fine to do manually as we insert the atom at the very end. |
| 1905 | atom.offset = atom.prev.?.offset + atom.prev.?.size; |
| 1906 | } |
| 1907 | |
| 1690 | 1908 | fn setupImports(wasm: *Wasm) !void { |
| 1691 | 1909 | log.debug("Merging imports", .{}); |
| 1692 | 1910 | var discarded_it = wasm.discarded.keyIterator(); |
| ... | ... | @@ -1859,16 +2077,12 @@ fn setupExports(wasm: *Wasm) !void { |
| 1859 | 2077 | |
| 1860 | 2078 | const force_exp_names = wasm.base.options.export_symbol_names; |
| 1861 | 2079 | if (force_exp_names.len > 0) { |
| 1862 | | var failed_exports = try std.ArrayList([]const u8).initCapacity(wasm.base.allocator, force_exp_names.len); |
| 1863 | | defer failed_exports.deinit(); |
| 2080 | var failed_exports = false; |
| 1864 | 2081 | |
| 1865 | 2082 | for (force_exp_names) |exp_name| { |
| 1866 | | const name_index = wasm.string_table.getOffset(exp_name) orelse { |
| 1867 | | failed_exports.appendAssumeCapacity(exp_name); |
| 1868 | | continue; |
| 1869 | | }; |
| 1870 | | const loc = wasm.globals.get(name_index) orelse { |
| 1871 | | failed_exports.appendAssumeCapacity(exp_name); |
| 2083 | const loc = wasm.findGlobalSymbol(exp_name) orelse { |
| 2084 | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 2085 | failed_exports = true; |
| 1872 | 2086 | continue; |
| 1873 | 2087 | }; |
| 1874 | 2088 | |
| ... | ... | @@ -1876,10 +2090,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1876 | 2090 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1877 | 2091 | } |
| 1878 | 2092 | |
| 1879 | | if (failed_exports.items.len > 0) { |
| 1880 | | for (failed_exports.items) |exp_name| { |
| 1881 | | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 1882 | | } |
| 2093 | if (failed_exports) { |
| 1883 | 2094 | return error.MissingSymbol; |
| 1884 | 2095 | } |
| 1885 | 2096 | } |
| ... | ... | @@ -1925,7 +2136,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1925 | 2136 | fn setupStart(wasm: *Wasm) !void { |
| 1926 | 2137 | const entry_name = wasm.base.options.entry orelse "_start"; |
| 1927 | 2138 | |
| 1928 | | const symbol_name_offset = wasm.string_table.getOffset(entry_name) orelse { |
| 2139 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 1929 | 2140 | if (wasm.base.options.output_mode == .Exe) { |
| 1930 | 2141 | if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors |
| 1931 | 2142 | } else { |
| ... | ... | @@ -1935,7 +2146,6 @@ fn setupStart(wasm: *Wasm) !void { |
| 1935 | 2146 | return error.MissingSymbol; |
| 1936 | 2147 | }; |
| 1937 | 2148 | |
| 1938 | | const symbol_loc = wasm.globals.get(symbol_name_offset).?; |
| 1939 | 2149 | const symbol = symbol_loc.getSymbol(wasm); |
| 1940 | 2150 | if (symbol.tag != .function) { |
| 1941 | 2151 | log.err("Entry symbol '{s}' is not a function", .{entry_name}); |
| ... | ... | @@ -1955,6 +2165,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1955 | 2165 | // Use the user-provided stack size or else we use 1MB by default |
| 1956 | 2166 | const stack_size = wasm.base.options.stack_size_override orelse page_size * 16; |
| 1957 | 2167 | const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention |
| 2168 | const heap_alignment = 16; // wasm's heap alignment as specified by tool-convention |
| 2169 | |
| 1958 | 2170 | // Always place the stack at the start by default |
| 1959 | 2171 | // unless the user specified the global-base flag |
| 1960 | 2172 | var place_stack_first = true; |
| ... | ... | @@ -1973,8 +2185,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1973 | 2185 | } |
| 1974 | 2186 | |
| 1975 | 2187 | var offset: u32 = @intCast(u32, memory_ptr); |
| 1976 | | for (wasm.data_segments.values()) |segment_index| { |
| 1977 | | const segment = &wasm.segments.items[segment_index]; |
| 2188 | var data_seg_it = wasm.data_segments.iterator(); |
| 2189 | while (data_seg_it.next()) |entry| { |
| 2190 | if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) { |
| 2191 | // do not update synthetic segments as they are not part of the output |
| 2192 | continue; |
| 2193 | } |
| 2194 | const segment = &wasm.segments.items[entry.value_ptr.*]; |
| 1978 | 2195 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); |
| 1979 | 2196 | memory_ptr += segment.size; |
| 1980 | 2197 | segment.offset = offset; |
| ... | ... | @@ -1987,6 +2204,16 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1987 | 2204 | wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); |
| 1988 | 2205 | } |
| 1989 | 2206 | |
| 2207 | // One of the linked object files has a reference to the __heap_base symbol. |
| 2208 | // We must set its virtual address so it can be used in relocations. |
| 2209 | if (wasm.findGlobalSymbol("__heap_base")) |loc| { |
| 2210 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2211 | const segment = &wasm.segments.items[segment_index]; |
| 2212 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. |
| 2213 | const atom = wasm.symbol_atom.get(loc).?; |
| 2214 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); |
| 2215 | } |
| 2216 | |
| 1990 | 2217 | // Setup the max amount of pages |
| 1991 | 2218 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 |
| 1992 | 2219 | const max_memory_allowed: u64 = (1 << 32) - 1; |
| ... | ... | @@ -2006,12 +2233,20 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2006 | 2233 | } |
| 2007 | 2234 | memory_ptr = initial_memory; |
| 2008 | 2235 | } |
| 2009 | | |
| 2236 | memory_ptr = mem.alignForwardGeneric(u64, memory_ptr, std.wasm.page_size); |
| 2010 | 2237 | // In case we do not import memory, but define it ourselves, |
| 2011 | 2238 | // set the minimum amount of pages on the memory section. |
| 2012 | | wasm.memories.limits.min = @intCast(u32, std.mem.alignForwardGeneric(u64, memory_ptr, page_size) / page_size); |
| 2239 | wasm.memories.limits.min = @intCast(u32, memory_ptr / page_size); |
| 2013 | 2240 | log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); |
| 2014 | 2241 | |
| 2242 | if (wasm.findGlobalSymbol("__heap_end")) |loc| { |
| 2243 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2244 | const segment = &wasm.segments.items[segment_index]; |
| 2245 | segment.offset = 0; |
| 2246 | const atom = wasm.symbol_atom.get(loc).?; |
| 2247 | atom.offset = @intCast(u32, memory_ptr); |
| 2248 | } |
| 2249 | |
| 2015 | 2250 | if (wasm.base.options.max_memory) |max_memory| { |
| 2016 | 2251 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { |
| 2017 | 2252 | log.err("Maximum memory must be {d}-byte aligned", .{page_size}); |
| ... | ... | @@ -2488,8 +2723,10 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2488 | 2723 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2489 | 2724 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2490 | 2725 | try wasm.resolveSymbolsInArchives(); |
| 2726 | try wasm.resolveLazySymbols(); |
| 2491 | 2727 | try wasm.checkUndefinedSymbols(); |
| 2492 | 2728 | |
| 2729 | try wasm.setupInitFunctions(); |
| 2493 | 2730 | try wasm.setupStart(); |
| 2494 | 2731 | try wasm.setupImports(); |
| 2495 | 2732 | |
| ... | ... | @@ -2502,6 +2739,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2502 | 2739 | wasm.mapFunctionTable(); |
| 2503 | 2740 | try wasm.mergeSections(); |
| 2504 | 2741 | try wasm.mergeTypes(); |
| 2742 | try wasm.initializeCallCtorsFunction(); |
| 2505 | 2743 | try wasm.setupExports(); |
| 2506 | 2744 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2507 | 2745 | |
| ... | ... | @@ -2569,11 +2807,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2569 | 2807 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2570 | 2808 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2571 | 2809 | try wasm.resolveSymbolsInArchives(); |
| 2810 | try wasm.resolveLazySymbols(); |
| 2572 | 2811 | try wasm.checkUndefinedSymbols(); |
| 2573 | 2812 | |
| 2574 | 2813 | // When we finish/error we reset the state of the linker |
| 2575 | 2814 | // So we can rebuild the binary file on each incremental update |
| 2576 | 2815 | defer wasm.resetState(); |
| 2816 | try wasm.setupInitFunctions(); |
| 2577 | 2817 | try wasm.setupStart(); |
| 2578 | 2818 | try wasm.setupImports(); |
| 2579 | 2819 | if (wasm.base.options.module) |mod| { |
| ... | ... | @@ -2616,6 +2856,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2616 | 2856 | wasm.mapFunctionTable(); |
| 2617 | 2857 | try wasm.mergeSections(); |
| 2618 | 2858 | try wasm.mergeTypes(); |
| 2859 | try wasm.initializeCallCtorsFunction(); |
| 2619 | 2860 | try wasm.setupExports(); |
| 2620 | 2861 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2621 | 2862 | } |
| ... | ... | @@ -2810,7 +3051,7 @@ fn writeToFile( |
| 2810 | 3051 | if (wasm.function_table.count() > 0) { |
| 2811 | 3052 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2812 | 3053 | |
| 2813 | | const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 3054 | const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 2814 | 3055 | const table_sym = table_loc.getSymbol(wasm); |
| 2815 | 3056 | |
| 2816 | 3057 | var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually |
| ... | ... | @@ -2849,10 +3090,12 @@ fn writeToFile( |
| 2849 | 3090 | defer sorted_atoms.deinit(); |
| 2850 | 3091 | |
| 2851 | 3092 | while (true) { |
| 2852 | | if (!is_obj) { |
| 2853 | | atom.resolveRelocs(wasm); |
| 3093 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { |
| 3094 | if (!is_obj) { |
| 3095 | atom.resolveRelocs(wasm); |
| 3096 | } |
| 3097 | sorted_atoms.appendAssumeCapacity(atom); |
| 2854 | 3098 | } |
| 2855 | | sorted_atoms.appendAssumeCapacity(atom); |
| 2856 | 3099 | atom = atom.next orelse break; |
| 2857 | 3100 | } |
| 2858 | 3101 | |
| ... | ... | @@ -2893,10 +3136,11 @@ fn writeToFile( |
| 2893 | 3136 | // do not output 'bss' section unless we import memory and therefore |
| 2894 | 3137 | // want to guarantee the data is zero initialized |
| 2895 | 3138 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 2896 | | segment_count += 1; |
| 2897 | 3139 | const atom_index = entry.value_ptr.*; |
| 2898 | | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); |
| 2899 | 3140 | const segment = wasm.segments.items[atom_index]; |
| 3141 | if (segment.size == 0) continue; // do not emit empty segments |
| 3142 | segment_count += 1; |
| 3143 | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); |
| 2900 | 3144 | |
| 2901 | 3145 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) |
| 2902 | 3146 | try leb.writeULEB128(binary_writer, @as(u32, 0)); |
| ... | ... | @@ -3166,6 +3410,8 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem |
| 3166 | 3410 | // bss section is not emitted when this condition holds true, so we also |
| 3167 | 3411 | // do not output a name for it. |
| 3168 | 3412 | if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; |
| 3413 | // Synthetic segments are not emitted |
| 3414 | if (std.mem.eql(u8, key, ".synthetic")) continue; |
| 3169 | 3415 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); |
| 3170 | 3416 | data_segment_index += 1; |
| 3171 | 3417 | } |
| ... | ... | @@ -3896,8 +4142,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 3896 | 4142 | return null; |
| 3897 | 4143 | } |
| 3898 | 4144 | |
| 3899 | | /// Searches for an a matching function signature, when not found |
| 3900 | | /// a new entry will be made. The index of the existing/new signature will be returned. |
| 4145 | /// Searches for a matching function signature. When no matching signature is found, |
| 4146 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 3901 | 4147 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 3902 | 4148 | if (wasm.getTypeIndex(func_type)) |index| { |
| 3903 | 4149 | return index; |