| ... | ... | @@ -653,6 +653,8 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { |
| 653 | 653 | } |
| 654 | 654 | |
| 655 | 655 | fn checkUndefinedSymbols(self: *const Wasm) !void { |
| 656 | if (self.base.options.output_mode == .Obj) return; |
| 657 | |
| 656 | 658 | var found_undefined_symbols = false; |
| 657 | 659 | for (self.undefs.values()) |undef| { |
| 658 | 660 | const symbol = undef.getSymbol(self); |
| ... | ... | @@ -2207,33 +2209,38 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2207 | 2209 | try self.mergeTypes(); |
| 2208 | 2210 | try self.setupExports(); |
| 2209 | 2211 | |
| 2210 | | const file = self.base.file.?; |
| 2211 | 2212 | const header_size = 5 + 1; |
| 2212 | 2213 | const is_obj = self.base.options.output_mode == .Obj; |
| 2213 | 2214 | |
| 2215 | var binary_bytes = std.ArrayList(u8).init(self.base.allocator); |
| 2216 | defer binary_bytes.deinit(); |
| 2217 | const binary_writer = binary_bytes.writer(); |
| 2218 | |
| 2214 | 2219 | // 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); |
| 2218 | 2222 | |
| 2219 | 2223 | // Type section |
| 2220 | 2224 | 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); |
| 2223 | 2226 | log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); |
| 2224 | 2227 | 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 | } |
| 2230 | 2237 | } |
| 2231 | 2238 | |
| 2232 | 2239 | try writeVecSectionHeader( |
| 2233 | | file, |
| 2240 | binary_bytes.items, |
| 2234 | 2241 | header_offset, |
| 2235 | 2242 | .type, |
| 2236 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2243 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2237 | 2244 | @intCast(u32, self.func_types.items.len), |
| 2238 | 2245 | ); |
| 2239 | 2246 | section_count += 1; |
| ... | ... | @@ -2243,8 +2250,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2243 | 2250 | const import_memory = self.base.options.import_memory or is_obj; |
| 2244 | 2251 | const import_table = self.base.options.import_table or is_obj; |
| 2245 | 2252 | 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); |
| 2248 | 2254 | |
| 2249 | 2255 | // import table is always first table so emit that first |
| 2250 | 2256 | if (import_table) { |
| ... | ... | @@ -2261,14 +2267,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2261 | 2267 | }, |
| 2262 | 2268 | }, |
| 2263 | 2269 | }; |
| 2264 | | try self.emitImport(writer, table_imp); |
| 2270 | try self.emitImport(binary_writer, table_imp); |
| 2265 | 2271 | } |
| 2266 | 2272 | |
| 2267 | 2273 | var it = self.imports.iterator(); |
| 2268 | 2274 | while (it.next()) |entry| { |
| 2269 | 2275 | assert(entry.key_ptr.*.getSymbol(self).isUndefined()); |
| 2270 | 2276 | const import = entry.value_ptr.*; |
| 2271 | | try self.emitImport(writer, import); |
| 2277 | try self.emitImport(binary_writer, import); |
| 2272 | 2278 | } |
| 2273 | 2279 | |
| 2274 | 2280 | if (import_memory) { |
| ... | ... | @@ -2278,14 +2284,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2278 | 2284 | .name = try self.string_table.put(self.base.allocator, mem_name), |
| 2279 | 2285 | .kind = .{ .memory = self.memories.limits }, |
| 2280 | 2286 | }; |
| 2281 | | try self.emitImport(writer, mem_imp); |
| 2287 | try self.emitImport(binary_writer, mem_imp); |
| 2282 | 2288 | } |
| 2283 | 2289 | |
| 2284 | 2290 | try writeVecSectionHeader( |
| 2285 | | file, |
| 2291 | binary_bytes.items, |
| 2286 | 2292 | header_offset, |
| 2287 | 2293 | .import, |
| 2288 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2294 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2289 | 2295 | @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), |
| 2290 | 2296 | ); |
| 2291 | 2297 | section_count += 1; |
| ... | ... | @@ -2293,17 +2299,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2293 | 2299 | |
| 2294 | 2300 | // Function section |
| 2295 | 2301 | 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); |
| 2298 | 2303 | for (self.functions.values()) |function| { |
| 2299 | | try leb.writeULEB128(writer, function.type_index); |
| 2304 | try leb.writeULEB128(binary_writer, function.type_index); |
| 2300 | 2305 | } |
| 2301 | 2306 | |
| 2302 | 2307 | try writeVecSectionHeader( |
| 2303 | | file, |
| 2308 | binary_bytes.items, |
| 2304 | 2309 | header_offset, |
| 2305 | 2310 | .function, |
| 2306 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2311 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2307 | 2312 | @intCast(u32, self.functions.count()), |
| 2308 | 2313 | ); |
| 2309 | 2314 | section_count += 1; |
| ... | ... | @@ -2312,20 +2317,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2312 | 2317 | // Table section |
| 2313 | 2318 | const export_table = self.base.options.export_table; |
| 2314 | 2319 | 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); |
| 2317 | 2321 | |
| 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, .{ |
| 2320 | 2324 | .min = @intCast(u32, self.function_table.count()) + 1, |
| 2321 | 2325 | .max = null, |
| 2322 | 2326 | }); |
| 2323 | 2327 | |
| 2324 | 2328 | try writeVecSectionHeader( |
| 2325 | | file, |
| 2329 | binary_bytes.items, |
| 2326 | 2330 | header_offset, |
| 2327 | 2331 | .table, |
| 2328 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2332 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2329 | 2333 | @as(u32, 1), |
| 2330 | 2334 | ); |
| 2331 | 2335 | section_count += 1; |
| ... | ... | @@ -2333,15 +2337,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2333 | 2337 | |
| 2334 | 2338 | // Memory section |
| 2335 | 2339 | if (!import_memory) { |
| 2336 | | const header_offset = try reserveVecSectionHeader(file); |
| 2337 | | const writer = file.writer(); |
| 2340 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2338 | 2341 | |
| 2339 | | try emitLimits(writer, self.memories.limits); |
| 2342 | try emitLimits(binary_writer, self.memories.limits); |
| 2340 | 2343 | try writeVecSectionHeader( |
| 2341 | | file, |
| 2344 | binary_bytes.items, |
| 2342 | 2345 | header_offset, |
| 2343 | 2346 | .memory, |
| 2344 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2347 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2345 | 2348 | @as(u32, 1), // wasm currently only supports 1 linear memory segment |
| 2346 | 2349 | ); |
| 2347 | 2350 | section_count += 1; |
| ... | ... | @@ -2349,20 +2352,19 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2349 | 2352 | |
| 2350 | 2353 | // Global section (used to emit stack pointer) |
| 2351 | 2354 | 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); |
| 2354 | 2356 | |
| 2355 | 2357 | 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); |
| 2359 | 2361 | } |
| 2360 | 2362 | |
| 2361 | 2363 | try writeVecSectionHeader( |
| 2362 | | file, |
| 2364 | binary_bytes.items, |
| 2363 | 2365 | header_offset, |
| 2364 | 2366 | .global, |
| 2365 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2367 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2366 | 2368 | @intCast(u32, self.wasm_globals.items.len), |
| 2367 | 2369 | ); |
| 2368 | 2370 | section_count += 1; |
| ... | ... | @@ -2370,35 +2372,35 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2370 | 2372 | |
| 2371 | 2373 | // Export section |
| 2372 | 2374 | 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 | |
| 2375 | 2377 | for (self.exports.items) |exp| { |
| 2376 | 2378 | 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); |
| 2381 | 2383 | } |
| 2382 | 2384 | |
| 2383 | 2385 | 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 |
| 2388 | 2390 | } |
| 2389 | 2391 | |
| 2390 | 2392 | 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)); |
| 2395 | 2397 | } |
| 2396 | 2398 | |
| 2397 | 2399 | try writeVecSectionHeader( |
| 2398 | | file, |
| 2400 | binary_bytes.items, |
| 2399 | 2401 | header_offset, |
| 2400 | 2402 | .@"export", |
| 2401 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2403 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2402 | 2404 | @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), |
| 2403 | 2405 | ); |
| 2404 | 2406 | section_count += 1; |
| ... | ... | @@ -2406,25 +2408,24 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2406 | 2408 | |
| 2407 | 2409 | // element section (function table) |
| 2408 | 2410 | 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); |
| 2411 | 2412 | |
| 2412 | 2413 | 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())); |
| 2418 | 2419 | var symbol_it = self.function_table.keyIterator(); |
| 2419 | 2420 | 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); |
| 2421 | 2422 | } |
| 2422 | 2423 | |
| 2423 | 2424 | try writeVecSectionHeader( |
| 2424 | | file, |
| 2425 | binary_bytes.items, |
| 2425 | 2426 | header_offset, |
| 2426 | 2427 | .element, |
| 2427 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2428 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2428 | 2429 | @as(u32, 1), |
| 2429 | 2430 | ); |
| 2430 | 2431 | section_count += 1; |
| ... | ... | @@ -2433,8 +2434,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2433 | 2434 | // Code section |
| 2434 | 2435 | var code_section_size: u32 = 0; |
| 2435 | 2436 | 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); |
| 2438 | 2438 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); |
| 2439 | 2439 | |
| 2440 | 2440 | // 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 |
| 2460 | 2460 | std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn); |
| 2461 | 2461 | |
| 2462 | 2462 | 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); |
| 2465 | 2465 | } |
| 2466 | 2466 | |
| 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); |
| 2468 | 2468 | try writeVecSectionHeader( |
| 2469 | | file, |
| 2469 | binary_bytes.items, |
| 2470 | 2470 | header_offset, |
| 2471 | 2471 | .code, |
| 2472 | 2472 | code_section_size, |
| ... | ... | @@ -2478,8 +2478,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2478 | 2478 | |
| 2479 | 2479 | // Data section |
| 2480 | 2480 | 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); |
| 2483 | 2482 | |
| 2484 | 2483 | var it = self.data_segments.iterator(); |
| 2485 | 2484 | var segment_count: u32 = 0; |
| ... | ... | @@ -2493,10 +2492,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2493 | 2492 | const segment = self.segments.items[atom_index]; |
| 2494 | 2493 | |
| 2495 | 2494 | // 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)); |
| 2497 | 2496 | // 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); |
| 2500 | 2499 | |
| 2501 | 2500 | // fill in the offset table and the data segments |
| 2502 | 2501 | var current_offset: u32 = 0; |
| ... | ... | @@ -2508,12 +2507,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2508 | 2507 | // Pad with zeroes to ensure all segments are aligned |
| 2509 | 2508 | if (current_offset != atom.offset) { |
| 2510 | 2509 | const diff = atom.offset - current_offset; |
| 2511 | | try writer.writeByteNTimes(0, diff); |
| 2510 | try binary_writer.writeByteNTimes(0, diff); |
| 2512 | 2511 | current_offset += diff; |
| 2513 | 2512 | } |
| 2514 | 2513 | assert(current_offset == atom.offset); |
| 2515 | 2514 | assert(atom.code.items.len == atom.size); |
| 2516 | | try writer.writeAll(atom.code.items); |
| 2515 | try binary_writer.writeAll(atom.code.items); |
| 2517 | 2516 | |
| 2518 | 2517 | current_offset += atom.size; |
| 2519 | 2518 | if (atom.next) |next| { |
| ... | ... | @@ -2522,7 +2521,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2522 | 2521 | // also pad with zeroes when last atom to ensure |
| 2523 | 2522 | // segments are aligned. |
| 2524 | 2523 | if (current_offset != segment.size) { |
| 2525 | | try writer.writeByteNTimes(0, segment.size - current_offset); |
| 2524 | try binary_writer.writeByteNTimes(0, segment.size - current_offset); |
| 2526 | 2525 | current_offset += segment.size - current_offset; |
| 2527 | 2526 | } |
| 2528 | 2527 | break; |
| ... | ... | @@ -2532,10 +2531,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2532 | 2531 | } |
| 2533 | 2532 | |
| 2534 | 2533 | try writeVecSectionHeader( |
| 2535 | | file, |
| 2534 | binary_bytes.items, |
| 2536 | 2535 | header_offset, |
| 2537 | 2536 | .data, |
| 2538 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 2537 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| 2539 | 2538 | @intCast(u32, segment_count), |
| 2540 | 2539 | ); |
| 2541 | 2540 | data_section_index = section_count; |
| ... | ... | @@ -2547,12 +2546,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2547 | 2546 | // we never store all symbols in a single table, but store a location reference instead. |
| 2548 | 2547 | // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. |
| 2549 | 2548 | 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); |
| 2551 | 2550 | 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); |
| 2553 | 2552 | } |
| 2554 | 2553 | 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); |
| 2556 | 2555 | } |
| 2557 | 2556 | } else if (!self.base.options.strip) { |
| 2558 | 2557 | if (self.dwarf) |*dwarf| { |
| ... | ... | @@ -2592,41 +2591,45 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2592 | 2591 | try debug_bytes.appendSlice(atom.code.items); |
| 2593 | 2592 | atom = atom.next orelse break; |
| 2594 | 2593 | } |
| 2595 | | try emitDebugSection(file, debug_bytes.items, item.name); |
| 2594 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); |
| 2596 | 2595 | debug_bytes.clearRetainingCapacity(); |
| 2597 | 2596 | } |
| 2598 | 2597 | } |
| 2599 | | try self.emitNameSection(file, arena); |
| 2598 | try self.emitNameSection(&binary_bytes, arena); |
| 2600 | 2599 | } |
| 2601 | 2600 | |
| 2602 | 2601 | // Only when writing all sections executed properly we write the magic |
| 2603 | 2602 | // bytes. This allows us to easily detect what went wrong while generating |
| 2604 | 2603 | // 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); |
| 2606 | 2612 | } |
| 2607 | 2613 | |
| 2608 | | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { |
| 2614 | fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void { |
| 2609 | 2615 | 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(); |
| 2612 | 2618 | try leb.writeULEB128(writer, @intCast(u32, name.len)); |
| 2613 | 2619 | try writer.writeAll(name); |
| 2614 | 2620 | |
| 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; |
| 2620 | 2622 | 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); |
| 2621 | 2624 | |
| 2622 | 2625 | try writeCustomSectionHeader( |
| 2623 | | file, |
| 2626 | binary_bytes.items, |
| 2624 | 2627 | header_offset, |
| 2625 | | @intCast(u32, (try file.getPos()) - header_offset - 6), |
| 2628 | @intCast(u32, binary_bytes.items.len - header_offset - 6), |
| 2626 | 2629 | ); |
| 2627 | 2630 | } |
| 2628 | 2631 | |
| 2629 | | fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 2632 | fn emitNameSection(self: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { |
| 2630 | 2633 | const Name = struct { |
| 2631 | 2634 | index: u32, |
| 2632 | 2635 | name: []const u8, |
| ... | ... | @@ -2672,8 +2675,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 2672 | 2675 | std.sort.sort(Name, funcs.values(), {}, Name.lessThan); |
| 2673 | 2676 | std.sort.sort(Name, globals.items, {}, Name.lessThan); |
| 2674 | 2677 | |
| 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(); |
| 2677 | 2680 | try leb.writeULEB128(writer, @intCast(u32, "name".len)); |
| 2678 | 2681 | try writer.writeAll("name"); |
| 2679 | 2682 | |
| ... | ... | @@ -2682,9 +2685,9 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 2682 | 2685 | try self.emitNameSubsection(.data_segment, segments.items, writer); |
| 2683 | 2686 | |
| 2684 | 2687 | try writeCustomSectionHeader( |
| 2685 | | file, |
| 2688 | binary_bytes.items, |
| 2686 | 2689 | header_offset, |
| 2687 | | @intCast(u32, (try file.getPos()) - header_offset - 6), |
| 2690 | @intCast(u32, binary_bytes.items.len - header_offset - 6), |
| 2688 | 2691 | ); |
| 2689 | 2692 | } |
| 2690 | 2693 | |
| ... | ... | @@ -3197,54 +3200,40 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 3197 | 3200 | } |
| 3198 | 3201 | } |
| 3199 | 3202 | |
| 3200 | | fn reserveVecSectionHeader(file: fs.File) !u64 { |
| 3203 | fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { |
| 3201 | 3204 | // section id + fixed leb contents size + fixed leb vector length |
| 3202 | 3205 | 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; |
| 3207 | 3209 | } |
| 3208 | 3210 | |
| 3209 | | fn reserveCustomSectionHeader(file: fs.File) !u64 { |
| 3211 | fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u64 { |
| 3210 | 3212 | // unlike regular section, we don't emit the count |
| 3211 | 3213 | 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; |
| 3216 | 3217 | } |
| 3217 | 3218 | |
| 3218 | | fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void { |
| 3219 | fn writeVecSectionHeader(buffer: []u8, offset: u32, section: wasm.Section, size: u32, items: u32) !void { |
| 3219 | 3220 | var buf: [1 + 5 + 5]u8 = undefined; |
| 3220 | 3221 | buf[0] = @enumToInt(section); |
| 3221 | 3222 | leb.writeUnsignedFixed(5, buf[1..6], size); |
| 3222 | 3223 | 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); |
| 3230 | 3225 | } |
| 3231 | 3226 | |
| 3232 | | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { |
| 3227 | fn writeCustomSectionHeader(buffer: []u8, offset: u64, size: u32) !void { |
| 3233 | 3228 | var buf: [1 + 5]u8 = undefined; |
| 3234 | 3229 | buf[0] = 0; // 0 = 'custom' section |
| 3235 | 3230 | 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); |
| 3243 | 3232 | } |
| 3244 | 3233 | |
| 3245 | | fn 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(); |
| 3234 | fn 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(); |
| 3248 | 3237 | // emit "linking" custom section name |
| 3249 | 3238 | const section_name = "linking"; |
| 3250 | 3239 | try leb.writeULEB128(writer, section_name.len); |
| ... | ... | @@ -3255,21 +3244,18 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * |
| 3255 | 3244 | |
| 3256 | 3245 | // For each subsection type (found in types.Subsection) we can emit a section. |
| 3257 | 3246 | // 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); |
| 3260 | 3249 | |
| 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); |
| 3263 | 3252 | } |
| 3264 | 3253 | |
| 3265 | | fn 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(); |
| 3254 | fn emitSymbolTable(self: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { |
| 3255 | const writer = binary_bytes.writer(); |
| 3271 | 3256 | |
| 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; |
| 3273 | 3259 | |
| 3274 | 3260 | var symbol_count: u32 = 0; |
| 3275 | 3261 | for (self.resolved_symbols.keys()) |sym_loc| { |
| ... | ... | @@ -3307,23 +3293,17 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * |
| 3307 | 3293 | } |
| 3308 | 3294 | } |
| 3309 | 3295 | |
| 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); |
| 3321 | 3300 | } |
| 3322 | 3301 | |
| 3323 | | fn 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)); |
| 3302 | fn 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 | |
| 3327 | 3307 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); |
| 3328 | 3308 | for (self.segment_info.values()) |segment_info| { |
| 3329 | 3309 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| ... | ... | @@ -3337,13 +3317,9 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 3337 | 3317 | try leb.writeULEB128(writer, segment_info.flags); |
| 3338 | 3318 | } |
| 3339 | 3319 | |
| 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); |
| 3347 | 3323 | } |
| 3348 | 3324 | |
| 3349 | 3325 | pub fn getULEB128Size(uint_value: anytype) u32 { |
| ... | ... | @@ -3361,21 +3337,20 @@ pub fn getULEB128Size(uint_value: anytype) u32 { |
| 3361 | 3337 | /// For each relocatable section, emits a custom "relocation.<section_name>" section |
| 3362 | 3338 | fn emitCodeRelocations( |
| 3363 | 3339 | self: *Wasm, |
| 3364 | | file: fs.File, |
| 3365 | | arena: Allocator, |
| 3340 | binary_bytes: *std.ArrayList(u8), |
| 3366 | 3341 | section_index: u32, |
| 3367 | 3342 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), |
| 3368 | 3343 | ) !void { |
| 3369 | 3344 | 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); |
| 3372 | 3347 | |
| 3373 | 3348 | // write custom section information |
| 3374 | 3349 | const name = "reloc.CODE"; |
| 3375 | 3350 | try leb.writeULEB128(writer, @intCast(u32, name.len)); |
| 3376 | 3351 | try writer.writeAll(name); |
| 3377 | 3352 | try leb.writeULEB128(writer, section_index); |
| 3378 | | const reloc_start = payload.items.len; |
| 3353 | const reloc_start = binary_bytes.items.len; |
| 3379 | 3354 | |
| 3380 | 3355 | var count: u32 = 0; |
| 3381 | 3356 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); |
| ... | ... | @@ -3401,36 +3376,27 @@ fn emitCodeRelocations( |
| 3401 | 3376 | if (count == 0) return; |
| 3402 | 3377 | var buf: [5]u8 = undefined; |
| 3403 | 3378 | 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); |
| 3415 | 3382 | } |
| 3416 | 3383 | |
| 3417 | 3384 | fn emitDataRelocations( |
| 3418 | 3385 | self: *Wasm, |
| 3419 | | file: fs.File, |
| 3420 | | arena: Allocator, |
| 3386 | binary_bytes: *std.ArrayList(u8), |
| 3421 | 3387 | section_index: u32, |
| 3422 | 3388 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), |
| 3423 | 3389 | ) !void { |
| 3424 | 3390 | 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); |
| 3427 | 3393 | |
| 3428 | 3394 | // write custom section information |
| 3429 | 3395 | const name = "reloc.DATA"; |
| 3430 | 3396 | try leb.writeULEB128(writer, @intCast(u32, name.len)); |
| 3431 | 3397 | try writer.writeAll(name); |
| 3432 | 3398 | try leb.writeULEB128(writer, section_index); |
| 3433 | | const reloc_start = payload.items.len; |
| 3399 | const reloc_start = binary_bytes.items.len; |
| 3434 | 3400 | |
| 3435 | 3401 | var count: u32 = 0; |
| 3436 | 3402 | // for each atom, we calculate the uleb size and append that |
| ... | ... | @@ -3462,17 +3428,9 @@ fn emitDataRelocations( |
| 3462 | 3428 | |
| 3463 | 3429 | var buf: [5]u8 = undefined; |
| 3464 | 3430 | 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); |
| 3476 | 3434 | } |
| 3477 | 3435 | |
| 3478 | 3436 | /// Searches for an a matching function signature, when not found |