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
signaturelock-open 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
468468 .flags = 0,
469469 .tag = tag,
470470 .index = undefined,
471 .virtual_address = undefined,
471472 });
472473 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});
473474 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
......@@ -1011,6 +1012,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {
10111012 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
10121013 .tag = undefined, // will be set after updateDecl
10131014 .index = undefined, // will be set after updateDecl
1015 .virtual_address = undefined, // will be set during atom allocation
10141016 };
10151017 if (wasm.symbols_free_list.popOrNull()) |index| {
10161018 wasm.symbols.items[index] = symbol;
......@@ -1246,6 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
12461248 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
12471249 .tag = .data,
12481250 .index = undefined,
1251 .virtual_address = undefined,
12491252 };
12501253 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
12511254
......@@ -1292,6 +1295,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {
12921295 .flags = 0,
12931296 .index = undefined, // index to type will be set after merging function symbols
12941297 .tag = .function,
1298 .virtual_address = undefined,
12951299 };
12961300 symbol.setGlobal(true);
12971301 symbol.setUndefined(true);
......@@ -1788,6 +1792,30 @@ fn allocateAtoms(wasm: *Wasm) !void {
17881792 }
17891793}
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
17911819fn sortDataSegments(wasm: *Wasm) !void {
17921820 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
17931821 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());
......@@ -2137,13 +2165,10 @@ fn setupExports(wasm: *Wasm) !void {
21372165 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
21382166 };
21392167 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);
21432168 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
21442169 try wasm.wasm_globals.append(wasm.base.allocator, .{
21452170 .global_type = .{ .valtype = .i32, .mutable = false },
2146 .init = .{ .i32_const = @intCast(i32, va) },
2171 .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) },
21472172 });
21482173 break :exp .{
21492174 .name = export_name,
......@@ -2240,12 +2265,8 @@ fn setupMemory(wasm: *Wasm) !void {
22402265 // One of the linked object files has a reference to the __heap_base symbol.
22412266 // We must set its virtual address so it can be used in relocations.
22422267 if (wasm.findGlobalSymbol("__heap_base")) |loc| {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;
2244 const segment = &wasm.segments.items[segment_index];
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));
2268 const symbol = loc.getSymbol(wasm);
2269 symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
22492270 }
22502271
22512272 // Setup the max amount of pages
......@@ -2274,12 +2295,8 @@ fn setupMemory(wasm: *Wasm) !void {
22742295 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
22752296
22762297 if (wasm.findGlobalSymbol("__heap_end")) |loc| {
2277 const segment_index = wasm.data_segments.get(".synthetic").?;
2278 const segment = &wasm.segments.items[segment_index];
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);
2298 const symbol = loc.getSymbol(wasm);
2299 symbol.virtual_address = @intCast(u32, memory_ptr);
22832300 }
22842301
22852302 if (wasm.base.options.max_memory) |max_memory| {
......@@ -2417,6 +2434,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
24172434 .tag = .data,
24182435 .flags = 0,
24192436 .index = 0,
2437 .virtual_address = undefined,
24202438 };
24212439 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24222440
......@@ -2449,6 +2467,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24492467 .tag = .data,
24502468 .flags = 0,
24512469 .index = 0,
2470 .virtual_address = undefined,
24522471 };
24532472 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24542473
......@@ -2748,6 +2767,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
27482767
27492768 try wasm.allocateAtoms();
27502769 try wasm.setupMemory();
2770 wasm.allocateVirtualAddresses();
27512771 wasm.mapFunctionTable();
27522772 try wasm.mergeSections();
27532773 try wasm.mergeTypes();
......@@ -2866,6 +2886,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28662886
28672887 try wasm.allocateAtoms();
28682888 try wasm.setupMemory();
2889 wasm.allocateVirtualAddresses();
28692890 wasm.mapFunctionTable();
28702891 try wasm.mergeSections();
28712892 try wasm.mergeTypes();
src/link/Wasm/Atom.zig+1-23
......@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {
8989 return atom.sym_index;
9090}
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
10792/// Resolves the relocations within the atom, writing the new value
10893/// at the calculated offset.
10994pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
......@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
186171 if (symbol.isUndefined()) {
187172 return 0;
188173 }
189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
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));
174 const va = @intCast(i64, symbol.virtual_address);
197175 return @intCast(u32, va + relocation.addend);
198176 },
199177 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
src/link/Wasm/Object.zig+2
......@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
270270 .name = table_import.name,
271271 .tag = .table,
272272 .index = 0,
273 .virtual_address = undefined,
273274 };
274275 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
275276 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
......@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {
758759 .tag = tag,
759760 .name = undefined,
760761 .index = undefined,
762 .virtual_address = undefined,
761763 };
762764
763765 switch (tag) {
src/link/Wasm/Symbol.zig+3
......@@ -20,6 +20,9 @@ name: u32,
2020index: u32,
2121/// Represents the kind of the symbol, such as a function or global.
2222tag: 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
2427pub const Tag = enum {
2528 function,