authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-27 18:09:43+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-01 08:35:20+01:00
log49f01c0a0cd510437f6f2d13d5de3e722f48cc3d
tree35481c8603ed3c0dc69aef5621ea524cd09670af
parenteaf1c97ce80606505768b8dd8daf8b479aec91a7

wasm-object: Use given allocator rather than arena

This is preliminary work for string interning in the wasm linker. Using an arena would defeat the purpose of de-duplicating strings as we wouldn't be able to free memory of duplicated strings. This change also means we can simplify wasm binary parsing, by creating a general purpose parser that parses the binary into its sections, but untyped. Doing this, allows us to re-use the base of that, for object file, but also debug info parsing.

2 files changed, 65 insertions(+), 23 deletions(-)

src/link/Wasm.zig+5-1
...@@ -248,7 +248,7 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool {...@@ -248,7 +248,7 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool {
248248
249 var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) {249 var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) {
250 error.InvalidMagicByte, error.NotObjectFile => {250 error.InvalidMagicByte, error.NotObjectFile => {
251 log.warn("Self hosted linker does not support non-object file parsing", .{});251 log.warn("Self hosted linker does not support non-object file parsing: {s}", .{@errorName(err)});
252 return false;252 return false;
253 },253 },
254 else => |e| return e,254 else => |e| return e,
...@@ -356,6 +356,10 @@ pub fn deinit(self: *Wasm) void {...@@ -356,6 +356,10 @@ pub fn deinit(self: *Wasm) void {
356 self.symbol_atom.deinit(gpa);356 self.symbol_atom.deinit(gpa);
357 self.export_names.deinit(gpa);357 self.export_names.deinit(gpa);
358 self.atoms.deinit(gpa);358 self.atoms.deinit(gpa);
359 for (self.managed_atoms.items) |managed_atom| {
360 managed_atom.deinit(gpa);
361 gpa.destroy(managed_atom);
362 }
359 self.managed_atoms.deinit(gpa);363 self.managed_atoms.deinit(gpa);
360 self.segments.deinit(gpa);364 self.segments.deinit(gpa);
361 self.data_segments.deinit(gpa);365 self.data_segments.deinit(gpa);
src/link/Wasm/Object.zig+60-22
...@@ -17,10 +17,6 @@ const log = std.log.scoped(.link);...@@ -17,10 +17,6 @@ const log = std.log.scoped(.link);
1717
18/// Wasm spec version used for this `Object`18/// Wasm spec version used for this `Object`
19version: u32 = 0,19version: u32 = 0,
20/// The entire object file is read and parsed in a single pass.
21/// For this reason it's a lot simpler to use an arena and store the entire
22/// state after parsing. This also allows to free all memory at once.
23arena: std.heap.ArenaAllocator.State = .{},
24/// The file descriptor that represents the wasm object file.20/// The file descriptor that represents the wasm object file.
25file: ?std.fs.File = null,21file: ?std.fs.File = null,
26/// Name (read path) of the object file.22/// Name (read path) of the object file.
...@@ -28,15 +24,15 @@ name: []const u8,...@@ -28,15 +24,15 @@ name: []const u8,
28/// Parsed type section24/// Parsed type section
29func_types: []const std.wasm.Type = &.{},25func_types: []const std.wasm.Type = &.{},
30/// A list of all imports for this module26/// A list of all imports for this module
31imports: []std.wasm.Import = &.{},27imports: []const std.wasm.Import = &.{},
32/// Parsed function section28/// Parsed function section
33functions: []std.wasm.Func = &.{},29functions: []const std.wasm.Func = &.{},
34/// Parsed table section30/// Parsed table section
35tables: []std.wasm.Table = &.{},31tables: []const std.wasm.Table = &.{},
36/// Parsed memory section32/// Parsed memory section
37memories: []const std.wasm.Memory = &.{},33memories: []const std.wasm.Memory = &.{},
38/// Parsed global section34/// Parsed global section
39globals: []std.wasm.Global = &.{},35globals: []const std.wasm.Global = &.{},
40/// Parsed export section36/// Parsed export section
41exports: []const std.wasm.Export = &.{},37exports: []const std.wasm.Export = &.{},
42/// Parsed element section38/// Parsed element section
...@@ -62,7 +58,7 @@ init_funcs: []const types.InitFunc = &.{},...@@ -62,7 +58,7 @@ init_funcs: []const types.InitFunc = &.{},
62comdat_info: []const types.Comdat = &.{},58comdat_info: []const types.Comdat = &.{},
63/// Represents non-synthetic sections that can essentially be mem-cpy'd into place59/// Represents non-synthetic sections that can essentially be mem-cpy'd into place
64/// after performing relocations.60/// after performing relocations.
65relocatable_data: []RelocatableData = &.{},61relocatable_data: []const RelocatableData = &.{},
6662
67/// Represents a single item within a section (depending on its `type`)63/// Represents a single item within a section (depending on its `type`)
68const RelocatableData = struct {64const RelocatableData = struct {
...@@ -111,12 +107,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj...@@ -111,12 +107,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj
111 .name = path,107 .name = path,
112 };108 };
113109
114 var arena = std.heap.ArenaAllocator.init(gpa);
115 errdefer arena.deinit();
116
117 var is_object_file: bool = false;110 var is_object_file: bool = false;
118 try object.parse(arena.allocator(), file.reader(), &is_object_file);111 try object.parse(gpa, file.reader(), &is_object_file);
119 object.arena = arena.state;112 errdefer object.deinit(gpa);
120 if (!is_object_file) return error.NotObjectFile;113 if (!is_object_file) return error.NotObjectFile;
121114
122 return object;115 return object;
...@@ -125,7 +118,44 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj...@@ -125,7 +118,44 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj
125/// Frees all memory of `Object` at once. The given `Allocator` must be118/// Frees all memory of `Object` at once. The given `Allocator` must be
126/// the same allocator that was used when `init` was called.119/// the same allocator that was used when `init` was called.
127pub fn deinit(self: *Object, gpa: Allocator) void {120pub fn deinit(self: *Object, gpa: Allocator) void {
128 self.arena.promote(gpa).deinit();121 for (self.func_types) |func_ty| {
122 gpa.free(func_ty.params);
123 gpa.free(func_ty.returns);
124 }
125 gpa.free(self.func_types);
126 for (self.imports) |imp| {
127 gpa.free(imp.name);
128 gpa.free(imp.module_name);
129 }
130 gpa.free(self.functions);
131 gpa.free(self.imports);
132 gpa.free(self.tables);
133 gpa.free(self.memories);
134 gpa.free(self.globals);
135 for (self.exports) |exp| {
136 gpa.free(exp.name);
137 }
138 gpa.free(self.exports);
139 gpa.free(self.elements);
140 gpa.free(self.features);
141 for (self.relocations.values()) |val| {
142 gpa.free(val);
143 }
144 self.relocations.deinit(gpa);
145 for (self.symtable) |symbol| {
146 gpa.free(std.mem.sliceTo(symbol.name, 0));
147 }
148 gpa.free(self.symtable);
149 gpa.free(self.comdat_info);
150 gpa.free(self.init_funcs);
151 for (self.segment_info) |info| {
152 gpa.free(info.name);
153 }
154 gpa.free(self.segment_info);
155 for (self.relocatable_data) |rel_data| {
156 gpa.free(rel_data.data[0..rel_data.size]);
157 }
158 gpa.free(self.relocatable_data);
129 self.* = undefined;159 self.* = undefined;
130}160}
131161
...@@ -149,13 +179,6 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32...@@ -149,13 +179,6 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
149 } else i;179 } else i;
150}180}
151181
152/// Returns a table by a given id, rather than by its index within the list.
153pub fn getTable(self: *const Object, id: u32) *std.wasm.Table {
154 return for (self.tables) |*table| {
155 if (table.table_idx == id) break table;
156 } else unreachable;
157}
158
159/// Checks if the object file is an MVP version.182/// Checks if the object file is an MVP version.
160/// When that's the case, we check if there's an import table definiton with its name183/// When that's the case, we check if there's an import table definiton with its name
161/// set to '__indirect_function_table". When that's also the case,184/// set to '__indirect_function_table". When that's also the case,
...@@ -328,10 +351,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -328,10 +351,12 @@ fn Parser(comptime ReaderType: type) type {
328 for (try readVec(&self.object.imports, reader, gpa)) |*import| {351 for (try readVec(&self.object.imports, reader, gpa)) |*import| {
329 const module_len = try readLeb(u32, reader);352 const module_len = try readLeb(u32, reader);
330 const module_name = try gpa.alloc(u8, module_len);353 const module_name = try gpa.alloc(u8, module_len);
354 errdefer gpa.free(module_name);
331 try reader.readNoEof(module_name);355 try reader.readNoEof(module_name);
332356
333 const name_len = try readLeb(u32, reader);357 const name_len = try readLeb(u32, reader);
334 const name = try gpa.alloc(u8, name_len);358 const name = try gpa.alloc(u8, name_len);
359 errdefer gpa.free(name);
335 try reader.readNoEof(name);360 try reader.readNoEof(name);
336361
337 const kind = try readEnum(std.wasm.ExternalKind, reader);362 const kind = try readEnum(std.wasm.ExternalKind, reader);
...@@ -393,6 +418,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -393,6 +418,7 @@ fn Parser(comptime ReaderType: type) type {
393 for (try readVec(&self.object.exports, reader, gpa)) |*exp| {418 for (try readVec(&self.object.exports, reader, gpa)) |*exp| {
394 const name_len = try readLeb(u32, reader);419 const name_len = try readLeb(u32, reader);
395 const name = try gpa.alloc(u8, name_len);420 const name = try gpa.alloc(u8, name_len);
421 errdefer gpa.free(name);
396 try reader.readNoEof(name);422 try reader.readNoEof(name);
397 exp.* = .{423 exp.* = .{
398 .name = name,424 .name = name,
...@@ -425,6 +451,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -425,6 +451,7 @@ fn Parser(comptime ReaderType: type) type {
425 const code_len = try readLeb(u32, reader);451 const code_len = try readLeb(u32, reader);
426 const offset = @intCast(u32, start - reader.context.bytes_left);452 const offset = @intCast(u32, start - reader.context.bytes_left);
427 const data = try gpa.alloc(u8, code_len);453 const data = try gpa.alloc(u8, code_len);
454 errdefer gpa.free(data);
428 try reader.readNoEof(data);455 try reader.readNoEof(data);
429 try relocatable_data.append(.{456 try relocatable_data.append(.{
430 .type = .code,457 .type = .code,
...@@ -448,6 +475,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -448,6 +475,7 @@ fn Parser(comptime ReaderType: type) type {
448 const data_len = try readLeb(u32, reader);475 const data_len = try readLeb(u32, reader);
449 const offset = @intCast(u32, start - reader.context.bytes_left);476 const offset = @intCast(u32, start - reader.context.bytes_left);
450 const data = try gpa.alloc(u8, data_len);477 const data = try gpa.alloc(u8, data_len);
478 errdefer gpa.free(data);
451 try reader.readNoEof(data);479 try reader.readNoEof(data);
452 try relocatable_data.append(.{480 try relocatable_data.append(.{
453 .type = .data,481 .type = .data,
...@@ -478,6 +506,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -478,6 +506,7 @@ fn Parser(comptime ReaderType: type) type {
478 const prefix = try readEnum(types.Feature.Prefix, reader);506 const prefix = try readEnum(types.Feature.Prefix, reader);
479 const name_len = try leb.readULEB128(u32, reader);507 const name_len = try leb.readULEB128(u32, reader);
480 const name = try gpa.alloc(u8, name_len);508 const name = try gpa.alloc(u8, name_len);
509 defer gpa.free(name);
481 try reader.readNoEof(name);510 try reader.readNoEof(name);
482511
483 const tag = types.known_features.get(name) orelse {512 const tag = types.known_features.get(name) orelse {
...@@ -499,6 +528,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -499,6 +528,7 @@ fn Parser(comptime ReaderType: type) type {
499 const section = try leb.readULEB128(u32, reader);528 const section = try leb.readULEB128(u32, reader);
500 const count = try leb.readULEB128(u32, reader);529 const count = try leb.readULEB128(u32, reader);
501 const relocations = try gpa.alloc(types.Relocation, count);530 const relocations = try gpa.alloc(types.Relocation, count);
531 errdefer gpa.free(relocations);
502532
503 log.debug("Found {d} relocations for section ({d})", .{533 log.debug("Found {d} relocations for section ({d})", .{
504 count,534 count,
...@@ -563,9 +593,11 @@ fn Parser(comptime ReaderType: type) type {...@@ -563,9 +593,11 @@ fn Parser(comptime ReaderType: type) type {
563 switch (@intToEnum(types.SubsectionType, sub_type)) {593 switch (@intToEnum(types.SubsectionType, sub_type)) {
564 .WASM_SEGMENT_INFO => {594 .WASM_SEGMENT_INFO => {
565 const segments = try gpa.alloc(types.Segment, count);595 const segments = try gpa.alloc(types.Segment, count);
596 errdefer gpa.free(segments);
566 for (segments) |*segment| {597 for (segments) |*segment| {
567 const name_len = try leb.readULEB128(u32, reader);598 const name_len = try leb.readULEB128(u32, reader);
568 const name = try gpa.alloc(u8, name_len);599 const name = try gpa.alloc(u8, name_len);
600 errdefer gpa.free(name);
569 try reader.readNoEof(name);601 try reader.readNoEof(name);
570 segment.* = .{602 segment.* = .{
571 .name = name,603 .name = name,
...@@ -582,6 +614,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -582,6 +614,7 @@ fn Parser(comptime ReaderType: type) type {
582 },614 },
583 .WASM_INIT_FUNCS => {615 .WASM_INIT_FUNCS => {
584 const funcs = try gpa.alloc(types.InitFunc, count);616 const funcs = try gpa.alloc(types.InitFunc, count);
617 errdefer gpa.free(funcs);
585 for (funcs) |*func| {618 for (funcs) |*func| {
586 func.* = .{619 func.* = .{
587 .priority = try leb.readULEB128(u32, reader),620 .priority = try leb.readULEB128(u32, reader),
...@@ -593,9 +626,11 @@ fn Parser(comptime ReaderType: type) type {...@@ -593,9 +626,11 @@ fn Parser(comptime ReaderType: type) type {
593 },626 },
594 .WASM_COMDAT_INFO => {627 .WASM_COMDAT_INFO => {
595 const comdats = try gpa.alloc(types.Comdat, count);628 const comdats = try gpa.alloc(types.Comdat, count);
629 errdefer gpa.free(comdats);
596 for (comdats) |*comdat| {630 for (comdats) |*comdat| {
597 const name_len = try leb.readULEB128(u32, reader);631 const name_len = try leb.readULEB128(u32, reader);
598 const name = try gpa.alloc(u8, name_len);632 const name = try gpa.alloc(u8, name_len);
633 errdefer gpa.free(name);
599 try reader.readNoEof(name);634 try reader.readNoEof(name);
600635
601 const flags = try leb.readULEB128(u32, reader);636 const flags = try leb.readULEB128(u32, reader);
...@@ -605,6 +640,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -605,6 +640,7 @@ fn Parser(comptime ReaderType: type) type {
605640
606 const symbol_count = try leb.readULEB128(u32, reader);641 const symbol_count = try leb.readULEB128(u32, reader);
607 const symbols = try gpa.alloc(types.ComdatSym, symbol_count);642 const symbols = try gpa.alloc(types.ComdatSym, symbol_count);
643 errdefer gpa.free(symbols);
608 for (symbols) |*symbol| {644 for (symbols) |*symbol| {
609 symbol.* = .{645 symbol.* = .{
610 .kind = @intToEnum(types.ComdatSym.Type, try leb.readULEB128(u8, reader)),646 .kind = @intToEnum(types.ComdatSym.Type, try leb.readULEB128(u8, reader)),
...@@ -664,6 +700,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -664,6 +700,7 @@ fn Parser(comptime ReaderType: type) type {
664 .data => {700 .data => {
665 const name_len = try leb.readULEB128(u32, reader);701 const name_len = try leb.readULEB128(u32, reader);
666 const name = try gpa.allocSentinel(u8, name_len, 0);702 const name = try gpa.allocSentinel(u8, name_len, 0);
703 errdefer gpa.free(name);
667 try reader.readNoEof(name);704 try reader.readNoEof(name);
668 symbol.name = name;705 symbol.name = name;
669706
...@@ -691,6 +728,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -691,6 +728,7 @@ fn Parser(comptime ReaderType: type) type {
691 if (!(is_undefined and !explicit_name)) {728 if (!(is_undefined and !explicit_name)) {
692 const name_len = try leb.readULEB128(u32, reader);729 const name_len = try leb.readULEB128(u32, reader);
693 const name = try gpa.allocSentinel(u8, name_len, 0);730 const name = try gpa.allocSentinel(u8, name_len, 0);
731 errdefer gpa.free(name);
694 try reader.readNoEof(name);732 try reader.readNoEof(name);
695 symbol.name = name;733 symbol.name = name;
696 } else {734 } else {