authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-10 16:48:30+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-10 16:48:30+01:00
log904f414e7eab7bc0f7ea00f616831bfc3c1f18a4
tree782a94bcd8bb6e0ea95540cdd38e1ae661c84d20
parent5a26d1b4268b2e4598e0c39d3703d184921cfa6d
parent0ee9a52507fe30983f7933cb19a5bfde3b40a60c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14869 from Luukdegram/wasm-linker

wasm-linker: refactor virtual addresses

4 files changed, 43 insertions(+), 68 deletions(-)

src/link/Wasm.zig+37-45
...@@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol...@@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
468 .flags = 0,468 .flags = 0,
469 .tag = tag,469 .tag = tag,
470 .index = undefined,470 .index = undefined,
471 .virtual_address = undefined,
471 });472 });
472 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});473 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});
473 try wasm.globals.put(wasm.base.allocator, name_offset, loc);474 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
...@@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
886 const loc = try wasm.createSyntheticSymbol("__heap_base", .data);887 const loc = try wasm.createSyntheticSymbol("__heap_base", .data);
887 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);888 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
888 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.889 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
889
890 // TODO: Can we use `createAtom` here while also re-using the symbol
891 // from `createSyntheticSymbol`.
892 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
893 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
894 atom.* = Atom.empty;
895 atom.sym_index = loc.index;
896 atom.alignment = 1;
897
898 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
899 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
900 }890 }
901891
902 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {892 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
903 const loc = try wasm.createSyntheticSymbol("__heap_end", .data);893 const loc = try wasm.createSyntheticSymbol("__heap_end", .data);
904 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
905 _ = wasm.resolved_symbols.swapRemove(loc);895 _ = wasm.resolved_symbols.swapRemove(loc);
906
907 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
908 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
909 atom.* = Atom.empty;
910 atom.sym_index = loc.index;
911 atom.alignment = 1;
912
913 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
914 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
915 }896 }
916}897}
917898
...@@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {...@@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {
1011 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),992 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1012 .tag = undefined, // will be set after updateDecl993 .tag = undefined, // will be set after updateDecl
1013 .index = undefined, // will be set after updateDecl994 .index = undefined, // will be set after updateDecl
995 .virtual_address = undefined, // will be set during atom allocation
1014 };996 };
1015 if (wasm.symbols_free_list.popOrNull()) |index| {997 if (wasm.symbols_free_list.popOrNull()) |index| {
1016 wasm.symbols.items[index] = symbol;998 wasm.symbols.items[index] = symbol;
...@@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1246 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1228 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1247 .tag = .data,1229 .tag = .data,
1248 .index = undefined,1230 .index = undefined,
1231 .virtual_address = undefined,
1249 };1232 };
1250 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});1233 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
12511234
...@@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {...@@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {
1292 .flags = 0,1275 .flags = 0,
1293 .index = undefined, // index to type will be set after merging function symbols1276 .index = undefined, // index to type will be set after merging function symbols
1294 .tag = .function,1277 .tag = .function,
1278 .virtual_address = undefined,
1295 };1279 };
1296 symbol.setGlobal(true);1280 symbol.setGlobal(true);
1297 symbol.setUndefined(true);1281 symbol.setUndefined(true);
...@@ -1610,7 +1594,6 @@ const Kind = union(enum) {...@@ -1610,7 +1594,6 @@ const Kind = union(enum) {
1610 read_only,1594 read_only,
1611 uninitialized,1595 uninitialized,
1612 initialized,1596 initialized,
1613 synthetic,
1614 },1597 },
1615 function: void,1598 function: void,
16161599
...@@ -1621,7 +1604,6 @@ const Kind = union(enum) {...@@ -1621,7 +1604,6 @@ const Kind = union(enum) {
1621 .read_only => return ".rodata.",1604 .read_only => return ".rodata.",
1622 .uninitialized => return ".bss.",1605 .uninitialized => return ".bss.",
1623 .initialized => return ".data.",1606 .initialized => return ".data.",
1624 .synthetic => return ".synthetic",
1625 }1607 }
1626 }1608 }
1627};1609};
...@@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void {
1788 }1770 }
1789}1771}
17901772
1773/// For each data symbol, sets the virtual address.
1774fn allocateVirtualAddresses(wasm: *Wasm) void {
1775 for (wasm.resolved_symbols.keys()) |loc| {
1776 const symbol = loc.getSymbol(wasm);
1777 if (symbol.tag != .data) {
1778 continue; // only data symbols have virtual addresses
1779 }
1780 const atom_index = wasm.symbol_atom.get(loc) orelse {
1781 // synthetic symbol that does not contain an atom
1782 continue;
1783 };
1784
1785 const atom = wasm.getAtom(atom_index);
1786 const merge_segment = wasm.base.options.output_mode != .Obj;
1787 const segment_info = if (atom.file) |object_index| blk: {
1788 break :blk wasm.objects.items[object_index].segment_info;
1789 } else wasm.segment_info.values();
1790 const segment_name = segment_info[symbol.index].outputName(merge_segment);
1791 const segment_index = wasm.data_segments.get(segment_name).?;
1792 const segment = wasm.segments.items[segment_index];
1793 symbol.virtual_address = atom.offset + segment.offset;
1794 }
1795}
1796
1791fn sortDataSegments(wasm: *Wasm) !void {1797fn sortDataSegments(wasm: *Wasm) !void {
1792 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};1798 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
1793 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());1799 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());
...@@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void {...@@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void {
1805 if (mem.startsWith(u8, name, ".rodata")) return 0;1811 if (mem.startsWith(u8, name, ".rodata")) return 0;
1806 if (mem.startsWith(u8, name, ".data")) return 1;1812 if (mem.startsWith(u8, name, ".data")) return 1;
1807 if (mem.startsWith(u8, name, ".text")) return 2;1813 if (mem.startsWith(u8, name, ".text")) return 2;
1808 if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end
1809 return 3;1814 return 3;
1810 }1815 }
1811 };1816 };
...@@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void {
2137 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);2142 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
2138 };2143 };
2139 const exp: types.Export = if (symbol.tag == .data) exp: {2144 const exp: types.Export = if (symbol.tag == .data) exp: {
2140 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2141 const atom = wasm.getAtom(atom_index);
2142 const va = atom.getVA(wasm, symbol);
2143 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);2145 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
2144 try wasm.wasm_globals.append(wasm.base.allocator, .{2146 try wasm.wasm_globals.append(wasm.base.allocator, .{
2145 .global_type = .{ .valtype = .i32, .mutable = false },2147 .global_type = .{ .valtype = .i32, .mutable = false },
2146 .init = .{ .i32_const = @intCast(i32, va) },2148 .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) },
2147 });2149 });
2148 break :exp .{2150 break :exp .{
2149 .name = export_name,2151 .name = export_name,
...@@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void {
2220 var offset: u32 = @intCast(u32, memory_ptr);2222 var offset: u32 = @intCast(u32, memory_ptr);
2221 var data_seg_it = wasm.data_segments.iterator();2223 var data_seg_it = wasm.data_segments.iterator();
2222 while (data_seg_it.next()) |entry| {2224 while (data_seg_it.next()) |entry| {
2223 if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) {
2224 // do not update synthetic segments as they are not part of the output
2225 continue;
2226 }
2227 const segment = &wasm.segments.items[entry.value_ptr.*];2225 const segment = &wasm.segments.items[entry.value_ptr.*];
2228 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);2226 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
2229 memory_ptr += segment.size;2227 memory_ptr += segment.size;
...@@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void {
2240 // One of the linked object files has a reference to the __heap_base symbol.2238 // One of the linked object files has a reference to the __heap_base symbol.
2241 // We must set its virtual address so it can be used in relocations.2239 // We must set its virtual address so it can be used in relocations.
2242 if (wasm.findGlobalSymbol("__heap_base")) |loc| {2240 if (wasm.findGlobalSymbol("__heap_base")) |loc| {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;2241 const symbol = loc.getSymbol(wasm);
2244 const segment = &wasm.segments.items[segment_index];2242 symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2245 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.
2246 const atom_index = wasm.symbol_atom.get(loc).?;
2247 const atom = wasm.getAtomPtr(atom_index);
2248 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2249 }2243 }
22502244
2251 // Setup the max amount of pages2245 // Setup the max amount of pages
...@@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void {
2274 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});2268 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
22752269
2276 if (wasm.findGlobalSymbol("__heap_end")) |loc| {2270 if (wasm.findGlobalSymbol("__heap_end")) |loc| {
2277 const segment_index = wasm.data_segments.get(".synthetic").?;2271 const symbol = loc.getSymbol(wasm);
2278 const segment = &wasm.segments.items[segment_index];2272 symbol.virtual_address = @intCast(u32, memory_ptr);
2279 segment.offset = 0;
2280 const atom_index = wasm.symbol_atom.get(loc).?;
2281 const atom = wasm.getAtomPtr(atom_index);
2282 atom.offset = @intCast(u32, memory_ptr);
2283 }2273 }
22842274
2285 if (wasm.base.options.max_memory) |max_memory| {2275 if (wasm.base.options.max_memory) |max_memory| {
...@@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2417 .tag = .data,2407 .tag = .data,
2418 .flags = 0,2408 .flags = 0,
2419 .index = 0,2409 .index = 0,
2410 .virtual_address = undefined,
2420 };2411 };
2421 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2412 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24222413
...@@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
2449 .tag = .data,2440 .tag = .data,
2450 .flags = 0,2441 .flags = 0,
2451 .index = 0,2442 .index = 0,
2443 .virtual_address = undefined,
2452 };2444 };
2453 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2445 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24542446
...@@ -2749,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -2749,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
27492741
2750 try wasm.allocateAtoms();2742 try wasm.allocateAtoms();
2751 try wasm.setupMemory();2743 try wasm.setupMemory();
2744 wasm.allocateVirtualAddresses();
2752 wasm.mapFunctionTable();2745 wasm.mapFunctionTable();
2753 try wasm.mergeSections();2746 try wasm.mergeSections();
2754 try wasm.mergeTypes();2747 try wasm.mergeTypes();
...@@ -2867,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2867,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28672860
2868 try wasm.allocateAtoms();2861 try wasm.allocateAtoms();
2869 try wasm.setupMemory();2862 try wasm.setupMemory();
2863 wasm.allocateVirtualAddresses();
2870 wasm.mapFunctionTable();2864 wasm.mapFunctionTable();
2871 try wasm.mergeSections();2865 try wasm.mergeSections();
2872 try wasm.mergeTypes();2866 try wasm.mergeTypes();
...@@ -3460,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem...@@ -3460,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
3460 // bss section is not emitted when this condition holds true, so we also3454 // bss section is not emitted when this condition holds true, so we also
3461 // do not output a name for it.3455 // do not output a name for it.
3462 if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;3456 if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;
3463 // Synthetic segments are not emitted
3464 if (std.mem.eql(u8, key, ".synthetic")) continue;
3465 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });3457 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
3466 data_segment_index += 1;3458 data_segment_index += 1;
3467 }3459 }
src/link/Wasm/Atom.zig+1-23
...@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {...@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {
89 return atom.sym_index;89 return atom.sym_index;
90}90}
9191
92/// Returns the virtual address of the `Atom`. This is the address starting
93/// from the first entry within a section.
94pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 {
95 if (symbol.tag == .function) return atom.offset;
96 std.debug.assert(symbol.tag == .data);
97 const merge_segment = wasm.base.options.output_mode != .Obj;
98 const segment_info = if (atom.file) |object_index| blk: {
99 break :blk wasm.objects.items[object_index].segment_info;
100 } else wasm.segment_info.values();
101 const segment_name = segment_info[symbol.index].outputName(merge_segment);
102 const segment_index = wasm.data_segments.get(segment_name).?;
103 const segment = wasm.segments.items[segment_index];
104 return segment.offset + atom.offset;
105}
106
107/// Resolves the relocations within the atom, writing the new value92/// Resolves the relocations within the atom, writing the new value
108/// at the calculated offset.93/// at the calculated offset.
109pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {94pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
...@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
186 if (symbol.isUndefined()) {171 if (symbol.isUndefined()) {
187 return 0;172 return 0;
188 }173 }
189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {174 const va = @intCast(i64, symbol.virtual_address);
190 // this can only occur during incremental-compilation when a relocation
191 // still points to a freed decl. It is fine to emit the value 0 here
192 // as no actual code will point towards it.
193 return 0;
194 };
195 const target_atom = wasm_bin.getAtom(target_atom_index);
196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
197 return @intCast(u32, va + relocation.addend);175 return @intCast(u32, va + relocation.addend);
198 },176 },
199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,177 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
src/link/Wasm/Object.zig+2
...@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {...@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
270 .name = table_import.name,270 .name = table_import.name,
271 .tag = .table,271 .tag = .table,
272 .index = 0,272 .index = 0,
273 .virtual_address = undefined,
273 };274 };
274 table_symbol.setFlag(.WASM_SYM_UNDEFINED);275 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
275 table_symbol.setFlag(.WASM_SYM_NO_STRIP);276 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
...@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {
758 .tag = tag,759 .tag = tag,
759 .name = undefined,760 .name = undefined,
760 .index = undefined,761 .index = undefined,
762 .virtual_address = undefined,
761 };763 };
762764
763 switch (tag) {765 switch (tag) {
src/link/Wasm/Symbol.zig+3
...@@ -20,6 +20,9 @@ name: u32,...@@ -20,6 +20,9 @@ name: u32,
20index: u32,20index: u32,
21/// Represents the kind of the symbol, such as a function or global.21/// Represents the kind of the symbol, such as a function or global.
22tag: Tag,22tag: Tag,
23/// Contains the virtual address of the symbol, relative to the start of its section.
24/// This differs from the offset of an `Atom` which is relative to the start of a segment.
25virtual_address: u32,
2326
24pub const Tag = enum {27pub const Tag = enum {
25 function,28 function,