| ... | ... | @@ -114,6 +114,9 @@ resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{}, |
| 114 | 114 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 115 | 115 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. |
| 116 | 116 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{}, |
| 117 | /// Maps a symbol's location to its export name, which may differ from the decl's name |
| 118 | /// which does the exporting. |
| 119 | export_names: std.AutoHashMapUnmanaged(SymbolLoc, []const u8) = .{}, |
| 117 | 120 | |
| 118 | 121 | pub const Segment = struct { |
| 119 | 122 | alignment: u32, |
| ... | ... | @@ -129,6 +132,10 @@ pub const FnData = struct { |
| 129 | 132 | }; |
| 130 | 133 | }; |
| 131 | 134 | |
| 135 | pub const Export = struct { |
| 136 | sym_index: ?u32 = null, |
| 137 | }; |
| 138 | |
| 132 | 139 | pub const SymbolLoc = struct { |
| 133 | 140 | /// The index of the symbol within the specified file |
| 134 | 141 | index: u32, |
| ... | ... | @@ -191,6 +198,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 191 | 198 | }, |
| 192 | 199 | ); |
| 193 | 200 | } else { |
| 201 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 194 | 202 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 195 | 203 | global.* = .{ |
| 196 | 204 | .global_type = .{ |
| ... | ... | @@ -345,6 +353,7 @@ pub fn deinit(self: *Wasm) void { |
| 345 | 353 | self.resolved_symbols.deinit(gpa); |
| 346 | 354 | self.discarded.deinit(gpa); |
| 347 | 355 | self.symbol_atom.deinit(gpa); |
| 356 | self.export_names.deinit(gpa); |
| 348 | 357 | self.atoms.deinit(gpa); |
| 349 | 358 | self.managed_atoms.deinit(gpa); |
| 350 | 359 | self.segments.deinit(gpa); |
| ... | ... | @@ -372,7 +381,7 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 372 | 381 | |
| 373 | 382 | var symbol: Symbol = .{ |
| 374 | 383 | .name = undefined, // will be set after updateDecl |
| 375 | | .flags = 0, |
| 384 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 376 | 385 | .tag = undefined, // will be set after updateDecl |
| 377 | 386 | .index = undefined, // will be set after updateDecl |
| 378 | 387 | }; |
| ... | ... | @@ -485,7 +494,6 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { |
| 485 | 494 | atom.alignment = decl.ty.abiAlignment(self.base.options.target); |
| 486 | 495 | const symbol = &self.symbols.items[atom.sym_index]; |
| 487 | 496 | symbol.name = decl.name; |
| 488 | | symbol.setFlag(.WASM_SYM_BINDING_LOCAL); |
| 489 | 497 | try atom.code.appendSlice(self.base.allocator, code); |
| 490 | 498 | } |
| 491 | 499 | |
| ... | ... | @@ -541,6 +549,7 @@ pub fn getDeclVAddr( |
| 541 | 549 | offset: u32, |
| 542 | 550 | addend: u32, |
| 543 | 551 | ) !u32 { |
| 552 | assert(target_symbol_index != 0); |
| 544 | 553 | const atom = decl.link.wasm.symbolAtom(symbol_index); |
| 545 | 554 | const is_wasm32 = self.base.options.target.cpu.arch == .wasm32; |
| 546 | 555 | if (ty.zigTypeTag() == .Fn) { |
| ... | ... | @@ -569,6 +578,20 @@ pub fn getDeclVAddr( |
| 569 | 578 | return target_symbol_index; |
| 570 | 579 | } |
| 571 | 580 | |
| 581 | pub fn deleteExport(self: *Wasm, exp: Export) void { |
| 582 | if (self.llvm_object) |_| return; |
| 583 | const sym_index = exp.sym_index orelse return; |
| 584 | const loc: SymbolLoc = .{ .file = null, .index = sym_index }; |
| 585 | const symbol = loc.getSymbol(self); |
| 586 | const symbol_name = mem.sliceTo(symbol.name, 0); |
| 587 | log.debug("Deleting export for decl '{s}'", .{symbol_name}); |
| 588 | if (self.export_names.fetchRemove(loc)) |kv| { |
| 589 | assert(self.globals.remove(kv.value)); |
| 590 | } else { |
| 591 | assert(self.globals.remove(symbol_name)); |
| 592 | } |
| 593 | } |
| 594 | |
| 572 | 595 | pub fn updateDeclExports( |
| 573 | 596 | self: *Wasm, |
| 574 | 597 | module: *Module, |
| ... | ... | @@ -581,6 +604,82 @@ pub fn updateDeclExports( |
| 581 | 604 | if (build_options.have_llvm) { |
| 582 | 605 | if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); |
| 583 | 606 | } |
| 607 | |
| 608 | for (exports) |exp| { |
| 609 | if (exp.options.section) |section| { |
| 610 | try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create( |
| 611 | module.gpa, |
| 612 | decl.srcLoc(), |
| 613 | "Unimplemented: ExportOptions.section '{s}'", |
| 614 | .{section}, |
| 615 | )); |
| 616 | continue; |
| 617 | } |
| 618 | if (self.globals.getPtr(exp.options.name)) |existing_loc| { |
| 619 | if (existing_loc.index == decl.link.wasm.sym_index) continue; |
| 620 | const existing_sym: Symbol = existing_loc.getSymbol(self).*; |
| 621 | |
| 622 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; |
| 623 | // When both the to-bo-exported symbol and the already existing symbol |
| 624 | // are strong symbols, we have a linker error. |
| 625 | // In the other case we replace one with the other. |
| 626 | if (!exp_is_weak and !existing_sym.isWeak()) { |
| 627 | try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create( |
| 628 | module.gpa, |
| 629 | decl.srcLoc(), |
| 630 | \\LinkError: symbol '{s}' defined multiple times |
| 631 | \\ first definition in '{s}' |
| 632 | \\ next definition in '{s}' |
| 633 | , |
| 634 | .{ exp.options.name, self.name, self.name }, |
| 635 | )); |
| 636 | } else if (exp_is_weak) { |
| 637 | continue; // to-be-exported symbol is weak, so we keep the existing symbol |
| 638 | } else { |
| 639 | existing_loc.index = decl.link.wasm.sym_index; |
| 640 | existing_loc.file = null; |
| 641 | exp.link.wasm.sym_index = existing_loc.index; |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | const sym_index = exp.exported_decl.link.wasm.sym_index; |
| 646 | const sym_loc = exp.exported_decl.link.wasm.symbolLoc(); |
| 647 | const symbol = sym_loc.getSymbol(self); |
| 648 | switch (exp.options.linkage) { |
| 649 | .Internal => { |
| 650 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 651 | symbol.setFlag(.WASM_SYM_BINDING_WEAK); |
| 652 | }, |
| 653 | .Weak => { |
| 654 | symbol.setFlag(.WASM_SYM_BINDING_WEAK); |
| 655 | }, |
| 656 | .Strong => {}, // symbols are strong by default |
| 657 | .LinkOnce => { |
| 658 | try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create( |
| 659 | module.gpa, |
| 660 | decl.srcLoc(), |
| 661 | "Unimplemented: LinkOnce", |
| 662 | .{}, |
| 663 | )); |
| 664 | continue; |
| 665 | }, |
| 666 | } |
| 667 | // Ensure the symbol will be exported using the given name |
| 668 | if (!mem.eql(u8, exp.options.name, mem.sliceTo(exp.exported_decl.name, 0))) { |
| 669 | try self.export_names.put(self.base.allocator, sym_loc, exp.options.name); |
| 670 | } |
| 671 | |
| 672 | symbol.setGlobal(true); |
| 673 | try self.globals.put( |
| 674 | self.base.allocator, |
| 675 | exp.options.name, |
| 676 | sym_loc, |
| 677 | ); |
| 678 | |
| 679 | // if the symbol was previously undefined, remove it as an import |
| 680 | _ = self.imports.remove(sym_loc); |
| 681 | exp.link.wasm.sym_index = sym_index; |
| 682 | } |
| 584 | 683 | } |
| 585 | 684 | |
| 586 | 685 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| ... | ... | @@ -596,12 +695,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 596 | 695 | local_symbol.tag = .dead; // also for any local symbol |
| 597 | 696 | self.base.allocator.free(mem.sliceTo(local_symbol.name, 0)); |
| 598 | 697 | self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {}; |
| 698 | assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc())); |
| 599 | 699 | } |
| 600 | 700 | |
| 601 | 701 | if (decl.isExtern()) { |
| 602 | | assert(self.imports.remove(.{ .file = null, .index = atom.sym_index })); |
| 702 | assert(self.imports.remove(atom.symbolLoc())); |
| 603 | 703 | } |
| 604 | | assert(self.resolved_symbols.swapRemove(.{ .index = atom.sym_index, .file = null })); |
| 704 | assert(self.resolved_symbols.swapRemove(atom.symbolLoc())); |
| 605 | 705 | atom.deinit(self.base.allocator); |
| 606 | 706 | } |
| 607 | 707 | |
| ... | ... | @@ -627,6 +727,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { |
| 627 | 727 | const symbol: *Symbol = &self.symbols.items[symbol_index]; |
| 628 | 728 | symbol.name = decl.name; |
| 629 | 729 | symbol.setUndefined(true); |
| 730 | symbol.setGlobal(true); |
| 630 | 731 | try self.globals.putNoClobber( |
| 631 | 732 | self.base.allocator, |
| 632 | 733 | mem.sliceTo(symbol.name, 0), |
| ... | ... | @@ -778,6 +879,7 @@ fn setupImports(self: *Wasm) !void { |
| 778 | 879 | } |
| 779 | 880 | } |
| 780 | 881 | } |
| 882 | |
| 781 | 883 | for (self.resolved_symbols.keys()) |symbol_loc| { |
| 782 | 884 | if (symbol_loc.file == null) { |
| 783 | 885 | // imports generated by Zig code are already in the `import` section |
| ... | ... | @@ -916,23 +1018,20 @@ fn mergeTypes(self: *Wasm) !void { |
| 916 | 1018 | } |
| 917 | 1019 | |
| 918 | 1020 | fn setupExports(self: *Wasm) !void { |
| 1021 | if (self.base.options.output_mode == .Obj) return; |
| 919 | 1022 | log.debug("Building exports from symbols", .{}); |
| 920 | 1023 | |
| 921 | | // When importing memory option if false, we export it instead |
| 922 | | if (!self.base.options.import_memory) { |
| 923 | | try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 }); |
| 924 | | } |
| 925 | | |
| 926 | 1024 | for (self.resolved_symbols.keys()) |sym_loc| { |
| 927 | 1025 | const symbol = sym_loc.getSymbol(self); |
| 928 | 1026 | if (!symbol.isExported()) continue; |
| 929 | 1027 | |
| 1028 | const export_name = if (self.export_names.get(sym_loc)) |name| name else mem.sliceTo(symbol.name, 0); |
| 930 | 1029 | const exp: wasm.Export = .{ |
| 931 | | .name = mem.sliceTo(symbol.name, 0), |
| 1030 | .name = export_name, |
| 932 | 1031 | .kind = symbol.tag.externalType(), |
| 933 | 1032 | .index = symbol.index, |
| 934 | 1033 | }; |
| 935 | | log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index }); |
| 1034 | log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ symbol.name, exp.name, exp.index }); |
| 936 | 1035 | try self.exports.append(self.base.allocator, exp); |
| 937 | 1036 | } |
| 938 | 1037 | |
| ... | ... | @@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void { |
| 959 | 1058 | } |
| 960 | 1059 | |
| 961 | 1060 | // Ensure the symbol is exported so host environment can access it |
| 962 | | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1061 | if (self.base.options.output_mode != .Obj) { |
| 1062 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1063 | } |
| 963 | 1064 | } |
| 964 | 1065 | |
| 965 | 1066 | /// Sets up the memory section of the wasm module, as well as the stack. |
| ... | ... | @@ -1093,10 +1194,12 @@ fn resetState(self: *Wasm) void { |
| 1093 | 1194 | atom.prev = null; |
| 1094 | 1195 | } |
| 1095 | 1196 | self.functions.clearRetainingCapacity(); |
| 1197 | self.exports.clearRetainingCapacity(); |
| 1096 | 1198 | self.segments.clearRetainingCapacity(); |
| 1097 | 1199 | self.segment_info.clearRetainingCapacity(); |
| 1098 | 1200 | self.data_segments.clearRetainingCapacity(); |
| 1099 | 1201 | self.atoms.clearRetainingCapacity(); |
| 1202 | self.symbol_atom.clearRetainingCapacity(); |
| 1100 | 1203 | self.code_section_index = null; |
| 1101 | 1204 | } |
| 1102 | 1205 | |
| ... | ... | @@ -1121,6 +1224,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1121 | 1224 | const tracy = trace(@src()); |
| 1122 | 1225 | defer tracy.end(); |
| 1123 | 1226 | |
| 1227 | // The amount of sections that will be written |
| 1228 | var section_count: u32 = 0; |
| 1229 | // Index of the code section. Used to tell relocation table where the section lives. |
| 1230 | var code_section_index: ?u32 = null; |
| 1231 | // Index of the data section. Used to tell relocation table where the section lives. |
| 1232 | var data_section_index: ?u32 = null; |
| 1233 | |
| 1124 | 1234 | // Used for all temporary memory allocated during flushin |
| 1125 | 1235 | var arena_instance = std.heap.ArenaAllocator.init(self.base.allocator); |
| 1126 | 1236 | defer arena_instance.deinit(); |
| ... | ... | @@ -1148,6 +1258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1148 | 1258 | // When we finish/error we reset the state of the linker |
| 1149 | 1259 | // So we can rebuild the binary file on each incremental update |
| 1150 | 1260 | defer self.resetState(); |
| 1261 | try self.setupStart(); |
| 1151 | 1262 | try self.setupImports(); |
| 1152 | 1263 | var decl_it = self.decls.keyIterator(); |
| 1153 | 1264 | while (decl_it.next()) |decl| { |
| ... | ... | @@ -1186,7 +1297,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1186 | 1297 | try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); |
| 1187 | 1298 | |
| 1188 | 1299 | // Type section |
| 1189 | | { |
| 1300 | if (self.func_types.items.len != 0) { |
| 1190 | 1301 | const header_offset = try reserveVecSectionHeader(file); |
| 1191 | 1302 | const writer = file.writer(); |
| 1192 | 1303 | log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); |
| ... | ... | @@ -1205,11 +1316,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1205 | 1316 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1206 | 1317 | @intCast(u32, self.func_types.items.len), |
| 1207 | 1318 | ); |
| 1319 | section_count += 1; |
| 1208 | 1320 | } |
| 1209 | 1321 | |
| 1210 | 1322 | // Import section |
| 1211 | | const import_memory = self.base.options.import_memory; |
| 1212 | | const import_table = self.base.options.import_table; |
| 1323 | const import_memory = self.base.options.import_memory or is_obj; |
| 1324 | const import_table = self.base.options.import_table or is_obj; |
| 1213 | 1325 | if (self.imports.count() != 0 or import_memory or import_table) { |
| 1214 | 1326 | const header_offset = try reserveVecSectionHeader(file); |
| 1215 | 1327 | const writer = file.writer(); |
| ... | ... | @@ -1242,7 +1354,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1242 | 1354 | if (import_memory) { |
| 1243 | 1355 | const mem_imp: wasm.Import = .{ |
| 1244 | 1356 | .module_name = self.host_name, |
| 1245 | | .name = "memory", |
| 1357 | .name = "__linear_memory", |
| 1246 | 1358 | .kind = .{ .memory = self.memories.limits }, |
| 1247 | 1359 | }; |
| 1248 | 1360 | try emitImport(writer, mem_imp); |
| ... | ... | @@ -1255,10 +1367,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1255 | 1367 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1256 | 1368 | @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), |
| 1257 | 1369 | ); |
| 1370 | section_count += 1; |
| 1258 | 1371 | } |
| 1259 | 1372 | |
| 1260 | 1373 | // Function section |
| 1261 | | { |
| 1374 | if (self.functions.items.len != 0) { |
| 1262 | 1375 | const header_offset = try reserveVecSectionHeader(file); |
| 1263 | 1376 | const writer = file.writer(); |
| 1264 | 1377 | for (self.functions.items) |function| { |
| ... | ... | @@ -1272,11 +1385,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1272 | 1385 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1273 | 1386 | @intCast(u32, self.functions.items.len), |
| 1274 | 1387 | ); |
| 1388 | section_count += 1; |
| 1275 | 1389 | } |
| 1276 | 1390 | |
| 1277 | 1391 | // Table section |
| 1278 | 1392 | const export_table = self.base.options.export_table; |
| 1279 | | if (!import_table) { |
| 1393 | if (!import_table and self.function_table.count() != 0) { |
| 1280 | 1394 | const header_offset = try reserveVecSectionHeader(file); |
| 1281 | 1395 | const writer = file.writer(); |
| 1282 | 1396 | |
| ... | ... | @@ -1293,10 +1407,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1293 | 1407 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1294 | 1408 | @as(u32, 1), |
| 1295 | 1409 | ); |
| 1410 | section_count += 1; |
| 1296 | 1411 | } |
| 1297 | 1412 | |
| 1298 | 1413 | // Memory section |
| 1299 | | if (!self.base.options.import_memory) { |
| 1414 | if (!import_memory) { |
| 1300 | 1415 | const header_offset = try reserveVecSectionHeader(file); |
| 1301 | 1416 | const writer = file.writer(); |
| 1302 | 1417 | |
| ... | ... | @@ -1308,6 +1423,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1308 | 1423 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1309 | 1424 | @as(u32, 1), // wasm currently only supports 1 linear memory segment |
| 1310 | 1425 | ); |
| 1426 | section_count += 1; |
| 1311 | 1427 | } |
| 1312 | 1428 | |
| 1313 | 1429 | // Global section (used to emit stack pointer) |
| ... | ... | @@ -1328,43 +1444,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1328 | 1444 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1329 | 1445 | @intCast(u32, self.wasm_globals.items.len), |
| 1330 | 1446 | ); |
| 1447 | section_count += 1; |
| 1331 | 1448 | } |
| 1332 | 1449 | |
| 1333 | 1450 | // Export section |
| 1334 | | if (self.base.options.module) |module| { |
| 1451 | if (self.exports.items.len != 0 or export_table or !import_memory) { |
| 1335 | 1452 | const header_offset = try reserveVecSectionHeader(file); |
| 1336 | 1453 | const writer = file.writer(); |
| 1337 | | var count: u32 = 0; |
| 1338 | | for (module.decl_exports.values()) |exports| { |
| 1339 | | for (exports) |exprt| { |
| 1340 | | // Export name length + name |
| 1341 | | try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len)); |
| 1342 | | try writer.writeAll(exprt.options.name); |
| 1343 | | |
| 1344 | | switch (exprt.exported_decl.ty.zigTypeTag()) { |
| 1345 | | .Fn => { |
| 1346 | | const target = exprt.exported_decl.link.wasm.sym_index; |
| 1347 | | const target_symbol = self.symbols.items[target]; |
| 1348 | | assert(target_symbol.tag == .function); |
| 1349 | | // Type of the export |
| 1350 | | try writer.writeByte(wasm.externalKind(.function)); |
| 1351 | | // Exported function index |
| 1352 | | try leb.writeULEB128(writer, target_symbol.index); |
| 1353 | | }, |
| 1354 | | else => return error.TODOImplementNonFnDeclsForWasm, |
| 1355 | | } |
| 1356 | | |
| 1357 | | count += 1; |
| 1358 | | } |
| 1359 | | } |
| 1360 | | |
| 1361 | | // export memory if size is not 0 |
| 1362 | | if (!import_memory) { |
| 1363 | | try leb.writeULEB128(writer, @intCast(u32, "memory".len)); |
| 1364 | | try writer.writeAll("memory"); |
| 1365 | | try writer.writeByte(wasm.externalKind(.memory)); |
| 1366 | | try leb.writeULEB128(writer, @as(u32, 0)); // only 1 memory 'object' can exist |
| 1367 | | count += 1; |
| 1454 | for (self.exports.items) |exp| { |
| 1455 | try leb.writeULEB128(writer, @intCast(u32, exp.name.len)); |
| 1456 | try writer.writeAll(exp.name); |
| 1457 | try leb.writeULEB128(writer, @enumToInt(exp.kind)); |
| 1458 | try leb.writeULEB128(writer, exp.index); |
| 1368 | 1459 | } |
| 1369 | 1460 | |
| 1370 | 1461 | if (export_table) { |
| ... | ... | @@ -1372,7 +1463,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1372 | 1463 | try writer.writeAll("__indirect_function_table"); |
| 1373 | 1464 | try writer.writeByte(wasm.externalKind(.table)); |
| 1374 | 1465 | try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table |
| 1375 | | count += 1; |
| 1466 | } |
| 1467 | |
| 1468 | if (!import_memory) { |
| 1469 | try leb.writeULEB128(writer, @intCast(u32, "memory".len)); |
| 1470 | try writer.writeAll("memory"); |
| 1471 | try writer.writeByte(wasm.externalKind(.memory)); |
| 1472 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1376 | 1473 | } |
| 1377 | 1474 | |
| 1378 | 1475 | try writeVecSectionHeader( |
| ... | ... | @@ -1380,8 +1477,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1380 | 1477 | header_offset, |
| 1381 | 1478 | .@"export", |
| 1382 | 1479 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1383 | | count, |
| 1480 | @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory), |
| 1384 | 1481 | ); |
| 1482 | section_count += 1; |
| 1385 | 1483 | } |
| 1386 | 1484 | |
| 1387 | 1485 | // element section (function table) |
| ... | ... | @@ -1407,6 +1505,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1407 | 1505 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1408 | 1506 | @as(u32, 1), |
| 1409 | 1507 | ); |
| 1508 | section_count += 1; |
| 1410 | 1509 | } |
| 1411 | 1510 | |
| 1412 | 1511 | // Code section |
| ... | ... | @@ -1429,6 +1528,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1429 | 1528 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1430 | 1529 | @intCast(u32, self.functions.items.len), |
| 1431 | 1530 | ); |
| 1531 | code_section_index = section_count; |
| 1532 | section_count += 1; |
| 1432 | 1533 | } |
| 1433 | 1534 | |
| 1434 | 1535 | // Data section |
| ... | ... | @@ -1493,6 +1594,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1493 | 1594 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1494 | 1595 | @intCast(u32, segment_count), |
| 1495 | 1596 | ); |
| 1597 | data_section_index = section_count; |
| 1598 | section_count += 1; |
| 1496 | 1599 | } |
| 1497 | 1600 | |
| 1498 | 1601 | if (is_obj) { |
| ... | ... | @@ -1501,8 +1604,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1501 | 1604 | // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. |
| 1502 | 1605 | var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); |
| 1503 | 1606 | try self.emitLinkSection(file, arena, &symbol_table); |
| 1504 | | try self.emitCodeRelocations(file, arena, 6, symbol_table); |
| 1505 | | try self.emitDataRelocations(file, arena, 7, symbol_table); |
| 1607 | if (code_section_index) |code_index| { |
| 1608 | try self.emitCodeRelocations(file, arena, code_index, symbol_table); |
| 1609 | } |
| 1610 | if (data_section_index) |data_index| { |
| 1611 | try self.emitDataRelocations(file, arena, data_index, symbol_table); |
| 1612 | } |
| 1506 | 1613 | } else { |
| 1507 | 1614 | try self.emitNameSection(file, arena); |
| 1508 | 1615 | } |
| ... | ... | @@ -1563,6 +1670,7 @@ fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: a |
| 1563 | 1670 | |
| 1564 | 1671 | try leb.writeULEB128(sub_writer, @intCast(u32, names.len)); |
| 1565 | 1672 | for (names) |name| { |
| 1673 | log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) }); |
| 1566 | 1674 | try leb.writeULEB128(sub_writer, name.index); |
| 1567 | 1675 | try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len)); |
| 1568 | 1676 | try sub_writer.writeAll(name.name); |
| ... | ... | @@ -2194,6 +2302,18 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 2194 | 2302 | try file.writevAll(&.{iovec}); |
| 2195 | 2303 | } |
| 2196 | 2304 | |
| 2305 | fn getULEB128Size(uint_value: anytype) u32 { |
| 2306 | const T = @TypeOf(uint_value); |
| 2307 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; |
| 2308 | var value = @intCast(U, uint_value); |
| 2309 | |
| 2310 | var size: u32 = 0; |
| 2311 | while (value != 0) : (size += 1) { |
| 2312 | value >>= 7; |
| 2313 | } |
| 2314 | return size; |
| 2315 | } |
| 2316 | |
| 2197 | 2317 | /// For each relocatable section, emits a custom "relocation.<section_name>" section |
| 2198 | 2318 | fn emitCodeRelocations( |
| 2199 | 2319 | self: *Wasm, |
| ... | ... | @@ -2215,17 +2335,22 @@ fn emitCodeRelocations( |
| 2215 | 2335 | |
| 2216 | 2336 | var count: u32 = 0; |
| 2217 | 2337 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); |
| 2338 | // for each atom, we calculate the uleb size and append that |
| 2339 | var size_offset: u32 = 5; // account for code section size leb128 |
| 2218 | 2340 | while (true) { |
| 2341 | size_offset += getULEB128Size(atom.size); |
| 2219 | 2342 | for (atom.relocs.items) |relocation| { |
| 2220 | 2343 | count += 1; |
| 2221 | 2344 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index }; |
| 2222 | 2345 | const symbol_index = symbol_table.get(sym_loc).?; |
| 2223 | 2346 | try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type)); |
| 2224 | | try leb.writeULEB128(writer, atom.offset + relocation.offset); |
| 2347 | const offset = atom.offset + relocation.offset + size_offset; |
| 2348 | try leb.writeULEB128(writer, offset); |
| 2225 | 2349 | try leb.writeULEB128(writer, symbol_index); |
| 2226 | 2350 | if (relocation.relocation_type.addendIsPresent()) { |
| 2227 | 2351 | try leb.writeULEB128(writer, relocation.addend orelse 0); |
| 2228 | 2352 | } |
| 2353 | log.debug("Emit relocation: {}", .{relocation}); |
| 2229 | 2354 | } |
| 2230 | 2355 | atom = atom.next orelse break; |
| 2231 | 2356 | } |
| ... | ... | @@ -2262,9 +2387,12 @@ fn emitDataRelocations( |
| 2262 | 2387 | const reloc_start = payload.items.len; |
| 2263 | 2388 | |
| 2264 | 2389 | var count: u32 = 0; |
| 2390 | // for each atom, we calculate the uleb size and append that |
| 2391 | var size_offset: u32 = 5; // account for code section size leb128 |
| 2265 | 2392 | for (self.data_segments.values()) |segment_index| { |
| 2266 | 2393 | var atom: *Atom = self.atoms.get(segment_index).?.getFirst(); |
| 2267 | 2394 | while (true) { |
| 2395 | size_offset += getULEB128Size(atom.size); |
| 2268 | 2396 | for (atom.relocs.items) |relocation| { |
| 2269 | 2397 | count += 1; |
| 2270 | 2398 | const sym_loc: SymbolLoc = .{ |
| ... | ... | @@ -2273,11 +2401,13 @@ fn emitDataRelocations( |
| 2273 | 2401 | }; |
| 2274 | 2402 | const symbol_index = symbol_table.get(sym_loc).?; |
| 2275 | 2403 | try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type)); |
| 2276 | | try leb.writeULEB128(writer, atom.offset + relocation.offset); |
| 2404 | const offset = atom.offset + relocation.offset + size_offset; |
| 2405 | try leb.writeULEB128(writer, offset); |
| 2277 | 2406 | try leb.writeULEB128(writer, symbol_index); |
| 2278 | 2407 | if (relocation.relocation_type.addendIsPresent()) { |
| 2279 | 2408 | try leb.writeULEB128(writer, relocation.addend orelse 0); |
| 2280 | 2409 | } |
| 2410 | log.debug("Emit relocation: {}", .{relocation}); |
| 2281 | 2411 | } |
| 2282 | 2412 | atom = atom.next orelse break; |
| 2283 | 2413 | } |