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 {
248248
249249 var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) {
250250 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)});
252252 return false;
253253 },
254254 else => |e| return e,
......@@ -356,6 +356,10 @@ pub fn deinit(self: *Wasm) void {
356356 self.symbol_atom.deinit(gpa);
357357 self.export_names.deinit(gpa);
358358 self.atoms.deinit(gpa);
359 for (self.managed_atoms.items) |managed_atom| {
360 managed_atom.deinit(gpa);
361 gpa.destroy(managed_atom);
362 }
359363 self.managed_atoms.deinit(gpa);
360364 self.segments.deinit(gpa);
361365 self.data_segments.deinit(gpa);
src/link/Wasm/Object.zig+60-22
......@@ -17,10 +17,6 @@ const log = std.log.scoped(.link);
1717
1818/// Wasm spec version used for this `Object`
1919version: 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 = .{},
2420/// The file descriptor that represents the wasm object file.
2521file: ?std.fs.File = null,
2622/// Name (read path) of the object file.
......@@ -28,15 +24,15 @@ name: []const u8,
2824/// Parsed type section
2925func_types: []const std.wasm.Type = &.{},
3026/// A list of all imports for this module
31imports: []std.wasm.Import = &.{},
27imports: []const std.wasm.Import = &.{},
3228/// Parsed function section
33functions: []std.wasm.Func = &.{},
29functions: []const std.wasm.Func = &.{},
3430/// Parsed table section
35tables: []std.wasm.Table = &.{},
31tables: []const std.wasm.Table = &.{},
3632/// Parsed memory section
3733memories: []const std.wasm.Memory = &.{},
3834/// Parsed global section
39globals: []std.wasm.Global = &.{},
35globals: []const std.wasm.Global = &.{},
4036/// Parsed export section
4137exports: []const std.wasm.Export = &.{},
4238/// Parsed element section
......@@ -62,7 +58,7 @@ init_funcs: []const types.InitFunc = &.{},
6258comdat_info: []const types.Comdat = &.{},
6359/// Represents non-synthetic sections that can essentially be mem-cpy'd into place
6460/// after performing relocations.
65relocatable_data: []RelocatableData = &.{},
61relocatable_data: []const RelocatableData = &.{},
6662
6763/// Represents a single item within a section (depending on its `type`)
6864const RelocatableData = struct {
......@@ -111,12 +107,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj
111107 .name = path,
112108 };
113109
114 var arena = std.heap.ArenaAllocator.init(gpa);
115 errdefer arena.deinit();
116
117110 var is_object_file: bool = false;
118 try object.parse(arena.allocator(), file.reader(), &is_object_file);
119 object.arena = arena.state;
111 try object.parse(gpa, file.reader(), &is_object_file);
112 errdefer object.deinit(gpa);
120113 if (!is_object_file) return error.NotObjectFile;
121114
122115 return object;
......@@ -125,7 +118,44 @@ pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Obj
125118/// Frees all memory of `Object` at once. The given `Allocator` must be
126119/// the same allocator that was used when `init` was called.
127120pub 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);
129159 self.* = undefined;
130160}
131161
......@@ -149,13 +179,6 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
149179 } else i;
150180}
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
159182/// Checks if the object file is an MVP version.
160183/// When that's the case, we check if there's an import table definiton with its name
161184/// set to '__indirect_function_table". When that's also the case,
......@@ -328,10 +351,12 @@ fn Parser(comptime ReaderType: type) type {
328351 for (try readVec(&self.object.imports, reader, gpa)) |*import| {
329352 const module_len = try readLeb(u32, reader);
330353 const module_name = try gpa.alloc(u8, module_len);
354 errdefer gpa.free(module_name);
331355 try reader.readNoEof(module_name);
332356
333357 const name_len = try readLeb(u32, reader);
334358 const name = try gpa.alloc(u8, name_len);
359 errdefer gpa.free(name);
335360 try reader.readNoEof(name);
336361
337362 const kind = try readEnum(std.wasm.ExternalKind, reader);
......@@ -393,6 +418,7 @@ fn Parser(comptime ReaderType: type) type {
393418 for (try readVec(&self.object.exports, reader, gpa)) |*exp| {
394419 const name_len = try readLeb(u32, reader);
395420 const name = try gpa.alloc(u8, name_len);
421 errdefer gpa.free(name);
396422 try reader.readNoEof(name);
397423 exp.* = .{
398424 .name = name,
......@@ -425,6 +451,7 @@ fn Parser(comptime ReaderType: type) type {
425451 const code_len = try readLeb(u32, reader);
426452 const offset = @intCast(u32, start - reader.context.bytes_left);
427453 const data = try gpa.alloc(u8, code_len);
454 errdefer gpa.free(data);
428455 try reader.readNoEof(data);
429456 try relocatable_data.append(.{
430457 .type = .code,
......@@ -448,6 +475,7 @@ fn Parser(comptime ReaderType: type) type {
448475 const data_len = try readLeb(u32, reader);
449476 const offset = @intCast(u32, start - reader.context.bytes_left);
450477 const data = try gpa.alloc(u8, data_len);
478 errdefer gpa.free(data);
451479 try reader.readNoEof(data);
452480 try relocatable_data.append(.{
453481 .type = .data,
......@@ -478,6 +506,7 @@ fn Parser(comptime ReaderType: type) type {
478506 const prefix = try readEnum(types.Feature.Prefix, reader);
479507 const name_len = try leb.readULEB128(u32, reader);
480508 const name = try gpa.alloc(u8, name_len);
509 defer gpa.free(name);
481510 try reader.readNoEof(name);
482511
483512 const tag = types.known_features.get(name) orelse {
......@@ -499,6 +528,7 @@ fn Parser(comptime ReaderType: type) type {
499528 const section = try leb.readULEB128(u32, reader);
500529 const count = try leb.readULEB128(u32, reader);
501530 const relocations = try gpa.alloc(types.Relocation, count);
531 errdefer gpa.free(relocations);
502532
503533 log.debug("Found {d} relocations for section ({d})", .{
504534 count,
......@@ -563,9 +593,11 @@ fn Parser(comptime ReaderType: type) type {
563593 switch (@intToEnum(types.SubsectionType, sub_type)) {
564594 .WASM_SEGMENT_INFO => {
565595 const segments = try gpa.alloc(types.Segment, count);
596 errdefer gpa.free(segments);
566597 for (segments) |*segment| {
567598 const name_len = try leb.readULEB128(u32, reader);
568599 const name = try gpa.alloc(u8, name_len);
600 errdefer gpa.free(name);
569601 try reader.readNoEof(name);
570602 segment.* = .{
571603 .name = name,
......@@ -582,6 +614,7 @@ fn Parser(comptime ReaderType: type) type {
582614 },
583615 .WASM_INIT_FUNCS => {
584616 const funcs = try gpa.alloc(types.InitFunc, count);
617 errdefer gpa.free(funcs);
585618 for (funcs) |*func| {
586619 func.* = .{
587620 .priority = try leb.readULEB128(u32, reader),
......@@ -593,9 +626,11 @@ fn Parser(comptime ReaderType: type) type {
593626 },
594627 .WASM_COMDAT_INFO => {
595628 const comdats = try gpa.alloc(types.Comdat, count);
629 errdefer gpa.free(comdats);
596630 for (comdats) |*comdat| {
597631 const name_len = try leb.readULEB128(u32, reader);
598632 const name = try gpa.alloc(u8, name_len);
633 errdefer gpa.free(name);
599634 try reader.readNoEof(name);
600635
601636 const flags = try leb.readULEB128(u32, reader);
......@@ -605,6 +640,7 @@ fn Parser(comptime ReaderType: type) type {
605640
606641 const symbol_count = try leb.readULEB128(u32, reader);
607642 const symbols = try gpa.alloc(types.ComdatSym, symbol_count);
643 errdefer gpa.free(symbols);
608644 for (symbols) |*symbol| {
609645 symbol.* = .{
610646 .kind = @intToEnum(types.ComdatSym.Type, try leb.readULEB128(u8, reader)),
......@@ -664,6 +700,7 @@ fn Parser(comptime ReaderType: type) type {
664700 .data => {
665701 const name_len = try leb.readULEB128(u32, reader);
666702 const name = try gpa.allocSentinel(u8, name_len, 0);
703 errdefer gpa.free(name);
667704 try reader.readNoEof(name);
668705 symbol.name = name;
669706
......@@ -691,6 +728,7 @@ fn Parser(comptime ReaderType: type) type {
691728 if (!(is_undefined and !explicit_name)) {
692729 const name_len = try leb.readULEB128(u32, reader);
693730 const name = try gpa.allocSentinel(u8, name_len, 0);
731 errdefer gpa.free(name);
694732 try reader.readNoEof(name);
695733 symbol.name = name;
696734 } else {