| ... | ... | @@ -475,7 +475,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 475 | 475 | .index = undefined, |
| 476 | 476 | }); |
| 477 | 477 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); |
| 478 | | try wasm.globals.putNoClobber(wasm.base.allocator, name_offset, loc); |
| 478 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| 479 | 479 | return loc; |
| 480 | 480 | } |
| 481 | 481 | /// Initializes symbols and atoms for the debug sections |
| ... | ... | @@ -851,6 +851,35 @@ fn validateFeatures( |
| 851 | 851 | to_emit.* = allowed; |
| 852 | 852 | } |
| 853 | 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 | |
| 876 | // Tries to find a global symbol by its name. Returns null when not found, |
| 877 | /// and its location when it is found. |
| 878 | fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { |
| 879 | const offset = wasm.string_table.getOffset(name) orelse return null; |
| 880 | return wasm.globals.get(offset); |
| 881 | } |
| 882 | |
| 854 | 883 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 855 | 884 | if (wasm.base.options.output_mode == .Obj) return; |
| 856 | 885 | if (wasm.base.options.import_symbols) return; |
| ... | ... | @@ -1458,14 +1487,13 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1458 | 1487 | } |
| 1459 | 1488 | |
| 1460 | 1489 | if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { |
| 1461 | | const sym_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 1490 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1462 | 1491 | const import = wasm.imports.getPtr(sym_loc).?; |
| 1463 | 1492 | import.kind.table.limits.min = index - 1; // we start at index 1. |
| 1464 | 1493 | } else if (index > 1) { |
| 1465 | 1494 | log.debug("Appending indirect function table", .{}); |
| 1466 | | const offset = wasm.string_table.getOffset("__indirect_function_table").?; |
| 1467 | | const sym_with_loc = wasm.globals.get(offset).?; |
| 1468 | | const symbol = sym_with_loc.getSymbol(wasm); |
| 1495 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1496 | const symbol = sym_loc.getSymbol(wasm); |
| 1469 | 1497 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; |
| 1470 | 1498 | table.limits = .{ .min = index, .max = index }; |
| 1471 | 1499 | } |
| ... | ... | @@ -1544,6 +1572,7 @@ const Kind = union(enum) { |
| 1544 | 1572 | read_only, |
| 1545 | 1573 | uninitialized, |
| 1546 | 1574 | initialized, |
| 1575 | synthetic, |
| 1547 | 1576 | }, |
| 1548 | 1577 | function: FnData, |
| 1549 | 1578 | |
| ... | ... | @@ -1554,6 +1583,7 @@ const Kind = union(enum) { |
| 1554 | 1583 | .read_only => return ".rodata.", |
| 1555 | 1584 | .uninitialized => return ".bss.", |
| 1556 | 1585 | .initialized => return ".data.", |
| 1586 | .synthetic => return ".synthetic", |
| 1557 | 1587 | } |
| 1558 | 1588 | } |
| 1559 | 1589 | }; |
| ... | ... | @@ -1690,9 +1720,14 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1690 | 1720 | var offset: u32 = 0; |
| 1691 | 1721 | while (true) { |
| 1692 | 1722 | const symbol_loc = atom.symbolLoc(); |
| 1693 | | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1694 | | atom = atom.next orelse break; |
| 1695 | | continue; |
| 1723 | if (wasm.code_section_index) |index| { |
| 1724 | if (index == entry.key_ptr.*) { |
| 1725 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1726 | // only allocate resolved function body's. |
| 1727 | atom = atom.next orelse break; |
| 1728 | continue; |
| 1729 | } |
| 1730 | } |
| 1696 | 1731 | } |
| 1697 | 1732 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); |
| 1698 | 1733 | atom.offset = offset; |
| ... | ... | @@ -1727,6 +1762,7 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1727 | 1762 | if (mem.startsWith(u8, name, ".rodata")) return 0; |
| 1728 | 1763 | if (mem.startsWith(u8, name, ".data")) return 1; |
| 1729 | 1764 | if (mem.startsWith(u8, name, ".text")) return 2; |
| 1765 | if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end |
| 1730 | 1766 | return 3; |
| 1731 | 1767 | } |
| 1732 | 1768 | }; |
| ... | ... | @@ -1789,7 +1825,7 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1789 | 1825 | if (wasm.code_section_index == null) { |
| 1790 | 1826 | // Make sure to remove it from the resolved symbols so we do not emit |
| 1791 | 1827 | // 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").?).?; |
| 1828 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| 1793 | 1829 | std.debug.assert(wasm.resolved_symbols.swapRemove(loc)); |
| 1794 | 1830 | return; |
| 1795 | 1831 | } |
| ... | ... | @@ -1806,11 +1842,6 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1806 | 1842 | // call constructors |
| 1807 | 1843 | for (wasm.init_funcs.items) |init_func_loc| { |
| 1808 | 1844 | 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 | 1845 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count]; |
| 1815 | 1846 | const ty = wasm.func_types.items[func.type_index]; |
| 1816 | 1847 | |
| ... | ... | @@ -1828,7 +1859,7 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1828 | 1859 | try writer.writeByte(std.wasm.opcode(.end)); |
| 1829 | 1860 | } |
| 1830 | 1861 | |
| 1831 | | const loc = wasm.globals.get(wasm.string_table.getOffset("__wasm_call_ctors").?).?; |
| 1862 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| 1832 | 1863 | const symbol = loc.getSymbol(wasm); |
| 1833 | 1864 | // create type (() -> nil) as we do not have any parameters or return value. |
| 1834 | 1865 | const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} }); |
| ... | ... | @@ -2039,12 +2070,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 2039 | 2070 | var failed_exports = false; |
| 2040 | 2071 | |
| 2041 | 2072 | for (force_exp_names) |exp_name| { |
| 2042 | | const name_index = wasm.string_table.getOffset(exp_name) orelse { |
| 2043 | | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 2044 | | failed_exports = true; |
| 2045 | | continue; |
| 2046 | | }; |
| 2047 | | const loc = wasm.globals.get(name_index) orelse { |
| 2073 | const loc = wasm.findGlobalSymbol(exp_name) orelse { |
| 2048 | 2074 | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 2049 | 2075 | failed_exports = true; |
| 2050 | 2076 | continue; |
| ... | ... | @@ -2100,7 +2126,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 2100 | 2126 | fn setupStart(wasm: *Wasm) !void { |
| 2101 | 2127 | const entry_name = wasm.base.options.entry orelse "_start"; |
| 2102 | 2128 | |
| 2103 | | const symbol_name_offset = wasm.string_table.getOffset(entry_name) orelse { |
| 2129 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 2104 | 2130 | if (wasm.base.options.output_mode == .Exe) { |
| 2105 | 2131 | if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors |
| 2106 | 2132 | } else { |
| ... | ... | @@ -2110,10 +2136,6 @@ fn setupStart(wasm: *Wasm) !void { |
| 2110 | 2136 | return error.MissingSymbol; |
| 2111 | 2137 | }; |
| 2112 | 2138 | |
| 2113 | | const symbol_loc = wasm.globals.get(symbol_name_offset) orelse { |
| 2114 | | log.err("Entry symbol '{s}' not found", .{entry_name}); |
| 2115 | | return error.MissingSymbol; |
| 2116 | | }; |
| 2117 | 2139 | const symbol = symbol_loc.getSymbol(wasm); |
| 2118 | 2140 | if (symbol.tag != .function) { |
| 2119 | 2141 | log.err("Entry symbol '{s}' is not a function", .{entry_name}); |
| ... | ... | @@ -2133,6 +2155,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2133 | 2155 | // Use the user-provided stack size or else we use 1MB by default |
| 2134 | 2156 | const stack_size = wasm.base.options.stack_size_override orelse page_size * 16; |
| 2135 | 2157 | const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention |
| 2158 | const heap_alignment = 16; // wasm's heap alignment as specified by tool-convention |
| 2159 | |
| 2136 | 2160 | // Always place the stack at the start by default |
| 2137 | 2161 | // unless the user specified the global-base flag |
| 2138 | 2162 | var place_stack_first = true; |
| ... | ... | @@ -2151,8 +2175,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2151 | 2175 | } |
| 2152 | 2176 | |
| 2153 | 2177 | var offset: u32 = @intCast(u32, memory_ptr); |
| 2154 | | for (wasm.data_segments.values()) |segment_index| { |
| 2155 | | const segment = &wasm.segments.items[segment_index]; |
| 2178 | var data_seg_it = wasm.data_segments.iterator(); |
| 2179 | while (data_seg_it.next()) |entry| { |
| 2180 | if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) { |
| 2181 | // do not update synthetic segments as they are not part of the output |
| 2182 | continue; |
| 2183 | } |
| 2184 | const segment = &wasm.segments.items[entry.value_ptr.*]; |
| 2156 | 2185 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); |
| 2157 | 2186 | memory_ptr += segment.size; |
| 2158 | 2187 | segment.offset = offset; |
| ... | ... | @@ -2165,6 +2194,16 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2165 | 2194 | wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); |
| 2166 | 2195 | } |
| 2167 | 2196 | |
| 2197 | // One of the linked object files has a reference to the __heap_base symbol. |
| 2198 | // We must set its virtual address so it can be used in relocations. |
| 2199 | if (wasm.findGlobalSymbol("__heap_base")) |loc| { |
| 2200 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2201 | const segment = &wasm.segments.items[segment_index]; |
| 2202 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. |
| 2203 | const atom = wasm.symbol_atom.get(loc).?; |
| 2204 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); |
| 2205 | } |
| 2206 | |
| 2168 | 2207 | // Setup the max amount of pages |
| 2169 | 2208 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 |
| 2170 | 2209 | const max_memory_allowed: u64 = (1 << 32) - 1; |
| ... | ... | @@ -2666,6 +2705,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2666 | 2705 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2667 | 2706 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2668 | 2707 | try wasm.resolveSymbolsInArchives(); |
| 2708 | try wasm.resolveLazySymbols(); |
| 2669 | 2709 | try wasm.checkUndefinedSymbols(); |
| 2670 | 2710 | |
| 2671 | 2711 | try wasm.setupInitFunctions(); |
| ... | ... | @@ -2749,6 +2789,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2749 | 2789 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2750 | 2790 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2751 | 2791 | try wasm.resolveSymbolsInArchives(); |
| 2792 | try wasm.resolveLazySymbols(); |
| 2752 | 2793 | try wasm.checkUndefinedSymbols(); |
| 2753 | 2794 | |
| 2754 | 2795 | // When we finish/error we reset the state of the linker |
| ... | ... | @@ -2992,7 +3033,7 @@ fn writeToFile( |
| 2992 | 3033 | if (wasm.function_table.count() > 0) { |
| 2993 | 3034 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2994 | 3035 | |
| 2995 | | const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; |
| 3036 | const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 2996 | 3037 | const table_sym = table_loc.getSymbol(wasm); |
| 2997 | 3038 | |
| 2998 | 3039 | var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually |
| ... | ... | @@ -3031,10 +3072,12 @@ fn writeToFile( |
| 3031 | 3072 | defer sorted_atoms.deinit(); |
| 3032 | 3073 | |
| 3033 | 3074 | while (true) { |
| 3034 | | if (!is_obj) { |
| 3035 | | atom.resolveRelocs(wasm); |
| 3075 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { |
| 3076 | if (!is_obj) { |
| 3077 | atom.resolveRelocs(wasm); |
| 3078 | } |
| 3079 | sorted_atoms.appendAssumeCapacity(atom); |
| 3036 | 3080 | } |
| 3037 | | sorted_atoms.appendAssumeCapacity(atom); |
| 3038 | 3081 | atom = atom.next orelse break; |
| 3039 | 3082 | } |
| 3040 | 3083 | |
| ... | ... | @@ -3075,10 +3118,11 @@ fn writeToFile( |
| 3075 | 3118 | // do not output 'bss' section unless we import memory and therefore |
| 3076 | 3119 | // want to guarantee the data is zero initialized |
| 3077 | 3120 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 3078 | | segment_count += 1; |
| 3079 | 3121 | const atom_index = entry.value_ptr.*; |
| 3080 | | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); |
| 3081 | 3122 | const segment = wasm.segments.items[atom_index]; |
| 3123 | if (segment.size == 0) continue; // do not emit empty segments |
| 3124 | segment_count += 1; |
| 3125 | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); |
| 3082 | 3126 | |
| 3083 | 3127 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) |
| 3084 | 3128 | try leb.writeULEB128(binary_writer, @as(u32, 0)); |