authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-16 19:29:57+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-11-28 15:47:07+01:00
log8856ba75059f74a326d1f8d3af40a30c5a3ac1ed
tree35ceb0bd2015c4bf89dda04c8ffc2bc49633c3bf
parentc986c6c90a5746cb671177e486a77bd78a5946b0
signature Commit is signed but in an unrecognized format.

wasm-linker: parse symbols into atoms lazily

Rather than parsing every symbol into an atom, we now only parse them into an atom when such atom is marked. This means garbage-collected symbols will also not be parsed into atoms, and neither are discarded symbols which have been resolved by other symbols. (Such as multiple weak symbols). This also introduces a binary search for finding the start index into the list of relocations. This speeds up finding the corresponding relocations tremendously as they're ordered ascended by address. Lastly, we re-use the memory of atom's data as well as relocations instead of duplicating it. This means we half the memory usage of atom's data and relocations for linked object files. As we are aware of decls and synthetic atoms, we free the memory of those atoms indepedently of the atoms of object files to prevent double-frees.

3 files changed, 208 insertions(+), 200 deletions(-)

src/link/Wasm.zig+75-51
......@@ -1309,6 +1309,31 @@ pub fn deinit(wasm: *Wasm) void {
13091309 archive.deinit(gpa);
13101310 }
13111311
1312 // For decls and anon decls we free the memory of its atoms.
1313 // The memory of atoms parsed from object files is managed by
1314 // the object file itself, and therefore we can skip those.
1315 {
1316 var it = wasm.decls.valueIterator();
1317 while (it.next()) |atom_index_ptr| {
1318 const atom = wasm.getAtomPtr(atom_index_ptr.*);
1319 for (atom.locals.items) |local_index| {
1320 const local_atom = wasm.getAtomPtr(local_index);
1321 local_atom.deinit(gpa);
1322 }
1323 atom.deinit(gpa);
1324 }
1325 }
1326 {
1327 for (wasm.anon_decls.values()) |atom_index| {
1328 const atom = wasm.getAtomPtr(atom_index);
1329 for (atom.locals.items) |local_index| {
1330 const local_atom = wasm.getAtomPtr(local_index);
1331 local_atom.deinit(gpa);
1332 }
1333 atom.deinit(gpa);
1334 }
1335 }
1336
13121337 wasm.decls.deinit(gpa);
13131338 wasm.anon_decls.deinit(gpa);
13141339 wasm.atom_types.deinit(gpa);
......@@ -1321,9 +1346,6 @@ pub fn deinit(wasm: *Wasm) void {
13211346 wasm.symbol_atom.deinit(gpa);
13221347 wasm.export_names.deinit(gpa);
13231348 wasm.atoms.deinit(gpa);
1324 for (wasm.managed_atoms.items) |*managed_atom| {
1325 managed_atom.deinit(wasm);
1326 }
13271349 wasm.managed_atoms.deinit(gpa);
13281350 wasm.segments.deinit(gpa);
13291351 wasm.data_segments.deinit(gpa);
......@@ -1342,6 +1364,10 @@ pub fn deinit(wasm: *Wasm) void {
13421364 wasm.exports.deinit(gpa);
13431365
13441366 wasm.string_table.deinit(gpa);
1367 for (wasm.synthetic_functions.items) |atom_index| {
1368 const atom = wasm.getAtomPtr(atom_index);
1369 atom.deinit(gpa);
1370 }
13451371 wasm.synthetic_functions.deinit(gpa);
13461372
13471373 if (wasm.dwarf) |*dwarf| {
......@@ -2406,7 +2432,7 @@ fn setupErrorsLen(wasm: *Wasm) !void {
24062432 prev_atom.next = atom.next;
24072433 atom.prev = null;
24082434 }
2409 atom.deinit(wasm);
2435 atom.deinit(wasm.base.allocator);
24102436 break :blk index;
24112437 } else new_atom: {
24122438 const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
......@@ -2509,6 +2535,7 @@ fn createSyntheticFunction(
25092535 .next = null,
25102536 .prev = null,
25112537 .code = function_body.moveToUnmanaged(),
2538 .original_offset = 0,
25122539 };
25132540 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
25142541 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
......@@ -2545,6 +2572,7 @@ pub fn createFunction(
25452572 .prev = null,
25462573 .code = function_body.moveToUnmanaged(),
25472574 .relocs = relocations.moveToUnmanaged(),
2575 .original_offset = 0,
25482576 };
25492577 const symbol = loc.getSymbol(wasm);
25502578 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported
......@@ -3016,14 +3044,14 @@ fn setupMemory(wasm: *Wasm) !void {
30163044/// From a given object's index and the index of the segment, returns the corresponding
30173045/// index of the segment within the final data section. When the segment does not yet
30183046/// exist, a new one will be initialized and appended. The new index will be returned in that case.
3019pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32) !?u32 {
3047pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 {
30203048 const object: Object = wasm.objects.items[object_index];
3021 const relocatable_data = object.relocatable_data[relocatable_index];
3049 const symbol = object.symtable[symbol_index];
30223050 const index = @as(u32, @intCast(wasm.segments.items.len));
30233051
3024 switch (relocatable_data.type) {
3052 switch (symbol.tag) {
30253053 .data => {
3026 const segment_info = object.segment_info[relocatable_data.index];
3054 const segment_info = object.segment_info[symbol.index];
30273055 const merge_segment = wasm.base.options.output_mode != .Obj;
30283056 const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment));
30293057 if (!result.found_existing) {
......@@ -3041,67 +3069,67 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32
30413069 return index;
30423070 } else return result.value_ptr.*;
30433071 },
3044 .code => return wasm.code_section_index orelse blk: {
3072 .function => return wasm.code_section_index orelse blk: {
30453073 wasm.code_section_index = index;
30463074 try wasm.appendDummySegment();
30473075 break :blk index;
30483076 },
3049 .debug => {
3050 const debug_name = object.getDebugName(relocatable_data);
3051 if (mem.eql(u8, debug_name, ".debug_info")) {
3077 .section => {
3078 const section_name = object.string_table.get(symbol.name);
3079 if (mem.eql(u8, section_name, ".debug_info")) {
30523080 return wasm.debug_info_index orelse blk: {
30533081 wasm.debug_info_index = index;
30543082 try wasm.appendDummySegment();
30553083 break :blk index;
30563084 };
3057 } else if (mem.eql(u8, debug_name, ".debug_line")) {
3085 } else if (mem.eql(u8, section_name, ".debug_line")) {
30583086 return wasm.debug_line_index orelse blk: {
30593087 wasm.debug_line_index = index;
30603088 try wasm.appendDummySegment();
30613089 break :blk index;
30623090 };
3063 } else if (mem.eql(u8, debug_name, ".debug_loc")) {
3091 } else if (mem.eql(u8, section_name, ".debug_loc")) {
30643092 return wasm.debug_loc_index orelse blk: {
30653093 wasm.debug_loc_index = index;
30663094 try wasm.appendDummySegment();
30673095 break :blk index;
30683096 };
3069 } else if (mem.eql(u8, debug_name, ".debug_ranges")) {
3097 } else if (mem.eql(u8, section_name, ".debug_ranges")) {
30703098 return wasm.debug_line_index orelse blk: {
30713099 wasm.debug_ranges_index = index;
30723100 try wasm.appendDummySegment();
30733101 break :blk index;
30743102 };
3075 } else if (mem.eql(u8, debug_name, ".debug_pubnames")) {
3103 } else if (mem.eql(u8, section_name, ".debug_pubnames")) {
30763104 return wasm.debug_pubnames_index orelse blk: {
30773105 wasm.debug_pubnames_index = index;
30783106 try wasm.appendDummySegment();
30793107 break :blk index;
30803108 };
3081 } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) {
3109 } else if (mem.eql(u8, section_name, ".debug_pubtypes")) {
30823110 return wasm.debug_pubtypes_index orelse blk: {
30833111 wasm.debug_pubtypes_index = index;
30843112 try wasm.appendDummySegment();
30853113 break :blk index;
30863114 };
3087 } else if (mem.eql(u8, debug_name, ".debug_abbrev")) {
3115 } else if (mem.eql(u8, section_name, ".debug_abbrev")) {
30883116 return wasm.debug_abbrev_index orelse blk: {
30893117 wasm.debug_abbrev_index = index;
30903118 try wasm.appendDummySegment();
30913119 break :blk index;
30923120 };
3093 } else if (mem.eql(u8, debug_name, ".debug_str")) {
3121 } else if (mem.eql(u8, section_name, ".debug_str")) {
30943122 return wasm.debug_str_index orelse blk: {
30953123 wasm.debug_str_index = index;
30963124 try wasm.appendDummySegment();
30973125 break :blk index;
30983126 };
30993127 } else {
3100 log.warn("found unknown debug section '{s}'", .{debug_name});
3101 log.warn(" debug section will be skipped", .{});
3102 return null;
3128 log.warn("found unknown section '{s}'", .{section_name});
3129 return error.UnexpectedValue;
31033130 }
31043131 },
3132 else => unreachable,
31053133 }
31063134}
31073135
......@@ -3468,11 +3496,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
34683496 try wasm.setupInitFunctions();
34693497 try wasm.setupStart();
34703498
3471 for (wasm.objects.items, 0..) |*object, object_index| {
3472 try object.parseIntoAtoms(gpa, @as(u16, @intCast(object_index)), wasm);
3473 }
3474
3475 wasm.markReferences();
3499 try wasm.markReferences();
34763500 try wasm.setupImports();
34773501 try wasm.allocateAtoms();
34783502 try wasm.setupMemory();
......@@ -3558,7 +3582,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
35583582 try wasm.setupInitFunctions();
35593583 try wasm.setupErrorsLen();
35603584 try wasm.setupStart();
3561 wasm.markReferences();
3585 try wasm.markReferences();
35623586 try wasm.setupImports();
35633587 if (wasm.base.options.module) |mod| {
35643588 var decl_it = wasm.decls.iterator();
......@@ -3615,10 +3639,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
36153639 }
36163640 }
36173641
3618 for (wasm.objects.items, 0..) |*object, object_index| {
3619 try object.parseIntoAtoms(wasm.base.allocator, @as(u16, @intCast(object_index)), wasm);
3620 }
3621
36223642 try wasm.allocateAtoms();
36233643 try wasm.setupMemory();
36243644 wasm.allocateVirtualAddresses();
......@@ -3885,18 +3905,15 @@ fn writeToFile(
38853905 var atom_index = wasm.atoms.get(code_index).?;
38863906
38873907 // The code section must be sorted in line with the function order.
3888 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
3908 var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
38893909 defer sorted_atoms.deinit();
38903910
38913911 while (true) {
3892 var atom = wasm.getAtomPtr(atom_index);
3893 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {
3894 if (!is_obj) {
3895 atom.resolveRelocs(wasm);
3896 }
3897 sorted_atoms.appendAssumeCapacity(atom);
3912 const atom = wasm.getAtomPtr(atom_index);
3913 if (!is_obj) {
3914 atom.resolveRelocs(wasm);
38983915 }
3899 // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3916 sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions
39003917 atom_index = atom.prev orelse break;
39013918 }
39023919
......@@ -3908,7 +3925,7 @@ fn writeToFile(
39083925 }
39093926 }.sort;
39103927
3911 mem.sort(*Atom, sorted_atoms.items, wasm, atom_sort_fn);
3928 mem.sort(*const Atom, sorted_atoms.items, wasm, atom_sort_fn);
39123929
39133930 for (sorted_atoms.items) |sorted_atom| {
39143931 try leb.writeULEB128(binary_writer, sorted_atom.size);
......@@ -5060,20 +5077,20 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s
50605077
50615078/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
50625079/// as well as any of its references.
5063fn markReferences(wasm: *Wasm) void {
5080fn markReferences(wasm: *Wasm) !void {
50645081 const tracy = trace(@src());
50655082 defer tracy.end();
50665083 for (wasm.resolved_symbols.keys()) |sym_loc| {
50675084 const sym = sym_loc.getSymbol(wasm);
50685085 if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) {
5069 wasm.mark(sym_loc);
5086 try wasm.mark(sym_loc);
50705087 }
50715088 }
50725089}
50735090
50745091/// Marks a symbol as 'alive' recursively so itself and any references it contains to
50755092/// other symbols will not be omit from the binary.
5076fn mark(wasm: *Wasm, loc: SymbolLoc) void {
5093fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
50775094 const symbol = loc.getSymbol(wasm);
50785095 if (symbol.isAlive()) {
50795096 // Symbol is already marked alive, including its references.
......@@ -5082,13 +5099,20 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) void {
50825099 return;
50835100 }
50845101 symbol.mark();
5102 if (symbol.isUndefined()) {
5103 // undefined symbols do not have an associated `Atom` and therefore also
5104 // do not contain relocations.
5105 return;
5106 }
50855107
5086 if (wasm.symbol_atom.get(loc)) |atom_index| {
5087 const atom = wasm.getAtom(atom_index);
5088 const relocations: []const types.Relocation = atom.relocs.items;
5089 for (relocations) |reloc| {
5090 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
5091 wasm.mark(target_loc.finalLoc(wasm));
5092 }
5108 const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately
5109 const object = &wasm.objects.items[file];
5110 const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm);
5111
5112 const atom = wasm.getAtom(atom_index);
5113 const relocations: []const types.Relocation = atom.relocs.items;
5114 for (relocations) |reloc| {
5115 const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file };
5116 try wasm.mark(target_loc.finalLoc(wasm));
50935117 }
50945118}
src/link/Wasm/Atom.zig+11-7
......@@ -23,6 +23,10 @@ alignment: Wasm.Alignment,
2323/// Offset into the section where the atom lives, this already accounts
2424/// for alignment.
2525offset: u32,
26/// The original offset within the object file. This value is substracted from
27/// relocation offsets to determine where in the `data` to rewrite the value
28original_offset: u32,
29
2630/// Represents the index of the file this atom was generated from.
2731/// This is 'null' when the atom was generated by a Decl from Zig code.
2832file: ?u16,
......@@ -50,11 +54,11 @@ pub const empty: Atom = .{
5054 .prev = null,
5155 .size = 0,
5256 .sym_index = 0,
57 .original_offset = 0,
5358};
5459
5560/// Frees all resources owned by this `Atom`.
56pub fn deinit(atom: *Atom, wasm: *Wasm) void {
57 const gpa = wasm.base.allocator;
61pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
5862 atom.relocs.deinit(gpa);
5963 atom.code.deinit(gpa);
6064 atom.locals.deinit(gpa);
......@@ -114,10 +118,10 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
114118 .R_WASM_GLOBAL_INDEX_I32,
115119 .R_WASM_MEMORY_ADDR_I32,
116120 .R_WASM_SECTION_OFFSET_I32,
117 => std.mem.writeInt(u32, atom.code.items[reloc.offset..][0..4], @as(u32, @intCast(value)), .little),
121 => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @intCast(value)), .little),
118122 .R_WASM_TABLE_INDEX_I64,
119123 .R_WASM_MEMORY_ADDR_I64,
120 => std.mem.writeInt(u64, atom.code.items[reloc.offset..][0..8], value, .little),
124 => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
121125 .R_WASM_GLOBAL_INDEX_LEB,
122126 .R_WASM_EVENT_INDEX_LEB,
123127 .R_WASM_FUNCTION_INDEX_LEB,
......@@ -127,12 +131,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
127131 .R_WASM_TABLE_NUMBER_LEB,
128132 .R_WASM_TYPE_INDEX_LEB,
129133 .R_WASM_MEMORY_ADDR_TLS_SLEB,
130 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @as(u32, @intCast(value))),
134 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @intCast(value))),
131135 .R_WASM_MEMORY_ADDR_LEB64,
132136 .R_WASM_MEMORY_ADDR_SLEB64,
133137 .R_WASM_TABLE_INDEX_SLEB64,
134138 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
135 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),
139 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
136140 }
137141 }
138142}
......@@ -150,7 +154,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
150154 .R_WASM_TABLE_INDEX_I64,
151155 .R_WASM_TABLE_INDEX_SLEB,
152156 .R_WASM_TABLE_INDEX_SLEB64,
153 => return wasm_bin.function_table.get(target_loc) orelse 0,
157 => return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0,
154158 .R_WASM_TYPE_INDEX_LEB => {
155159 const file_index = atom.file orelse {
156160 return relocation.index;
src/link/Wasm/Object.zig+122-142
......@@ -59,20 +59,16 @@ init_funcs: []const types.InitFunc = &.{},
5959comdat_info: []const types.Comdat = &.{},
6060/// Represents non-synthetic sections that can essentially be mem-cpy'd into place
6161/// after performing relocations.
62relocatable_data: []const RelocatableData = &.{},
62relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .{},
6363/// String table for all strings required by the object file, such as symbol names,
6464/// import name, module name and export names. Each string will be deduplicated
6565/// and returns an offset into the table.
6666string_table: Wasm.StringTable = .{},
67/// All the names of each debug section found in the current object file.
68/// Each name is terminated by a null-terminator. The name can be found,
69/// from the `index` offset within the `RelocatableData`.
70debug_names: [:0]const u8,
7167
7268/// Represents a single item within a section (depending on its `type`)
7369const RelocatableData = struct {
7470 /// The type of the relocatable data
75 type: enum { data, code, debug },
71 type: Tag,
7672 /// Pointer to the data of the segment, where its length is written to `size`
7773 data: [*]u8,
7874 /// The size in bytes of the data representing the segment within the section
......@@ -85,6 +81,8 @@ const RelocatableData = struct {
8581 /// Represents the index of the section it belongs to
8682 section_index: u32,
8783
84 const Tag = enum { data, code, custom };
85
8886 /// Returns the alignment of the segment, by retrieving it from the segment
8987 /// meta data of the given object file.
9088 /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's
......@@ -99,14 +97,14 @@ const RelocatableData = struct {
9997 return switch (relocatable_data.type) {
10098 .data => .data,
10199 .code => .function,
102 .debug => .section,
100 .custom => .section,
103101 };
104102 }
105103
106 /// Returns the index within a section itrelocatable_data, or in case of a debug section,
104 /// Returns the index within a section, or in case of a custom section,
107105 /// returns the section index within the object file.
108106 pub fn getIndex(relocatable_data: RelocatableData) u32 {
109 if (relocatable_data.type == .debug) return relocatable_data.section_index;
107 if (relocatable_data.type == .custom) return relocatable_data.section_index;
110108 return relocatable_data.index;
111109 }
112110};
......@@ -121,7 +119,6 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
121119 var object: Object = .{
122120 .file = file,
123121 .name = try gpa.dupe(u8, name),
124 .debug_names = &.{},
125122 };
126123
127124 var is_object_file: bool = false;
......@@ -182,10 +179,16 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
182179 gpa.free(info.name);
183180 }
184181 gpa.free(object.segment_info);
185 for (object.relocatable_data) |rel_data| {
186 gpa.free(rel_data.data[0..rel_data.size]);
182 {
183 var it = object.relocatable_data.valueIterator();
184 while (it.next()) |relocatable_data| {
185 for (relocatable_data.*) |rel_data| {
186 gpa.free(rel_data.data[0..rel_data.size]);
187 }
188 gpa.free(relocatable_data.*);
189 }
187190 }
188 gpa.free(object.relocatable_data);
191 object.relocatable_data.deinit(gpa);
189192 object.string_table.deinit(gpa);
190193 gpa.free(object.name);
191194 object.* = undefined;
......@@ -345,23 +348,7 @@ fn Parser(comptime ReaderType: type) type {
345348 errdefer parser.object.deinit(gpa);
346349 try parser.verifyMagicBytes();
347350 const version = try parser.reader.reader().readInt(u32, .little);
348
349351 parser.object.version = version;
350 var relocatable_data = std.ArrayList(RelocatableData).init(gpa);
351 var debug_names = std.ArrayList(u8).init(gpa);
352
353 errdefer {
354 // only free the inner contents of relocatable_data if we didn't
355 // assign it to the object yet.
356 if (parser.object.relocatable_data.len == 0) {
357 for (relocatable_data.items) |rel_data| {
358 gpa.free(rel_data.data[0..rel_data.size]);
359 }
360 relocatable_data.deinit();
361 }
362 gpa.free(debug_names.items);
363 debug_names.deinit();
364 }
365352
366353 var section_index: u32 = 0;
367354 while (parser.reader.reader().readByte()) |byte| : (section_index += 1) {
......@@ -377,26 +364,34 @@ fn Parser(comptime ReaderType: type) type {
377364
378365 if (std.mem.eql(u8, name, "linking")) {
379366 is_object_file.* = true;
380 parser.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them.
381367 try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left)));
382368 } else if (std.mem.startsWith(u8, name, "reloc")) {
383369 try parser.parseRelocations(gpa);
384370 } else if (std.mem.eql(u8, name, "target_features")) {
385371 try parser.parseFeatures(gpa);
386372 } else if (std.mem.startsWith(u8, name, ".debug")) {
373 const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom);
374 var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .{};
375 defer relocatable_data.deinit(gpa);
376 if (!gop.found_existing) {
377 gop.value_ptr.* = &.{};
378 } else {
379 relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*);
380 }
387381 const debug_size = @as(u32, @intCast(reader.context.bytes_left));
388382 const debug_content = try gpa.alloc(u8, debug_size);
389383 errdefer gpa.free(debug_content);
390384 try reader.readNoEof(debug_content);
391385
392 try relocatable_data.append(.{
393 .type = .debug,
386 try relocatable_data.append(gpa, .{
387 .type = .custom,
394388 .data = debug_content.ptr,
395389 .size = debug_size,
396390 .index = try parser.object.string_table.put(gpa, name),
397391 .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset
398392 .section_index = section_index,
399393 });
394 gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa);
400395 } else {
401396 try reader.skipBytes(reader.context.bytes_left, .{});
402397 }
......@@ -515,26 +510,32 @@ fn Parser(comptime ReaderType: type) type {
515510 const start = reader.context.bytes_left;
516511 var index: u32 = 0;
517512 const count = try readLeb(u32, reader);
513 const imported_function_count = parser.object.importedCountByKind(.function);
514 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
515 defer relocatable_data.deinit();
518516 while (index < count) : (index += 1) {
519517 const code_len = try readLeb(u32, reader);
520518 const offset = @as(u32, @intCast(start - reader.context.bytes_left));
521519 const data = try gpa.alloc(u8, code_len);
522520 errdefer gpa.free(data);
523521 try reader.readNoEof(data);
524 try relocatable_data.append(.{
522 relocatable_data.appendAssumeCapacity(.{
525523 .type = .code,
526524 .data = data.ptr,
527525 .size = code_len,
528 .index = parser.object.importedCountByKind(.function) + index,
526 .index = imported_function_count + index,
529527 .offset = offset,
530528 .section_index = section_index,
531529 });
532530 }
531 try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice());
533532 },
534533 .data => {
535534 const start = reader.context.bytes_left;
536535 var index: u32 = 0;
537536 const count = try readLeb(u32, reader);
537 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
538 defer relocatable_data.deinit();
538539 while (index < count) : (index += 1) {
539540 const flags = try readLeb(u32, reader);
540541 const data_offset = try readInit(reader);
......@@ -545,7 +546,7 @@ fn Parser(comptime ReaderType: type) type {
545546 const data = try gpa.alloc(u8, data_len);
546547 errdefer gpa.free(data);
547548 try reader.readNoEof(data);
548 try relocatable_data.append(.{
549 relocatable_data.appendAssumeCapacity(.{
549550 .type = .data,
550551 .data = data.ptr,
551552 .size = data_len,
......@@ -554,6 +555,7 @@ fn Parser(comptime ReaderType: type) type {
554555 .section_index = section_index,
555556 });
556557 }
558 try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice());
557559 },
558560 else => try parser.reader.reader().skipBytes(len, .{}),
559561 }
......@@ -561,7 +563,6 @@ fn Parser(comptime ReaderType: type) type {
561563 error.EndOfStream => {}, // finished parsing the file
562564 else => |e| return e,
563565 }
564 parser.object.relocatable_data = try relocatable_data.toOwnedSlice();
565566 }
566567
567568 /// Based on the "features" custom section, parses it into a list of
......@@ -789,7 +790,8 @@ fn Parser(comptime ReaderType: type) type {
789790 },
790791 .section => {
791792 symbol.index = try leb.readULEB128(u32, reader);
792 for (parser.object.relocatable_data) |data| {
793 const section_data = parser.object.relocatable_data.get(.custom).?;
794 for (section_data) |data| {
793795 if (data.section_index == symbol.index) {
794796 symbol.name = data.index;
795797 break;
......@@ -798,22 +800,15 @@ fn Parser(comptime ReaderType: type) type {
798800 },
799801 else => {
800802 symbol.index = try leb.readULEB128(u32, reader);
801 var maybe_import: ?types.Import = null;
802
803803 const is_undefined = symbol.isUndefined();
804 if (is_undefined) {
805 maybe_import = parser.object.findImport(symbol.tag.externalType(), symbol.index);
806 }
807804 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);
808 if (!(is_undefined and !explicit_name)) {
805 symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: {
809806 const name_len = try leb.readULEB128(u32, reader);
810807 const name = try gpa.alloc(u8, name_len);
811808 defer gpa.free(name);
812809 try reader.readNoEof(name);
813 symbol.name = try parser.object.string_table.put(gpa, name);
814 } else {
815 symbol.name = maybe_import.?.name;
816 }
810 break :name try parser.object.string_table.put(gpa, name);
811 } else parser.object.findImport(symbol.tag.externalType(), symbol.index).name;
817812 },
818813 }
819814 return symbol;
......@@ -887,110 +882,95 @@ fn assertEnd(reader: anytype) !void {
887882}
888883
889884/// Parses an object file into atoms, for code and data sections
890pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
891 const Key = struct {
892 kind: Symbol.Tag,
893 index: u32,
894 };
895 var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa);
896 defer for (symbol_for_segment.values()) |*list| {
897 list.deinit();
898 } else symbol_for_segment.deinit();
899
900 for (object.symtable, 0..) |symbol, symbol_index| {
901 switch (symbol.tag) {
902 .function, .data, .section => if (!symbol.isUndefined()) {
903 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
904 const sym_idx = @as(u32, @intCast(symbol_index));
905 if (!gop.found_existing) {
906 gop.value_ptr.* = std.ArrayList(u32).init(gpa);
885pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index {
886 const symbol = &object.symtable[symbol_index];
887 const relocatable_data: RelocatableData = switch (symbol.tag) {
888 .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)],
889 .data => object.relocatable_data.get(.data).?[symbol.index],
890 .section => blk: {
891 const data = object.relocatable_data.get(.custom).?;
892 for (data) |dat| {
893 if (dat.section_index == symbol.index) {
894 break :blk dat;
907895 }
908 try gop.value_ptr.*.append(sym_idx);
909 },
910 else => continue,
911 }
896 }
897 unreachable;
898 },
899 else => unreachable,
900 };
901 const final_index = try wasm.getMatchingSegment(object_index, symbol_index);
902 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
903 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
904 atom.* = Atom.empty;
905 try wasm.appendAtomAtIndex(final_index, atom_index);
906
907 atom.sym_index = symbol_index;
908 atom.file = object_index;
909 atom.size = relocatable_data.size;
910 atom.alignment = relocatable_data.getAlignment(object);
911 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
912 atom.original_offset = relocatable_data.offset;
913 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom_index);
914 const segment: *Wasm.Segment = &wasm.segments.items[final_index];
915 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
916 segment.alignment = segment.alignment.max(atom.alignment);
912917 }
913918
914 for (object.relocatable_data, 0..) |relocatable_data, index| {
915 const final_index = (try wasm_bin.getMatchingSegment(object_index, @as(u32, @intCast(index)))) orelse {
916 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.
917 };
918
919 const atom_index: Atom.Index = @intCast(wasm_bin.managed_atoms.items.len);
920 const atom = try wasm_bin.managed_atoms.addOne(gpa);
921 atom.* = Atom.empty;
922 atom.file = object_index;
923 atom.size = relocatable_data.size;
924 atom.alignment = relocatable_data.getAlignment(object);
925
926 const relocations: []types.Relocation = object.relocations.get(relocatable_data.section_index) orelse &.{};
927 for (relocations) |relocation| {
928 if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) {
929 // set the offset relative to the offset of the segment itobject,
930 // rather than within the entire section.
931 var reloc = relocation;
932 reloc.offset -= relocatable_data.offset;
933 try atom.relocs.append(gpa, reloc);
934
935 switch (relocation.relocation_type) {
936 .R_WASM_TABLE_INDEX_I32,
937 .R_WASM_TABLE_INDEX_I64,
938 .R_WASM_TABLE_INDEX_SLEB,
939 .R_WASM_TABLE_INDEX_SLEB64,
940 => {
941 try wasm_bin.function_table.put(gpa, .{
942 .file = object_index,
943 .index = relocation.index,
944 }, 0);
945 },
946 .R_WASM_GLOBAL_INDEX_I32,
947 .R_WASM_GLOBAL_INDEX_LEB,
948 => {
949 const sym = object.symtable[relocation.index];
950 if (sym.tag != .global) {
951 try wasm_bin.got_symbols.append(
952 wasm_bin.base.allocator,
953 .{ .file = object_index, .index = relocation.index },
954 );
955 }
956 },
957 else => {},
958 }
919 if (object.relocations.get(relocatable_data.section_index)) |relocations| {
920 const start = searchRelocStart(relocations, relocatable_data.offset);
921 const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
922 atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]);
923 for (atom.relocs.items) |*reloc| {
924 switch (reloc.relocation_type) {
925 .R_WASM_TABLE_INDEX_I32,
926 .R_WASM_TABLE_INDEX_I64,
927 .R_WASM_TABLE_INDEX_SLEB,
928 .R_WASM_TABLE_INDEX_SLEB64,
929 => {
930 try wasm.function_table.put(wasm.base.allocator, .{
931 .file = object_index,
932 .index = reloc.index,
933 }, 0);
934 },
935 .R_WASM_GLOBAL_INDEX_I32,
936 .R_WASM_GLOBAL_INDEX_LEB,
937 => {
938 const sym = object.symtable[reloc.index];
939 if (sym.tag != .global) {
940 try wasm.got_symbols.append(
941 wasm.base.allocator,
942 .{ .file = object_index, .index = reloc.index },
943 );
944 }
945 },
946 else => {},
959947 }
960948 }
949 }
961950
962 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
963
964 if (symbol_for_segment.getPtr(.{
965 .kind = relocatable_data.getSymbolKind(),
966 .index = relocatable_data.getIndex(),
967 })) |symbols| {
968 atom.sym_index = symbols.pop();
969 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);
970
971 // symbols referencing the same atom will be added as alias
972 // or as 'parent' when they are global.
973 while (symbols.popOrNull()) |idx| {
974 try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom_index);
975 const alias_symbol = object.symtable[idx];
976 if (alias_symbol.isGlobal()) {
977 atom.sym_index = idx;
978 }
979 }
980 }
951 return atom_index;
952}
981953
982 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
983 if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned
984 segment.alignment = segment.alignment.max(atom.alignment);
954fn searchRelocStart(relocs: []const types.Relocation, address: u32) usize {
955 var min: usize = 0;
956 var max: usize = relocs.len;
957 while (min < max) {
958 const index = (min + max) / 2;
959 const curr = relocs[index];
960 if (curr.offset < address) {
961 min = index + 1;
962 } else {
963 max = index;
985964 }
986
987 try wasm_bin.appendAtomAtIndex(final_index, atom_index);
988 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index });
989965 }
966 return min;
990967}
991968
992/// Verifies if a given value is in between a minimum -and maximum value.
993/// The maxmimum value is calculated using the length, both start and end are inclusive.
994inline fn isInbetween(min: u32, length: u32, value: u32) bool {
995 return value >= min and value <= min + length;
969fn searchRelocEnd(relocs: []const types.Relocation, address: u32) usize {
970 for (relocs, 0..relocs.len) |reloc, index| {
971 if (reloc.offset > address) {
972 return index;
973 }
974 }
975 return relocs.len;
996976}