authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-09 19:14:17+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-09 19:14:17+01:00
log87738cad8607a31537a5c16826ab315990bc73c6
tree8d0bd79d5f16fb1cb3fd0fb8e909369576ed4948
parent27701596060c7a8018f9a9add21952a6d7f83d96
signature Commit is signed but in an unrecognized format.

wasm-linker: store symbol's virtual address

For data symbols we will now store its virtual address. This means we do no longer have to calculate it each time a relocation asks for the address. This is now done for all data symbols only once rather than every single relocation for that symbol. This now also allows us directly store the virtual address of synthetic symbols without having to create an atom for them. This means we also don't need to have a "synthetic" segment any longer and do not emit the synthetic symbols such as __heap_end and __heap_base into the final binary.

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

src/link/Wasm.zig+37-16
...@@ -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);
...@@ -1011,6 +1012,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {...@@ -1011,6 +1012,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {
1011 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1012 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1012 .tag = undefined, // will be set after updateDecl1013 .tag = undefined, // will be set after updateDecl
1013 .index = undefined, // will be set after updateDecl1014 .index = undefined, // will be set after updateDecl
1015 .virtual_address = undefined, // will be set during atom allocation
1014 };1016 };
1015 if (wasm.symbols_free_list.popOrNull()) |index| {1017 if (wasm.symbols_free_list.popOrNull()) |index| {
1016 wasm.symbols.items[index] = symbol;1018 wasm.symbols.items[index] = symbol;
...@@ -1246,6 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1246,6 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1246 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1248 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1247 .tag = .data,1249 .tag = .data,
1248 .index = undefined,1250 .index = undefined,
1251 .virtual_address = undefined,
1249 };1252 };
1250 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});1253 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
12511254
...@@ -1292,6 +1295,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {...@@ -1292,6 +1295,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {
1292 .flags = 0,1295 .flags = 0,
1293 .index = undefined, // index to type will be set after merging function symbols1296 .index = undefined, // index to type will be set after merging function symbols
1294 .tag = .function,1297 .tag = .function,
1298 .virtual_address = undefined,
1295 };1299 };
1296 symbol.setGlobal(true);1300 symbol.setGlobal(true);
1297 symbol.setUndefined(true);1301 symbol.setUndefined(true);
...@@ -1788,6 +1792,30 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1788,6 +1792,30 @@ fn allocateAtoms(wasm: *Wasm) !void {
1788 }1792 }
1789}1793}
17901794
1795/// For each data symbol, sets the virtual address.
1796fn allocateVirtualAddresses(wasm: *Wasm) void {
1797 for (wasm.resolved_symbols.keys()) |loc| {
1798 const symbol = loc.getSymbol(wasm);
1799 if (symbol.tag != .data) {
1800 continue; // only data symbols have virtual addresses
1801 }
1802 const atom_index = wasm.symbol_atom.get(loc) orelse {
1803 // synthetic symbol that does not contain an atom
1804 continue;
1805 };
1806
1807 const atom = wasm.getAtom(atom_index);
1808 const merge_segment = wasm.base.options.output_mode != .Obj;
1809 const segment_info = if (atom.file) |object_index| blk: {
1810 break :blk wasm.objects.items[object_index].segment_info;
1811 } else wasm.segment_info.values();
1812 const segment_name = segment_info[symbol.index].outputName(merge_segment);
1813 const segment_index = wasm.data_segments.get(segment_name).?;
1814 const segment = wasm.segments.items[segment_index];
1815 symbol.virtual_address = atom.offset + segment.offset;
1816 }
1817}
1818
1791fn sortDataSegments(wasm: *Wasm) !void {1819fn sortDataSegments(wasm: *Wasm) !void {
1792 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};1820 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
1793 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());1821 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());
...@@ -2137,13 +2165,10 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2137,13 +2165,10 @@ fn setupExports(wasm: *Wasm) !void {
2137 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);2165 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
2138 };2166 };
2139 const exp: types.Export = if (symbol.tag == .data) exp: {2167 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);2168 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
2144 try wasm.wasm_globals.append(wasm.base.allocator, .{2169 try wasm.wasm_globals.append(wasm.base.allocator, .{
2145 .global_type = .{ .valtype = .i32, .mutable = false },2170 .global_type = .{ .valtype = .i32, .mutable = false },
2146 .init = .{ .i32_const = @intCast(i32, va) },2171 .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) },
2147 });2172 });
2148 break :exp .{2173 break :exp .{
2149 .name = export_name,2174 .name = export_name,
...@@ -2240,12 +2265,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2240,12 +2265,8 @@ fn setupMemory(wasm: *Wasm) !void {
2240 // One of the linked object files has a reference to the __heap_base symbol.2265 // 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.2266 // We must set its virtual address so it can be used in relocations.
2242 if (wasm.findGlobalSymbol("__heap_base")) |loc| {2267 if (wasm.findGlobalSymbol("__heap_base")) |loc| {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;2268 const symbol = loc.getSymbol(wasm);
2244 const segment = &wasm.segments.items[segment_index];2269 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 }2270 }
22502271
2251 // Setup the max amount of pages2272 // Setup the max amount of pages
...@@ -2274,12 +2295,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2274,12 +2295,8 @@ fn setupMemory(wasm: *Wasm) !void {
2274 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});2295 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
22752296
2276 if (wasm.findGlobalSymbol("__heap_end")) |loc| {2297 if (wasm.findGlobalSymbol("__heap_end")) |loc| {
2277 const segment_index = wasm.data_segments.get(".synthetic").?;2298 const symbol = loc.getSymbol(wasm);
2278 const segment = &wasm.segments.items[segment_index];2299 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 }2300 }
22842301
2285 if (wasm.base.options.max_memory) |max_memory| {2302 if (wasm.base.options.max_memory) |max_memory| {
...@@ -2417,6 +2434,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2417,6 +2434,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2417 .tag = .data,2434 .tag = .data,
2418 .flags = 0,2435 .flags = 0,
2419 .index = 0,2436 .index = 0,
2437 .virtual_address = undefined,
2420 };2438 };
2421 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2439 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24222440
...@@ -2449,6 +2467,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2449,6 +2467,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
2449 .tag = .data,2467 .tag = .data,
2450 .flags = 0,2468 .flags = 0,
2451 .index = 0,2469 .index = 0,
2470 .virtual_address = undefined,
2452 };2471 };
2453 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2472 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24542473
...@@ -2748,6 +2767,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -2748,6 +2767,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
27482767
2749 try wasm.allocateAtoms();2768 try wasm.allocateAtoms();
2750 try wasm.setupMemory();2769 try wasm.setupMemory();
2770 wasm.allocateVirtualAddresses();
2751 wasm.mapFunctionTable();2771 wasm.mapFunctionTable();
2752 try wasm.mergeSections();2772 try wasm.mergeSections();
2753 try wasm.mergeTypes();2773 try wasm.mergeTypes();
...@@ -2866,6 +2886,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2866,6 +2886,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28662886
2867 try wasm.allocateAtoms();2887 try wasm.allocateAtoms();
2868 try wasm.setupMemory();2888 try wasm.setupMemory();
2889 wasm.allocateVirtualAddresses();
2869 wasm.mapFunctionTable();2890 wasm.mapFunctionTable();
2870 try wasm.mergeSections();2891 try wasm.mergeSections();
2871 try wasm.mergeTypes();2892 try wasm.mergeTypes();
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,