authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-01-14 17:58:09+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-14 17:58:09+01:00
log18191b80b6381eb41adb4354a243190865801212
treeaebc5cea62db5bf02ce273eb8ed0387f75e13045
parent6b3f59c3a735ddbda3b3a62a0dfb5d55fa045f57
parent5468684456b13b6465c4fcd50c072e5d5c8536a3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14302 from Luukdegram/wasm-ctor

wasm-linker: implement linking with WASI-libc

3 files changed, 309 insertions(+), 61 deletions(-)

src/link.zig+1
......@@ -716,6 +716,7 @@ pub const File = struct {
716716 InvalidFeatureSet,
717717 InvalidFormat,
718718 InvalidIndex,
719 InvalidInitFunc,
719720 InvalidMagicByte,
720721 InvalidWasmVersion,
721722 LLDCrashed,
src/link/Wasm.zig+307-61
......@@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
118118tables: std.ArrayListUnmanaged(std.wasm.Table) = .{},
119119/// Output export section
120120exports: 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.
123init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},
121124
122125/// Indirect function table, used to call function pointers
123126/// When this is non-zero, we must emit a table entry,
......@@ -238,6 +241,34 @@ pub const SymbolLoc = struct {
238241 }
239242};
240243
244// Contains the location of the function symbol, as well as
245/// the priority itself of the initialization function.
246pub 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};
241272/// Generic string table that duplicates strings
242273/// and converts them into offsets instead.
243274pub const StringTable = struct {
......@@ -393,6 +424,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
393424 }
394425 }
395426
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
396437 if (!options.strip and options.module != null) {
397438 wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
398439 try wasm_bin.initDebugSections();
......@@ -434,7 +475,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
434475 .index = undefined,
435476 });
436477 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);
438479 return loc;
439480}
440481/// Initializes symbols and atoms for the debug sections
......@@ -600,27 +641,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
600641 }
601642
602643 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 };
611654
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 }
623667 }
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;
624672 }
625673
626674 if (existing_sym.tag == .global) {
......@@ -646,8 +694,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
646694 }
647695 }
648696
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;
651701 try wasm.discarded.put(wasm.base.allocator, location, existing_loc);
652702 continue;
653703 }
......@@ -801,6 +851,51 @@ fn validateFeatures(
801851 to_emit.* = allowed;
802852}
803853
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.
858fn 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.
894fn 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
804899fn checkUndefinedSymbols(wasm: *const Wasm) !void {
805900 if (wasm.base.options.output_mode == .Obj) return;
806901 if (wasm.base.options.import_symbols) return;
......@@ -813,12 +908,8 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
813908 const file_name = if (undef.file) |file_index| name: {
814909 break :name wasm.objects.items[file_index].name;
815910 } 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});
822913 log.err(" defined in '{s}'", .{file_name});
823914 }
824915 }
......@@ -885,6 +976,7 @@ pub fn deinit(wasm: *Wasm) void {
885976 wasm.wasm_globals.deinit(gpa);
886977 wasm.function_table.deinit(gpa);
887978 wasm.tables.deinit(gpa);
979 wasm.init_funcs.deinit(gpa);
888980 wasm.exports.deinit(gpa);
889981
890982 wasm.string_table.deinit(gpa);
......@@ -1405,14 +1497,13 @@ fn mapFunctionTable(wasm: *Wasm) void {
14051497 }
14061498
14071499 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").?;
14091501 const import = wasm.imports.getPtr(sym_loc).?;
14101502 import.kind.table.limits.min = index - 1; // we start at index 1.
14111503 } else if (index > 1) {
14121504 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);
14161507 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
14171508 table.limits = .{ .min = index, .max = index };
14181509 }
......@@ -1491,6 +1582,7 @@ const Kind = union(enum) {
14911582 read_only,
14921583 uninitialized,
14931584 initialized,
1585 synthetic,
14941586 },
14951587 function: FnData,
14961588
......@@ -1501,6 +1593,7 @@ const Kind = union(enum) {
15011593 .read_only => return ".rodata.",
15021594 .uninitialized => return ".bss.",
15031595 .initialized => return ".data.",
1596 .synthetic => return ".synthetic",
15041597 }
15051598 }
15061599};
......@@ -1637,9 +1730,14 @@ fn allocateAtoms(wasm: *Wasm) !void {
16371730 var offset: u32 = 0;
16381731 while (true) {
16391732 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 }
16431741 }
16441742 offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);
16451743 atom.offset = offset;
......@@ -1674,6 +1772,7 @@ fn sortDataSegments(wasm: *Wasm) !void {
16741772 if (mem.startsWith(u8, name, ".rodata")) return 0;
16751773 if (mem.startsWith(u8, name, ".data")) return 1;
16761774 if (mem.startsWith(u8, name, ".text")) return 2;
1775 if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end
16771776 return 3;
16781777 }
16791778 };
......@@ -1687,6 +1786,125 @@ fn sortDataSegments(wasm: *Wasm) !void {
16871786 wasm.data_segments = new_mapping;
16881787}
16891788
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.
1797fn 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.
1833fn 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
16901908fn setupImports(wasm: *Wasm) !void {
16911909 log.debug("Merging imports", .{});
16921910 var discarded_it = wasm.discarded.keyIterator();
......@@ -1859,16 +2077,12 @@ fn setupExports(wasm: *Wasm) !void {
18592077
18602078 const force_exp_names = wasm.base.options.export_symbol_names;
18612079 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;
18642081
18652082 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;
18722086 continue;
18732087 };
18742088
......@@ -1876,10 +2090,7 @@ fn setupExports(wasm: *Wasm) !void {
18762090 symbol.setFlag(.WASM_SYM_EXPORTED);
18772091 }
18782092
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) {
18832094 return error.MissingSymbol;
18842095 }
18852096 }
......@@ -1925,7 +2136,7 @@ fn setupExports(wasm: *Wasm) !void {
19252136fn setupStart(wasm: *Wasm) !void {
19262137 const entry_name = wasm.base.options.entry orelse "_start";
19272138
1928 const symbol_name_offset = wasm.string_table.getOffset(entry_name) orelse {
2139 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
19292140 if (wasm.base.options.output_mode == .Exe) {
19302141 if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors
19312142 } else {
......@@ -1935,7 +2146,6 @@ fn setupStart(wasm: *Wasm) !void {
19352146 return error.MissingSymbol;
19362147 };
19372148
1938 const symbol_loc = wasm.globals.get(symbol_name_offset).?;
19392149 const symbol = symbol_loc.getSymbol(wasm);
19402150 if (symbol.tag != .function) {
19412151 log.err("Entry symbol '{s}' is not a function", .{entry_name});
......@@ -1955,6 +2165,8 @@ fn setupMemory(wasm: *Wasm) !void {
19552165 // Use the user-provided stack size or else we use 1MB by default
19562166 const stack_size = wasm.base.options.stack_size_override orelse page_size * 16;
19572167 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
19582170 // Always place the stack at the start by default
19592171 // unless the user specified the global-base flag
19602172 var place_stack_first = true;
......@@ -1973,8 +2185,13 @@ fn setupMemory(wasm: *Wasm) !void {
19732185 }
19742186
19752187 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.*];
19782195 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
19792196 memory_ptr += segment.size;
19802197 segment.offset = offset;
......@@ -1987,6 +2204,16 @@ fn setupMemory(wasm: *Wasm) !void {
19872204 wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr));
19882205 }
19892206
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
19902217 // Setup the max amount of pages
19912218 // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1
19922219 const max_memory_allowed: u64 = (1 << 32) - 1;
......@@ -2006,12 +2233,20 @@ fn setupMemory(wasm: *Wasm) !void {
20062233 }
20072234 memory_ptr = initial_memory;
20082235 }
2009
2236 memory_ptr = mem.alignForwardGeneric(u64, memory_ptr, std.wasm.page_size);
20102237 // In case we do not import memory, but define it ourselves,
20112238 // 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);
20132240 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
20142241
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
20152250 if (wasm.base.options.max_memory) |max_memory| {
20162251 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
20172252 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
24882723 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
24892724 try wasm.validateFeatures(&enabled_features, &emit_features_count);
24902725 try wasm.resolveSymbolsInArchives();
2726 try wasm.resolveLazySymbols();
24912727 try wasm.checkUndefinedSymbols();
24922728
2729 try wasm.setupInitFunctions();
24932730 try wasm.setupStart();
24942731 try wasm.setupImports();
24952732
......@@ -2502,6 +2739,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
25022739 wasm.mapFunctionTable();
25032740 try wasm.mergeSections();
25042741 try wasm.mergeTypes();
2742 try wasm.initializeCallCtorsFunction();
25052743 try wasm.setupExports();
25062744 try wasm.writeToFile(enabled_features, emit_features_count, arena);
25072745
......@@ -2569,11 +2807,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25692807 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
25702808 try wasm.validateFeatures(&enabled_features, &emit_features_count);
25712809 try wasm.resolveSymbolsInArchives();
2810 try wasm.resolveLazySymbols();
25722811 try wasm.checkUndefinedSymbols();
25732812
25742813 // When we finish/error we reset the state of the linker
25752814 // So we can rebuild the binary file on each incremental update
25762815 defer wasm.resetState();
2816 try wasm.setupInitFunctions();
25772817 try wasm.setupStart();
25782818 try wasm.setupImports();
25792819 if (wasm.base.options.module) |mod| {
......@@ -2616,6 +2856,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
26162856 wasm.mapFunctionTable();
26172857 try wasm.mergeSections();
26182858 try wasm.mergeTypes();
2859 try wasm.initializeCallCtorsFunction();
26192860 try wasm.setupExports();
26202861 try wasm.writeToFile(enabled_features, emit_features_count, arena);
26212862}
......@@ -2810,7 +3051,7 @@ fn writeToFile(
28103051 if (wasm.function_table.count() > 0) {
28113052 const header_offset = try reserveVecSectionHeader(&binary_bytes);
28123053
2813 const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?;
3054 const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
28143055 const table_sym = table_loc.getSymbol(wasm);
28153056
28163057 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(
28493090 defer sorted_atoms.deinit();
28503091
28513092 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);
28543098 }
2855 sorted_atoms.appendAssumeCapacity(atom);
28563099 atom = atom.next orelse break;
28573100 }
28583101
......@@ -2893,10 +3136,11 @@ fn writeToFile(
28933136 // do not output 'bss' section unless we import memory and therefore
28943137 // want to guarantee the data is zero initialized
28953138 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
2896 segment_count += 1;
28973139 const atom_index = entry.value_ptr.*;
2898 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();
28993140 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();
29003144
29013145 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
29023146 try leb.writeULEB128(binary_writer, @as(u32, 0));
......@@ -3166,6 +3410,8 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
31663410 // bss section is not emitted when this condition holds true, so we also
31673411 // do not output a name for it.
31683412 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;
31693415 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
31703416 data_segment_index += 1;
31713417 }
......@@ -3896,8 +4142,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
38964142 return null;
38974143}
38984144
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`.
39014147pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
39024148 if (wasm.getTypeIndex(func_type)) |index| {
39034149 return index;
src/link/Wasm/types.zig+1
......@@ -129,6 +129,7 @@ pub const Segment = struct {
129129 /// file or binary. When `merge_segments` is true, this will return the
130130 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
131131 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {
132 if (std.mem.startsWith(u8, self.name, ".synthetic")) return ".synthetic"; // always merge
132133 if (!merge_segments) return self.name;
133134 if (std.mem.startsWith(u8, self.name, ".rodata.")) {
134135 return ".rodata";