authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-10 19:38:19+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-12 21:19:16+02:00
log6dbf5f1d8686c1f5b31f3dfce9b1ac0944f8e3a5
tree47480b99b6c92dd133a177f60be140d47b4c1d56
parenta01b1448e2927146cde9f7861605585f96b5084d
signaturelock-open Commit is signed but in an unrecognized format.

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.

1 files changed, 158 insertions(+), 200 deletions(-)

src/link/Wasm.zig+158-200
......@@ -653,6 +653,8 @@ fn resolveSymbolsInArchives(self: *Wasm) !void {
653653}
654654
655655fn checkUndefinedSymbols(self: *const Wasm) !void {
656 if (self.base.options.output_mode == .Obj) return;
657
656658 var found_undefined_symbols = false;
657659 for (self.undefs.values()) |undef| {
658660 const symbol = undef.getSymbol(self);
......@@ -2207,33 +2209,38 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22072209 try self.mergeTypes();
22082210 try self.setupExports();
22092211
2210 const file = self.base.file.?;
22112212 const header_size = 5 + 1;
22122213 const is_obj = self.base.options.output_mode == .Obj;
22132214
2215 var binary_bytes = std.ArrayList(u8).init(self.base.allocator);
2216 defer binary_bytes.deinit();
2217 const binary_writer = binary_bytes.writer();
2218
22142219 // We write the magic bytes at the end so they will only be written
2215 // if everything succeeded as expected.
2216 try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version)));
2217 try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version)));
2220 // if everything succeeded as expected. So populate with 0's for now.
2221 try binary_writer.writeAll(&[_]u8{0} ** 8);
22182222
22192223 // Type section
22202224 if (self.func_types.items.len != 0) {
2221 const header_offset = try reserveVecSectionHeader(file);
2222 const writer = file.writer();
2225 const header_offset = try reserveVecSectionHeader(&binary_bytes);
22232226 log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len});
22242227 for (self.func_types.items) |func_type| {
2225 try leb.writeULEB128(writer, wasm.function_type);
2226 try leb.writeULEB128(writer, @intCast(u32, func_type.params.len));
2227 for (func_type.params) |param_ty| try leb.writeULEB128(writer, wasm.valtype(param_ty));
2228 try leb.writeULEB128(writer, @intCast(u32, func_type.returns.len));
2229 for (func_type.returns) |ret_ty| try leb.writeULEB128(writer, wasm.valtype(ret_ty));
2228 try leb.writeULEB128(binary_writer, wasm.function_type);
2229 try leb.writeULEB128(binary_writer, @intCast(u32, func_type.params.len));
2230 for (func_type.params) |param_ty| {
2231 try leb.writeULEB128(binary_writer, wasm.valtype(param_ty));
2232 }
2233 try leb.writeULEB128(binary_writer, @intCast(u32, func_type.returns.len));
2234 for (func_type.returns) |ret_ty| {
2235 try leb.writeULEB128(binary_writer, wasm.valtype(ret_ty));
2236 }
22302237 }
22312238
22322239 try writeVecSectionHeader(
2233 file,
2240 binary_bytes.items,
22342241 header_offset,
22352242 .type,
2236 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2243 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
22372244 @intCast(u32, self.func_types.items.len),
22382245 );
22392246 section_count += 1;
......@@ -2243,8 +2250,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22432250 const import_memory = self.base.options.import_memory or is_obj;
22442251 const import_table = self.base.options.import_table or is_obj;
22452252 if (self.imports.count() != 0 or import_memory or import_table) {
2246 const header_offset = try reserveVecSectionHeader(file);
2247 const writer = file.writer();
2253 const header_offset = try reserveVecSectionHeader(&binary_bytes);
22482254
22492255 // import table is always first table so emit that first
22502256 if (import_table) {
......@@ -2261,14 +2267,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22612267 },
22622268 },
22632269 };
2264 try self.emitImport(writer, table_imp);
2270 try self.emitImport(binary_writer, table_imp);
22652271 }
22662272
22672273 var it = self.imports.iterator();
22682274 while (it.next()) |entry| {
22692275 assert(entry.key_ptr.*.getSymbol(self).isUndefined());
22702276 const import = entry.value_ptr.*;
2271 try self.emitImport(writer, import);
2277 try self.emitImport(binary_writer, import);
22722278 }
22732279
22742280 if (import_memory) {
......@@ -2278,14 +2284,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22782284 .name = try self.string_table.put(self.base.allocator, mem_name),
22792285 .kind = .{ .memory = self.memories.limits },
22802286 };
2281 try self.emitImport(writer, mem_imp);
2287 try self.emitImport(binary_writer, mem_imp);
22822288 }
22832289
22842290 try writeVecSectionHeader(
2285 file,
2291 binary_bytes.items,
22862292 header_offset,
22872293 .import,
2288 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2294 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
22892295 @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)),
22902296 );
22912297 section_count += 1;
......@@ -2293,17 +2299,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22932299
22942300 // Function section
22952301 if (self.functions.count() != 0) {
2296 const header_offset = try reserveVecSectionHeader(file);
2297 const writer = file.writer();
2302 const header_offset = try reserveVecSectionHeader(&binary_bytes);
22982303 for (self.functions.values()) |function| {
2299 try leb.writeULEB128(writer, function.type_index);
2304 try leb.writeULEB128(binary_writer, function.type_index);
23002305 }
23012306
23022307 try writeVecSectionHeader(
2303 file,
2308 binary_bytes.items,
23042309 header_offset,
23052310 .function,
2306 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2311 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
23072312 @intCast(u32, self.functions.count()),
23082313 );
23092314 section_count += 1;
......@@ -2312,20 +2317,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
23122317 // Table section
23132318 const export_table = self.base.options.export_table;
23142319 if (!import_table and self.function_table.count() != 0) {
2315 const header_offset = try reserveVecSectionHeader(file);
2316 const writer = file.writer();
2320 const header_offset = try reserveVecSectionHeader(&binary_bytes);
23172321
2318 try leb.writeULEB128(writer, wasm.reftype(.funcref));
2319 try emitLimits(writer, .{
2322 try leb.writeULEB128(binary_writer, wasm.reftype(.funcref));
2323 try emitLimits(binary_writer, .{
23202324 .min = @intCast(u32, self.function_table.count()) + 1,
23212325 .max = null,
23222326 });
23232327
23242328 try writeVecSectionHeader(
2325 file,
2329 binary_bytes.items,
23262330 header_offset,
23272331 .table,
2328 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2332 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
23292333 @as(u32, 1),
23302334 );
23312335 section_count += 1;
......@@ -2333,15 +2337,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
23332337
23342338 // Memory section
23352339 if (!import_memory) {
2336 const header_offset = try reserveVecSectionHeader(file);
2337 const writer = file.writer();
2340 const header_offset = try reserveVecSectionHeader(&binary_bytes);
23382341
2339 try emitLimits(writer, self.memories.limits);
2342 try emitLimits(binary_writer, self.memories.limits);
23402343 try writeVecSectionHeader(
2341 file,
2344 binary_bytes.items,
23422345 header_offset,
23432346 .memory,
2344 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2347 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
23452348 @as(u32, 1), // wasm currently only supports 1 linear memory segment
23462349 );
23472350 section_count += 1;
......@@ -2349,20 +2352,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
23492352
23502353 // Global section (used to emit stack pointer)
23512354 if (self.wasm_globals.items.len > 0) {
2352 const header_offset = try reserveVecSectionHeader(file);
2353 const writer = file.writer();
2355 const header_offset = try reserveVecSectionHeader(&binary_bytes);
23542356
23552357 for (self.wasm_globals.items) |global| {
2356 try writer.writeByte(wasm.valtype(global.global_type.valtype));
2357 try writer.writeByte(@boolToInt(global.global_type.mutable));
2358 try emitInit(writer, global.init);
2358 try binary_writer.writeByte(wasm.valtype(global.global_type.valtype));
2359 try binary_writer.writeByte(@boolToInt(global.global_type.mutable));
2360 try emitInit(binary_writer, global.init);
23592361 }
23602362
23612363 try writeVecSectionHeader(
2362 file,
2364 binary_bytes.items,
23632365 header_offset,
23642366 .global,
2365 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2367 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
23662368 @intCast(u32, self.wasm_globals.items.len),
23672369 );
23682370 section_count += 1;
......@@ -2370,35 +2372,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
23702372
23712373 // Export section
23722374 if (self.exports.items.len != 0 or export_table or !import_memory) {
2373 const header_offset = try reserveVecSectionHeader(file);
2374 const writer = file.writer();
2375 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2376
23752377 for (self.exports.items) |exp| {
23762378 const name = self.string_table.get(exp.name);
2377 try leb.writeULEB128(writer, @intCast(u32, name.len));
2378 try writer.writeAll(name);
2379 try leb.writeULEB128(writer, @enumToInt(exp.kind));
2380 try leb.writeULEB128(writer, exp.index);
2379 try leb.writeULEB128(binary_writer, @intCast(u32, name.len));
2380 try binary_writer.writeAll(name);
2381 try leb.writeULEB128(binary_writer, @enumToInt(exp.kind));
2382 try leb.writeULEB128(binary_writer, exp.index);
23812383 }
23822384
23832385 if (export_table) {
2384 try leb.writeULEB128(writer, @intCast(u32, "__indirect_function_table".len));
2385 try writer.writeAll("__indirect_function_table");
2386 try writer.writeByte(wasm.externalKind(.table));
2387 try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table
2386 try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len));
2387 try binary_writer.writeAll("__indirect_function_table");
2388 try binary_writer.writeByte(wasm.externalKind(.table));
2389 try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table
23882390 }
23892391
23902392 if (!import_memory) {
2391 try leb.writeULEB128(writer, @intCast(u32, "memory".len));
2392 try writer.writeAll("memory");
2393 try writer.writeByte(wasm.externalKind(.memory));
2394 try leb.writeULEB128(writer, @as(u32, 0));
2393 try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len));
2394 try binary_writer.writeAll("memory");
2395 try binary_writer.writeByte(wasm.externalKind(.memory));
2396 try leb.writeULEB128(binary_writer, @as(u32, 0));
23952397 }
23962398
23972399 try writeVecSectionHeader(
2398 file,
2400 binary_bytes.items,
23992401 header_offset,
24002402 .@"export",
2401 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2403 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
24022404 @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory),
24032405 );
24042406 section_count += 1;
......@@ -2406,25 +2408,24 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24062408
24072409 // element section (function table)
24082410 if (self.function_table.count() > 0) {
2409 const header_offset = try reserveVecSectionHeader(file);
2410 const writer = file.writer();
2411 const header_offset = try reserveVecSectionHeader(&binary_bytes);
24112412
24122413 var flags: u32 = 0x2; // Yes we have a table
2413 try leb.writeULEB128(writer, flags);
2414 try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
2415 try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
2416 try leb.writeULEB128(writer, @as(u8, 0));
2417 try leb.writeULEB128(writer, @intCast(u32, self.function_table.count()));
2414 try leb.writeULEB128(binary_writer, flags);
2415 try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
2416 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
2417 try leb.writeULEB128(binary_writer, @as(u8, 0));
2418 try leb.writeULEB128(binary_writer, @intCast(u32, self.function_table.count()));
24182419 var symbol_it = self.function_table.keyIterator();
24192420 while (symbol_it.next()) |symbol_loc_ptr| {
2420 try leb.writeULEB128(writer, symbol_loc_ptr.*.getSymbol(self).index);
2421 try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(self).index);
24212422 }
24222423
24232424 try writeVecSectionHeader(
2424 file,
2425 binary_bytes.items,
24252426 header_offset,
24262427 .element,
2427 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2428 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
24282429 @as(u32, 1),
24292430 );
24302431 section_count += 1;
......@@ -2433,8 +2434,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24332434 // Code section
24342435 var code_section_size: u32 = 0;
24352436 if (self.code_section_index) |code_index| {
2436 const header_offset = try reserveVecSectionHeader(file);
2437 const writer = file.writer();
2437 const header_offset = try reserveVecSectionHeader(&binary_bytes);
24382438 var atom: *Atom = self.atoms.get(code_index).?.getFirst();
24392439
24402440 // 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
24602460 std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn);
24612461
24622462 for (sorted_atoms.items) |sorted_atom| {
2463 try leb.writeULEB128(writer, sorted_atom.size);
2464 try writer.writeAll(sorted_atom.code.items);
2463 try leb.writeULEB128(binary_writer, sorted_atom.size);
2464 try binary_writer.writeAll(sorted_atom.code.items);
24652465 }
24662466
2467 code_section_size = @intCast(u32, (try file.getPos()) - header_offset - header_size);
2467 code_section_size = @intCast(u32, binary_bytes.items.len - header_offset - header_size);
24682468 try writeVecSectionHeader(
2469 file,
2469 binary_bytes.items,
24702470 header_offset,
24712471 .code,
24722472 code_section_size,
......@@ -2478,8 +2478,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24782478
24792479 // Data section
24802480 if (self.data_segments.count() != 0) {
2481 const header_offset = try reserveVecSectionHeader(file);
2482 const writer = file.writer();
2481 const header_offset = try reserveVecSectionHeader(&binary_bytes);
24832482
24842483 var it = self.data_segments.iterator();
24852484 var segment_count: u32 = 0;
......@@ -2493,10 +2492,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24932492 const segment = self.segments.items[atom_index];
24942493
24952494 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
2496 try leb.writeULEB128(writer, @as(u32, 0));
2495 try leb.writeULEB128(binary_writer, @as(u32, 0));
24972496 // offset into data section
2498 try emitInit(writer, .{ .i32_const = @bitCast(i32, segment.offset) });
2499 try leb.writeULEB128(writer, segment.size);
2497 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
2498 try leb.writeULEB128(binary_writer, segment.size);
25002499
25012500 // fill in the offset table and the data segments
25022501 var current_offset: u32 = 0;
......@@ -2508,12 +2507,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25082507 // Pad with zeroes to ensure all segments are aligned
25092508 if (current_offset != atom.offset) {
25102509 const diff = atom.offset - current_offset;
2511 try writer.writeByteNTimes(0, diff);
2510 try binary_writer.writeByteNTimes(0, diff);
25122511 current_offset += diff;
25132512 }
25142513 assert(current_offset == atom.offset);
25152514 assert(atom.code.items.len == atom.size);
2516 try writer.writeAll(atom.code.items);
2515 try binary_writer.writeAll(atom.code.items);
25172516
25182517 current_offset += atom.size;
25192518 if (atom.next) |next| {
......@@ -2522,7 +2521,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25222521 // also pad with zeroes when last atom to ensure
25232522 // segments are aligned.
25242523 if (current_offset != segment.size) {
2525 try writer.writeByteNTimes(0, segment.size - current_offset);
2524 try binary_writer.writeByteNTimes(0, segment.size - current_offset);
25262525 current_offset += segment.size - current_offset;
25272526 }
25282527 break;
......@@ -2532,10 +2531,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25322531 }
25332532
25342533 try writeVecSectionHeader(
2535 file,
2534 binary_bytes.items,
25362535 header_offset,
25372536 .data,
2538 @intCast(u32, (try file.getPos()) - header_offset - header_size),
2537 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
25392538 @intCast(u32, segment_count),
25402539 );
25412540 data_section_index = section_count;
......@@ -2547,12 +2546,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25472546 // we never store all symbols in a single table, but store a location reference instead.
25482547 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
25492548 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
2550 try self.emitLinkSection(file, arena, &symbol_table);
2549 try self.emitLinkSection(&binary_bytes, &symbol_table);
25512550 if (code_section_index) |code_index| {
2552 try self.emitCodeRelocations(file, arena, code_index, symbol_table);
2551 try self.emitCodeRelocations(&binary_bytes, code_index, symbol_table);
25532552 }
25542553 if (data_section_index) |data_index| {
2555 try self.emitDataRelocations(file, arena, data_index, symbol_table);
2554 try self.emitDataRelocations(&binary_bytes, data_index, symbol_table);
25562555 }
25572556 } else if (!self.base.options.strip) {
25582557 if (self.dwarf) |*dwarf| {
......@@ -2592,41 +2591,45 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25922591 try debug_bytes.appendSlice(atom.code.items);
25932592 atom = atom.next orelse break;
25942593 }
2595 try emitDebugSection(file, debug_bytes.items, item.name);
2594 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
25962595 debug_bytes.clearRetainingCapacity();
25972596 }
25982597 }
2599 try self.emitNameSection(file, arena);
2598 try self.emitNameSection(&binary_bytes, arena);
26002599 }
26012600
26022601 // Only when writing all sections executed properly we write the magic
26032602 // bytes. This allows us to easily detect what went wrong while generating
26042603 // the final binary.
2605 try file.pwriteAll(&(wasm.magic ++ wasm.version), 0);
2604 mem.copy(u8, binary_bytes.items, &(wasm.magic ++ wasm.version));
2605
2606 // finally, write the entire binary into the file.
2607 var iovec = [_]std.os.iovec_const{.{
2608 .iov_base = binary_bytes.items.ptr,
2609 .iov_len = binary_bytes.items.len,
2610 }};
2611 try self.base.file.?.writevAll(&iovec);
26062612}
26072613
2608fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2614fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {
26092615 if (data.len == 0) return;
2610 const header_offset = try reserveCustomSectionHeader(file);
2611 const writer = file.writer();
2616 const header_offset = try reserveCustomSectionHeader(binary_bytes);
2617 const writer = binary_bytes.writer();
26122618 try leb.writeULEB128(writer, @intCast(u32, name.len));
26132619 try writer.writeAll(name);
26142620
2615 try file.writevAll(&[_]std.os.iovec_const{.{
2616 .iov_base = data.ptr,
2617 .iov_len = data.len,
2618 }});
2619 const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len));
2621 const start = binary_bytes.items.len - header_offset;
26202622 log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
2623 try writer.writeAll(data);
26212624
26222625 try writeCustomSectionHeader(
2623 file,
2626 binary_bytes.items,
26242627 header_offset,
2625 @intCast(u32, (try file.getPos()) - header_offset - 6),
2628 @intCast(u32, binary_bytes.items.len - header_offset - 6),
26262629 );
26272630}
26282631
2629fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
2632fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
26302633 const Name = struct {
26312634 index: u32,
26322635 name: []const u8,
......@@ -2672,8 +2675,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
26722675 std.sort.sort(Name, funcs.values(), {}, Name.lessThan);
26732676 std.sort.sort(Name, globals.items, {}, Name.lessThan);
26742677
2675 const header_offset = try reserveCustomSectionHeader(file);
2676 const writer = file.writer();
2678 const header_offset = try reserveCustomSectionHeader(binary_bytes);
2679 const writer = binary_bytes.writer();
26772680 try leb.writeULEB128(writer, @intCast(u32, "name".len));
26782681 try writer.writeAll("name");
26792682
......@@ -2682,9 +2685,9 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
26822685 try self.emitNameSubsection(.data_segment, segments.items, writer);
26832686
26842687 try writeCustomSectionHeader(
2685 file,
2688 binary_bytes.items,
26862689 header_offset,
2687 @intCast(u32, (try file.getPos()) - header_offset - 6),
2690 @intCast(u32, binary_bytes.items.len - header_offset - 6),
26882691 );
26892692}
26902693
......@@ -3197,54 +3200,40 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
31973200 }
31983201}
31993202
3200fn reserveVecSectionHeader(file: fs.File) !u64 {
3203fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
32013204 // section id + fixed leb contents size + fixed leb vector length
32023205 const header_size = 1 + 5 + 5;
3203 // TODO: this should be a single lseek(2) call, but fs.File does not
3204 // currently provide a way to do this.
3205 try file.seekBy(header_size);
3206 return (try file.getPos()) - header_size;
3206 const offset = @intCast(u32, bytes.items.len);
3207 try bytes.appendSlice(&[_]u8{0} ** header_size);
3208 return offset;
32073209}
32083210
3209fn reserveCustomSectionHeader(file: fs.File) !u64 {
3211fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 {
32103212 // unlike regular section, we don't emit the count
32113213 const header_size = 1 + 5;
3212 // TODO: this should be a single lseek(2) call, but fs.File does not
3213 // currently provide a way to do this.
3214 try file.seekBy(header_size);
3215 return (try file.getPos()) - header_size;
3214 const offset = @intCast(u32, bytes.items.len);
3215 try bytes.appendSlice(&[_]u8{0} ** header_size);
3216 return offset;
32163217}
32173218
3218fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void {
3219fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: u32, items: u32) !void {
32193220 var buf: [1 + 5 + 5]u8 = undefined;
32203221 buf[0] = @enumToInt(section);
32213222 leb.writeUnsignedFixed(5, buf[1..6], size);
32223223 leb.writeUnsignedFixed(5, buf[6..], items);
3223
3224 if (builtin.target.os.tag == .windows) {
3225 // https://github.com/ziglang/zig/issues/12783
3226 const curr_pos = try file.getPos();
3227 try file.pwriteAll(&buf, offset);
3228 try file.seekTo(curr_pos);
3229 } else try file.pwriteAll(&buf, offset);
3224 mem.copy(u8, buffer[offset..], &buf);
32303225}
32313226
3232fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
3227fn writeCustomSectionHeader(buffer: []u8, offset: u64, size: u32) !void {
32333228 var buf: [1 + 5]u8 = undefined;
32343229 buf[0] = 0; // 0 = 'custom' section
32353230 leb.writeUnsignedFixed(5, buf[1..6], size);
3236
3237 if (builtin.target.os.tag == .windows) {
3238 // https://github.com/ziglang/zig/issues/12783
3239 const curr_pos = try file.getPos();
3240 try file.pwriteAll(&buf, offset);
3241 try file.seekTo(curr_pos);
3242 } else try file.pwriteAll(&buf, offset);
3231 mem.copy(u8, buffer[offset..], &buf);
32433232}
32443233
3245fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3246 const offset = try reserveCustomSectionHeader(file);
3247 const writer = file.writer();
3234fn emitLinkSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3235 const offset = try reserveCustomSectionHeader(binary_bytes);
3236 const writer = binary_bytes.writer();
32483237 // emit "linking" custom section name
32493238 const section_name = "linking";
32503239 try leb.writeULEB128(writer, section_name.len);
......@@ -3255,21 +3244,18 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *
32553244
32563245 // For each subsection type (found in types.Subsection) we can emit a section.
32573246 // Currently, we only support emitting segment info and the symbol table.
3258 try self.emitSymbolTable(file, arena, symbol_table);
3259 try self.emitSegmentInfo(file, arena);
3247 try self.emitSymbolTable(binary_bytes, symbol_table);
3248 try self.emitSegmentInfo(binary_bytes);
32603249
3261 const size = @intCast(u32, (try file.getPos()) - offset - 6);
3262 try writeCustomSectionHeader(file, offset, size);
3250 const size = @intCast(u32, binary_bytes.items.len - offset - 6);
3251 try writeCustomSectionHeader(binary_bytes.items, offset, size);
32633252}
32643253
3265fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3266 // After emitting the subtype, we must emit the subsection's length
3267 // so first write it to a temporary arraylist to calculate the length
3268 // and then write all data at once.
3269 var payload = std.ArrayList(u8).init(arena);
3270 const writer = payload.writer();
3254fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3255 const writer = binary_bytes.writer();
32713256
3272 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE));
3257 try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE));
3258 const table_offset = binary_bytes.items.len;
32733259
32743260 var symbol_count: u32 = 0;
32753261 for (self.resolved_symbols.keys()) |sym_loc| {
......@@ -3307,23 +3293,17 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *
33073293 }
33083294 }
33093295
3310 var buf: [5]u8 = undefined;
3311 leb.writeUnsignedFixed(5, &buf, symbol_count);
3312 try payload.insertSlice(0, &buf);
3313 try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len));
3314
3315 const iovec: std.os.iovec_const = .{
3316 .iov_base = payload.items.ptr,
3317 .iov_len = payload.items.len,
3318 };
3319 var iovecs = [_]std.os.iovec_const{iovec};
3320 try file.writevAll(&iovecs);
3296 var buf: [10]u8 = undefined;
3297 leb.writeUnsignedFixed(5, buf[0..5], @intCast(u32, binary_bytes.items.len - table_offset + 5));
3298 leb.writeUnsignedFixed(5, buf[5..], symbol_count);
3299 try binary_bytes.insertSlice(table_offset, &buf);
33213300}
33223301
3323fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {
3324 var payload = std.ArrayList(u8).init(arena);
3325 const writer = payload.writer();
3326 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO));
3302fn emitSegmentInfo(self: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
3303 const writer = binary_bytes.writer();
3304 try leb.writeULEB128(writer, @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO));
3305 const segment_offset = binary_bytes.items.len;
3306
33273307 try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count()));
33283308 for (self.segment_info.values()) |segment_info| {
33293309 log.debug("Emit segment: {s} align({d}) flags({b})", .{
......@@ -3337,13 +3317,9 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {
33373317 try leb.writeULEB128(writer, segment_info.flags);
33383318 }
33393319
3340 try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len));
3341 const iovec: std.os.iovec_const = .{
3342 .iov_base = payload.items.ptr,
3343 .iov_len = payload.items.len,
3344 };
3345 var iovecs = [_]std.os.iovec_const{iovec};
3346 try file.writevAll(&iovecs);
3320 var buf: [5]u8 = undefined;
3321 leb.writeUnsignedFixed(5, &buf, @intCast(u32, binary_bytes.items.len - segment_offset));
3322 try binary_bytes.insertSlice(segment_offset, &buf);
33473323}
33483324
33493325pub fn getULEB128Size(uint_value: anytype) u32 {
......@@ -3361,21 +3337,20 @@ pub fn getULEB128Size(uint_value: anytype) u32 {
33613337/// For each relocatable section, emits a custom "relocation.<section_name>" section
33623338fn emitCodeRelocations(
33633339 self: *Wasm,
3364 file: fs.File,
3365 arena: Allocator,
3340 binary_bytes: *std.ArrayList(u8),
33663341 section_index: u32,
33673342 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
33683343) !void {
33693344 const code_index = self.code_section_index orelse return;
3370 var payload = std.ArrayList(u8).init(arena);
3371 const writer = payload.writer();
3345 const writer = binary_bytes.writer();
3346 const header_offset = try reserveCustomSectionHeader(binary_bytes);
33723347
33733348 // write custom section information
33743349 const name = "reloc.CODE";
33753350 try leb.writeULEB128(writer, @intCast(u32, name.len));
33763351 try writer.writeAll(name);
33773352 try leb.writeULEB128(writer, section_index);
3378 const reloc_start = payload.items.len;
3353 const reloc_start = binary_bytes.items.len;
33793354
33803355 var count: u32 = 0;
33813356 var atom: *Atom = self.atoms.get(code_index).?.getFirst();
......@@ -3401,36 +3376,27 @@ fn emitCodeRelocations(
34013376 if (count == 0) return;
34023377 var buf: [5]u8 = undefined;
34033378 leb.writeUnsignedFixed(5, &buf, count);
3404 try payload.insertSlice(reloc_start, &buf);
3405 var iovecs = [_]std.os.iovec_const{
3406 .{
3407 .iov_base = payload.items.ptr,
3408 .iov_len = payload.items.len,
3409 },
3410 };
3411 const header_offset = try reserveCustomSectionHeader(file);
3412 try file.writevAll(&iovecs);
3413 const size = @intCast(u32, payload.items.len);
3414 try writeCustomSectionHeader(file, header_offset, size);
3379 try binary_bytes.insertSlice(reloc_start, &buf);
3380 const size = @intCast(u32, binary_bytes.items.len - header_offset - 6);
3381 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
34153382}
34163383
34173384fn emitDataRelocations(
34183385 self: *Wasm,
3419 file: fs.File,
3420 arena: Allocator,
3386 binary_bytes: *std.ArrayList(u8),
34213387 section_index: u32,
34223388 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
34233389) !void {
34243390 if (self.data_segments.count() == 0) return;
3425 var payload = std.ArrayList(u8).init(arena);
3426 const writer = payload.writer();
3391 const writer = binary_bytes.writer();
3392 const header_offset = try reserveCustomSectionHeader(binary_bytes);
34273393
34283394 // write custom section information
34293395 const name = "reloc.DATA";
34303396 try leb.writeULEB128(writer, @intCast(u32, name.len));
34313397 try writer.writeAll(name);
34323398 try leb.writeULEB128(writer, section_index);
3433 const reloc_start = payload.items.len;
3399 const reloc_start = binary_bytes.items.len;
34343400
34353401 var count: u32 = 0;
34363402 // for each atom, we calculate the uleb size and append that
......@@ -3462,17 +3428,9 @@ fn emitDataRelocations(
34623428
34633429 var buf: [5]u8 = undefined;
34643430 leb.writeUnsignedFixed(5, &buf, count);
3465 try payload.insertSlice(reloc_start, &buf);
3466 var iovecs = [_]std.os.iovec_const{
3467 .{
3468 .iov_base = payload.items.ptr,
3469 .iov_len = payload.items.len,
3470 },
3471 };
3472 const header_offset = try reserveCustomSectionHeader(file);
3473 try file.writevAll(&iovecs);
3474 const size = @intCast(u32, payload.items.len);
3475 try writeCustomSectionHeader(file, header_offset, size);
3431 try binary_bytes.insertSlice(reloc_start, &buf);
3432 const size = @intCast(u32, binary_bytes.items.len - header_offset - 6);
3433 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
34763434}
34773435
34783436/// Searches for an a matching function signature, when not found