From 6dbf5f1d8686c1f5b31f3dfce9b1ac0944f8e3a5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 10 Sep 2022 19:38:19 +0200 Subject: [PATCH] wasm-linker: write to file at once Rather than writing to the file using a writer, we now first write to an arraylist and store the binary in memory. Once the full binary data was written, we write all data to disk at once. This reduces the amount of syscalls tremendously, increasing the performance of the linker in exchange for increased memory usage during flush. --- src/link/Wasm.zig | 358 ++++++++++++++++++++-------------------------- 1 file changed, 158 insertions(+), 200 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 6ff554de18..10df96499d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -653,6 +653,8 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { } fn checkUndefinedSymbols(self: *const Wasm) !void { + if (self.base.options.output_mode == .Obj) return; + var found_undefined_symbols = false; for (self.undefs.values()) |undef| { const symbol = undef.getSymbol(self); @@ -2207,33 +2209,38 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try self.mergeTypes(); try self.setupExports(); - const file = self.base.file.?; const header_size = 5 + 1; const is_obj = self.base.options.output_mode == .Obj; + var binary_bytes = std.ArrayList(u8).init(self.base.allocator); + defer binary_bytes.deinit(); + const binary_writer = binary_bytes.writer(); + // We write the magic bytes at the end so they will only be written - // if everything succeeded as expected. - try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); - try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); + // if everything succeeded as expected. So populate with 0's for now. + try binary_writer.writeAll(&[_]u8{0} ** 8); // Type section if (self.func_types.items.len != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); for (self.func_types.items) |func_type| { - try leb.writeULEB128(writer, wasm.function_type); - try leb.writeULEB128(writer, @intCast(u32, func_type.params.len)); - for (func_type.params) |param_ty| try leb.writeULEB128(writer, wasm.valtype(param_ty)); - try leb.writeULEB128(writer, @intCast(u32, func_type.returns.len)); - for (func_type.returns) |ret_ty| try leb.writeULEB128(writer, wasm.valtype(ret_ty)); + try leb.writeULEB128(binary_writer, wasm.function_type); + try leb.writeULEB128(binary_writer, @intCast(u32, func_type.params.len)); + for (func_type.params) |param_ty| { + try leb.writeULEB128(binary_writer, wasm.valtype(param_ty)); + } + try leb.writeULEB128(binary_writer, @intCast(u32, func_type.returns.len)); + for (func_type.returns) |ret_ty| { + try leb.writeULEB128(binary_writer, wasm.valtype(ret_ty)); + } } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .type, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.func_types.items.len), ); section_count += 1; @@ -2243,8 +2250,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const import_memory = self.base.options.import_memory or is_obj; const import_table = self.base.options.import_table or is_obj; if (self.imports.count() != 0 or import_memory or import_table) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); // import table is always first table so emit that first if (import_table) { @@ -2261,14 +2267,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod }, }, }; - try self.emitImport(writer, table_imp); + try self.emitImport(binary_writer, table_imp); } var it = self.imports.iterator(); while (it.next()) |entry| { assert(entry.key_ptr.*.getSymbol(self).isUndefined()); const import = entry.value_ptr.*; - try self.emitImport(writer, import); + try self.emitImport(binary_writer, import); } if (import_memory) { @@ -2278,14 +2284,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod .name = try self.string_table.put(self.base.allocator, mem_name), .kind = .{ .memory = self.memories.limits }, }; - try self.emitImport(writer, mem_imp); + try self.emitImport(binary_writer, mem_imp); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .import, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), ); section_count += 1; @@ -2293,17 +2299,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Function section if (self.functions.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); for (self.functions.values()) |function| { - try leb.writeULEB128(writer, function.type_index); + try leb.writeULEB128(binary_writer, function.type_index); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .function, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.functions.count()), ); section_count += 1; @@ -2312,20 +2317,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Table section const export_table = self.base.options.export_table; if (!import_table and self.function_table.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); - try leb.writeULEB128(writer, wasm.reftype(.funcref)); - try emitLimits(writer, .{ + try leb.writeULEB128(binary_writer, wasm.reftype(.funcref)); + try emitLimits(binary_writer, .{ .min = @intCast(u32, self.function_table.count()) + 1, .max = null, }); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .table, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), ); section_count += 1; @@ -2333,15 +2337,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Memory section if (!import_memory) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); - try emitLimits(writer, self.memories.limits); + try emitLimits(binary_writer, self.memories.limits); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .memory, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), // wasm currently only supports 1 linear memory segment ); section_count += 1; @@ -2349,20 +2352,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Global section (used to emit stack pointer) if (self.wasm_globals.items.len > 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); for (self.wasm_globals.items) |global| { - try writer.writeByte(wasm.valtype(global.global_type.valtype)); - try writer.writeByte(@boolToInt(global.global_type.mutable)); - try emitInit(writer, global.init); + try binary_writer.writeByte(wasm.valtype(global.global_type.valtype)); + try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); + try emitInit(binary_writer, global.init); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .global, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.wasm_globals.items.len), ); section_count += 1; @@ -2370,35 +2372,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Export section if (self.exports.items.len != 0 or export_table or !import_memory) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); + for (self.exports.items) |exp| { const name = self.string_table.get(exp.name); - try leb.writeULEB128(writer, @intCast(u32, name.len)); - try writer.writeAll(name); - try leb.writeULEB128(writer, @enumToInt(exp.kind)); - try leb.writeULEB128(writer, exp.index); + try leb.writeULEB128(binary_writer, @intCast(u32, name.len)); + try binary_writer.writeAll(name); + try leb.writeULEB128(binary_writer, @enumToInt(exp.kind)); + try leb.writeULEB128(binary_writer, exp.index); } if (export_table) { - try leb.writeULEB128(writer, @intCast(u32, "__indirect_function_table".len)); - try writer.writeAll("__indirect_function_table"); - try writer.writeByte(wasm.externalKind(.table)); - try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table + try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len)); + try binary_writer.writeAll("__indirect_function_table"); + try binary_writer.writeByte(wasm.externalKind(.table)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table } if (!import_memory) { - try leb.writeULEB128(writer, @intCast(u32, "memory".len)); - try writer.writeAll("memory"); - try writer.writeByte(wasm.externalKind(.memory)); - try leb.writeULEB128(writer, @as(u32, 0)); + try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len)); + try binary_writer.writeAll("memory"); + try binary_writer.writeByte(wasm.externalKind(.memory)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .@"export", - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), ); section_count += 1; @@ -2406,25 +2408,24 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // element section (function table) if (self.function_table.count() > 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var flags: u32 = 0x2; // Yes we have a table - try leb.writeULEB128(writer, flags); - try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols - try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid - try leb.writeULEB128(writer, @as(u8, 0)); - try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); + try leb.writeULEB128(binary_writer, flags); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols + try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid + try leb.writeULEB128(binary_writer, @as(u8, 0)); + try leb.writeULEB128(binary_writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator(); while (symbol_it.next()) |symbol_loc_ptr| { - try leb.writeULEB128(writer, symbol_loc_ptr.*.getSymbol(self).index); + try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(self).index); } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .element, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @as(u32, 1), ); section_count += 1; @@ -2433,8 +2434,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Code section var code_section_size: u32 = 0; if (self.code_section_index) |code_index| { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var atom: *Atom = self.atoms.get(code_index).?.getFirst(); // The code section must be sorted in line with the function order. @@ -2460,13 +2460,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn); for (sorted_atoms.items) |sorted_atom| { - try leb.writeULEB128(writer, sorted_atom.size); - try writer.writeAll(sorted_atom.code.items); + try leb.writeULEB128(binary_writer, sorted_atom.size); + try binary_writer.writeAll(sorted_atom.code.items); } - code_section_size = @intCast(u32, (try file.getPos()) - header_offset - header_size); + code_section_size = @intCast(u32, binary_bytes.items.len - header_offset - header_size); try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .code, code_section_size, @@ -2478,8 +2478,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Data section if (self.data_segments.count() != 0) { - const header_offset = try reserveVecSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveVecSectionHeader(&binary_bytes); var it = self.data_segments.iterator(); var segment_count: u32 = 0; @@ -2493,10 +2492,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const segment = self.segments.items[atom_index]; // flag and index to memory section (currently, there can only be 1 memory section in wasm) - try leb.writeULEB128(writer, @as(u32, 0)); + try leb.writeULEB128(binary_writer, @as(u32, 0)); // offset into data section - try emitInit(writer, .{ .i32_const = @bitCast(i32, segment.offset) }); - try leb.writeULEB128(writer, segment.size); + try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) }); + try leb.writeULEB128(binary_writer, segment.size); // fill in the offset table and the data segments var current_offset: u32 = 0; @@ -2508,12 +2507,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // Pad with zeroes to ensure all segments are aligned if (current_offset != atom.offset) { const diff = atom.offset - current_offset; - try writer.writeByteNTimes(0, diff); + try binary_writer.writeByteNTimes(0, diff); current_offset += diff; } assert(current_offset == atom.offset); assert(atom.code.items.len == atom.size); - try writer.writeAll(atom.code.items); + try binary_writer.writeAll(atom.code.items); current_offset += atom.size; if (atom.next) |next| { @@ -2522,7 +2521,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // also pad with zeroes when last atom to ensure // segments are aligned. if (current_offset != segment.size) { - try writer.writeByteNTimes(0, segment.size - current_offset); + try binary_writer.writeByteNTimes(0, segment.size - current_offset); current_offset += segment.size - current_offset; } break; @@ -2532,10 +2531,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try writeVecSectionHeader( - file, + binary_bytes.items, header_offset, .data, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, binary_bytes.items.len - header_offset - header_size), @intCast(u32, segment_count), ); data_section_index = section_count; @@ -2547,12 +2546,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // we never store all symbols in a single table, but store a location reference instead. // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); - try self.emitLinkSection(file, arena, &symbol_table); + try self.emitLinkSection(&binary_bytes, &symbol_table); if (code_section_index) |code_index| { - try self.emitCodeRelocations(file, arena, code_index, symbol_table); + try self.emitCodeRelocations(&binary_bytes, code_index, symbol_table); } if (data_section_index) |data_index| { - try self.emitDataRelocations(file, arena, data_index, symbol_table); + try self.emitDataRelocations(&binary_bytes, data_index, symbol_table); } } else if (!self.base.options.strip) { if (self.dwarf) |*dwarf| { @@ -2592,41 +2591,45 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try debug_bytes.appendSlice(atom.code.items); atom = atom.next orelse break; } - try emitDebugSection(file, debug_bytes.items, item.name); + try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); debug_bytes.clearRetainingCapacity(); } } - try self.emitNameSection(file, arena); + try self.emitNameSection(&binary_bytes, arena); } // Only when writing all sections executed properly we write the magic // bytes. This allows us to easily detect what went wrong while generating // the final binary. - try file.pwriteAll(&(wasm.magic ++ wasm.version), 0); + mem.copy(u8, binary_bytes.items, &(wasm.magic ++ wasm.version)); + + // finally, write the entire binary into the file. + var iovec = [_]std.os.iovec_const{.{ + .iov_base = binary_bytes.items.ptr, + .iov_len = binary_bytes.items.len, + }}; + try self.base.file.?.writevAll(&iovec); } -fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { +fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void { if (data.len == 0) return; - const header_offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); - try file.writevAll(&[_]std.os.iovec_const{.{ - .iov_base = data.ptr, - .iov_len = data.len, - }}); - const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len)); + const start = binary_bytes.items.len - header_offset; log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len }); + try writer.writeAll(data); try writeCustomSectionHeader( - file, + binary_bytes.items, header_offset, - @intCast(u32, (try file.getPos()) - header_offset - 6), + @intCast(u32, binary_bytes.items.len - header_offset - 6), ); } -fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { +fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { const Name = struct { index: u32, name: []const u8, @@ -2672,8 +2675,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { std.sort.sort(Name, funcs.values(), {}, Name.lessThan); std.sort.sort(Name, globals.items, {}, Name.lessThan); - const header_offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); try leb.writeULEB128(writer, @intCast(u32, "name".len)); try writer.writeAll("name"); @@ -2682,9 +2685,9 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { try self.emitNameSubsection(.data_segment, segments.items, writer); try writeCustomSectionHeader( - file, + binary_bytes.items, header_offset, - @intCast(u32, (try file.getPos()) - header_offset - 6), + @intCast(u32, binary_bytes.items.len - header_offset - 6), ); } @@ -3197,54 +3200,40 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } -fn reserveVecSectionHeader(file: fs.File) !u64 { +fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { // section id + fixed leb contents size + fixed leb vector length const header_size = 1 + 5 + 5; - // TODO: this should be a single lseek(2) call, but fs.File does not - // currently provide a way to do this. - try file.seekBy(header_size); - return (try file.getPos()) - header_size; + const offset = @intCast(u32, bytes.items.len); + try bytes.appendSlice(&[_]u8{0} ** header_size); + return offset; } -fn reserveCustomSectionHeader(file: fs.File) !u64 { +fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 { // unlike regular section, we don't emit the count const header_size = 1 + 5; - // TODO: this should be a single lseek(2) call, but fs.File does not - // currently provide a way to do this. - try file.seekBy(header_size); - return (try file.getPos()) - header_size; + const offset = @intCast(u32, bytes.items.len); + try bytes.appendSlice(&[_]u8{0} ** header_size); + return offset; } -fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void { +fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: u32, items: u32) !void { var buf: [1 + 5 + 5]u8 = undefined; buf[0] = @enumToInt(section); leb.writeUnsignedFixed(5, buf[1..6], size); leb.writeUnsignedFixed(5, buf[6..], items); - - if (builtin.target.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12783 - const curr_pos = try file.getPos(); - try file.pwriteAll(&buf, offset); - try file.seekTo(curr_pos); - } else try file.pwriteAll(&buf, offset); + mem.copy(u8, buffer[offset..], &buf); } -fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { +fn writeCustomSectionHeader(buffer: []u8, offset: u64, size: u32) !void { var buf: [1 + 5]u8 = undefined; buf[0] = 0; // 0 = 'custom' section leb.writeUnsignedFixed(5, buf[1..6], size); - - if (builtin.target.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12783 - const curr_pos = try file.getPos(); - try file.pwriteAll(&buf, offset); - try file.seekTo(curr_pos); - } else try file.pwriteAll(&buf, offset); + mem.copy(u8, buffer[offset..], &buf); } -fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { - const offset = try reserveCustomSectionHeader(file); - const writer = file.writer(); +fn emitLinkSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { + const offset = try reserveCustomSectionHeader(binary_bytes); + const writer = binary_bytes.writer(); // emit "linking" custom section name const section_name = "linking"; try leb.writeULEB128(writer, section_name.len); @@ -3255,21 +3244,18 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * // For each subsection type (found in types.Subsection) we can emit a section. // Currently, we only support emitting segment info and the symbol table. - try self.emitSymbolTable(file, arena, symbol_table); - try self.emitSegmentInfo(file, arena); + try self.emitSymbolTable(binary_bytes, symbol_table); + try self.emitSegmentInfo(binary_bytes); - const size = @intCast(u32, (try file.getPos()) - offset - 6); - try writeCustomSectionHeader(file, offset, size); + const size = @intCast(u32, binary_bytes.items.len - offset - 6); + try writeCustomSectionHeader(binary_bytes.items, offset, size); } -fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { - // After emitting the subtype, we must emit the subsection's length - // so first write it to a temporary arraylist to calculate the length - // and then write all data at once. - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); +fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { + const writer = binary_bytes.writer(); - try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE)); + try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE)); + const table_offset = binary_bytes.items.len; var symbol_count: u32 = 0; for (self.resolved_symbols.keys()) |sym_loc| { @@ -3307,23 +3293,17 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * } } - var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, symbol_count); - try payload.insertSlice(0, &buf); - try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len)); - - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }; - var iovecs = [_]std.os.iovec_const{iovec}; - try file.writevAll(&iovecs); + var buf: [10]u8 = undefined; + leb.writeUnsignedFixed(5, buf[0..5], @intCast(u32, binary_bytes.items.len - table_offset + 5)); + leb.writeUnsignedFixed(5, buf[5..], symbol_count); + try binary_bytes.insertSlice(table_offset, &buf); } -fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); - try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); +fn emitSegmentInfo(self: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { + const writer = binary_bytes.writer(); + try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); + const segment_offset = binary_bytes.items.len; + try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); for (self.segment_info.values()) |segment_info| { log.debug("Emit segment: {s} align({d}) flags({b})", .{ @@ -3337,13 +3317,9 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { try leb.writeULEB128(writer, segment_info.flags); } - try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len)); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }; - var iovecs = [_]std.os.iovec_const{iovec}; - try file.writevAll(&iovecs); + var buf: [5]u8 = undefined; + leb.writeUnsignedFixed(5, &buf, @intCast(u32, binary_bytes.items.len - segment_offset)); + try binary_bytes.insertSlice(segment_offset, &buf); } pub fn getULEB128Size(uint_value: anytype) u32 { @@ -3361,21 +3337,20 @@ pub fn getULEB128Size(uint_value: anytype) u32 { /// For each relocatable section, emits a custom "relocation." section fn emitCodeRelocations( self: *Wasm, - file: fs.File, - arena: Allocator, + binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { const code_index = self.code_section_index orelse return; - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); + const writer = binary_bytes.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); // write custom section information const name = "reloc.CODE"; try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); try leb.writeULEB128(writer, section_index); - const reloc_start = payload.items.len; + const reloc_start = binary_bytes.items.len; var count: u32 = 0; var atom: *Atom = self.atoms.get(code_index).?.getFirst(); @@ -3401,36 +3376,27 @@ fn emitCodeRelocations( if (count == 0) return; var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); - try payload.insertSlice(reloc_start, &buf); - var iovecs = [_]std.os.iovec_const{ - .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }, - }; - const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&iovecs); - const size = @intCast(u32, payload.items.len); - try writeCustomSectionHeader(file, header_offset, size); + try binary_bytes.insertSlice(reloc_start, &buf); + const size = @intCast(u32, binary_bytes.items.len - header_offset - 6); + try writeCustomSectionHeader(binary_bytes.items, header_offset, size); } fn emitDataRelocations( self: *Wasm, - file: fs.File, - arena: Allocator, + binary_bytes: *std.ArrayList(u8), section_index: u32, symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), ) !void { if (self.data_segments.count() == 0) return; - var payload = std.ArrayList(u8).init(arena); - const writer = payload.writer(); + const writer = binary_bytes.writer(); + const header_offset = try reserveCustomSectionHeader(binary_bytes); // write custom section information const name = "reloc.DATA"; try leb.writeULEB128(writer, @intCast(u32, name.len)); try writer.writeAll(name); try leb.writeULEB128(writer, section_index); - const reloc_start = payload.items.len; + const reloc_start = binary_bytes.items.len; var count: u32 = 0; // for each atom, we calculate the uleb size and append that @@ -3462,17 +3428,9 @@ fn emitDataRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); - try payload.insertSlice(reloc_start, &buf); - var iovecs = [_]std.os.iovec_const{ - .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, - }, - }; - const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&iovecs); - const size = @intCast(u32, payload.items.len); - try writeCustomSectionHeader(file, header_offset, size); + try binary_bytes.insertSlice(reloc_start, &buf); + const size = @intCast(u32, binary_bytes.items.len - header_offset - 6); + try writeCustomSectionHeader(binary_bytes.items, header_offset, size); } /// Searches for an a matching function signature, when not found