| ... | @@ -114,6 +114,9 @@ resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{}, | ... | @@ -114,6 +114,9 @@ resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{}, |
| 114 | /// data of a symbol, such as its size, or its offset to perform a relocation. | 114 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 115 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. | 115 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. |
| 116 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{}, | 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 | pub const Segment = struct { | 121 | pub const Segment = struct { |
| 119 | alignment: u32, | 122 | alignment: u32, |
| ... | @@ -129,6 +132,10 @@ pub const FnData = struct { | ... | @@ -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 | pub const SymbolLoc = struct { | 139 | pub const SymbolLoc = struct { |
| 133 | /// The index of the symbol within the specified file | 140 | /// The index of the symbol within the specified file |
| 134 | index: u32, | 141 | index: u32, |
| ... | @@ -191,6 +198,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -191,6 +198,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 191 | }, | 198 | }, |
| 192 | ); | 199 | ); |
| 193 | } else { | 200 | } else { |
| | 201 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 194 | const global = try wasm_bin.wasm_globals.addOne(allocator); | 202 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 195 | global.* = .{ | 203 | global.* = .{ |
| 196 | .global_type = .{ | 204 | .global_type = .{ |
| ... | @@ -345,6 +353,7 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -345,6 +353,7 @@ pub fn deinit(self: *Wasm) void { |
| 345 | self.resolved_symbols.deinit(gpa); | 353 | self.resolved_symbols.deinit(gpa); |
| 346 | self.discarded.deinit(gpa); | 354 | self.discarded.deinit(gpa); |
| 347 | self.symbol_atom.deinit(gpa); | 355 | self.symbol_atom.deinit(gpa); |
| | 356 | self.export_names.deinit(gpa); |
| 348 | self.atoms.deinit(gpa); | 357 | self.atoms.deinit(gpa); |
| 349 | self.managed_atoms.deinit(gpa); | 358 | self.managed_atoms.deinit(gpa); |
| 350 | self.segments.deinit(gpa); | 359 | self.segments.deinit(gpa); |
| ... | @@ -372,7 +381,7 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | ... | @@ -372,7 +381,7 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 372 | | 381 | |
| 373 | var symbol: Symbol = .{ | 382 | var symbol: Symbol = .{ |
| 374 | .name = undefined, // will be set after updateDecl | 383 | .name = undefined, // will be set after updateDecl |
| 375 | .flags = 0, | 384 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 376 | .tag = undefined, // will be set after updateDecl | 385 | .tag = undefined, // will be set after updateDecl |
| 377 | .index = undefined, // will be set after updateDecl | 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,7 +494,6 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { |
| 485 | atom.alignment = decl.ty.abiAlignment(self.base.options.target); | 494 | atom.alignment = decl.ty.abiAlignment(self.base.options.target); |
| 486 | const symbol = &self.symbols.items[atom.sym_index]; | 495 | const symbol = &self.symbols.items[atom.sym_index]; |
| 487 | symbol.name = decl.name; | 496 | symbol.name = decl.name; |
| 488 | symbol.setFlag(.WASM_SYM_BINDING_LOCAL); | | |
| 489 | try atom.code.appendSlice(self.base.allocator, code); | 497 | try atom.code.appendSlice(self.base.allocator, code); |
| 490 | } | 498 | } |
| 491 | | 499 | |
| ... | @@ -541,6 +549,7 @@ pub fn getDeclVAddr( | ... | @@ -541,6 +549,7 @@ pub fn getDeclVAddr( |
| 541 | offset: u32, | 549 | offset: u32, |
| 542 | addend: u32, | 550 | addend: u32, |
| 543 | ) !u32 { | 551 | ) !u32 { |
| | 552 | assert(target_symbol_index != 0); |
| 544 | const atom = decl.link.wasm.symbolAtom(symbol_index); | 553 | const atom = decl.link.wasm.symbolAtom(symbol_index); |
| 545 | const is_wasm32 = self.base.options.target.cpu.arch == .wasm32; | 554 | const is_wasm32 = self.base.options.target.cpu.arch == .wasm32; |
| 546 | if (ty.zigTypeTag() == .Fn) { | 555 | if (ty.zigTypeTag() == .Fn) { |
| ... | @@ -569,6 +578,20 @@ pub fn getDeclVAddr( | ... | @@ -569,6 +578,20 @@ pub fn getDeclVAddr( |
| 569 | return target_symbol_index; | 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 | pub fn updateDeclExports( | 595 | pub fn updateDeclExports( |
| 573 | self: *Wasm, | 596 | self: *Wasm, |
| 574 | module: *Module, | 597 | module: *Module, |
| ... | @@ -581,6 +604,82 @@ pub fn updateDeclExports( | ... | @@ -581,6 +604,82 @@ pub fn updateDeclExports( |
| 581 | if (build_options.have_llvm) { | 604 | if (build_options.have_llvm) { |
| 582 | if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); | 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 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { | 685 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| ... | @@ -596,12 +695,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { | ... | @@ -596,12 +695,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 596 | local_symbol.tag = .dead; // also for any local symbol | 695 | local_symbol.tag = .dead; // also for any local symbol |
| 597 | self.base.allocator.free(mem.sliceTo(local_symbol.name, 0)); | 696 | self.base.allocator.free(mem.sliceTo(local_symbol.name, 0)); |
| 598 | self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {}; | 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 | if (decl.isExtern()) { | 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 | atom.deinit(self.base.allocator); | 705 | atom.deinit(self.base.allocator); |
| 606 | } | 706 | } |
| 607 | | 707 | |
| ... | @@ -627,6 +727,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { | ... | @@ -627,6 +727,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { |
| 627 | const symbol: *Symbol = &self.symbols.items[symbol_index]; | 727 | const symbol: *Symbol = &self.symbols.items[symbol_index]; |
| 628 | symbol.name = decl.name; | 728 | symbol.name = decl.name; |
| 629 | symbol.setUndefined(true); | 729 | symbol.setUndefined(true); |
| | 730 | symbol.setGlobal(true); |
| 630 | try self.globals.putNoClobber( | 731 | try self.globals.putNoClobber( |
| 631 | self.base.allocator, | 732 | self.base.allocator, |
| 632 | mem.sliceTo(symbol.name, 0), | 733 | mem.sliceTo(symbol.name, 0), |
| ... | @@ -778,6 +879,7 @@ fn setupImports(self: *Wasm) !void { | ... | @@ -778,6 +879,7 @@ fn setupImports(self: *Wasm) !void { |
| 778 | } | 879 | } |
| 779 | } | 880 | } |
| 780 | } | 881 | } |
| | 882 | |
| 781 | for (self.resolved_symbols.keys()) |symbol_loc| { | 883 | for (self.resolved_symbols.keys()) |symbol_loc| { |
| 782 | if (symbol_loc.file == null) { | 884 | if (symbol_loc.file == null) { |
| 783 | // imports generated by Zig code are already in the `import` section | 885 | // imports generated by Zig code are already in the `import` section |
| ... | @@ -916,23 +1018,20 @@ fn mergeTypes(self: *Wasm) !void { | ... | @@ -916,23 +1018,20 @@ fn mergeTypes(self: *Wasm) !void { |
| 916 | } | 1018 | } |
| 917 | | 1019 | |
| 918 | fn setupExports(self: *Wasm) !void { | 1020 | fn setupExports(self: *Wasm) !void { |
| | 1021 | if (self.base.options.output_mode == .Obj) return; |
| 919 | log.debug("Building exports from symbols", .{}); | 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 | for (self.resolved_symbols.keys()) |sym_loc| { | 1024 | for (self.resolved_symbols.keys()) |sym_loc| { |
| 927 | const symbol = sym_loc.getSymbol(self); | 1025 | const symbol = sym_loc.getSymbol(self); |
| 928 | if (!symbol.isExported()) continue; | 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 | const exp: wasm.Export = .{ | 1029 | const exp: wasm.Export = .{ |
| 931 | .name = mem.sliceTo(symbol.name, 0), | 1030 | .name = export_name, |
| 932 | .kind = symbol.tag.externalType(), | 1031 | .kind = symbol.tag.externalType(), |
| 933 | .index = symbol.index, | 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 | try self.exports.append(self.base.allocator, exp); | 1035 | try self.exports.append(self.base.allocator, exp); |
| 937 | } | 1036 | } |
| 938 | | 1037 | |
| ... | @@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void { | ... | @@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void { |
| 959 | } | 1058 | } |
| 960 | | 1059 | |
| 961 | // Ensure the symbol is exported so host environment can access it | 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 | /// Sets up the memory section of the wasm module, as well as the stack. | 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,10 +1194,12 @@ fn resetState(self: *Wasm) void { |
| 1093 | atom.prev = null; | 1194 | atom.prev = null; |
| 1094 | } | 1195 | } |
| 1095 | self.functions.clearRetainingCapacity(); | 1196 | self.functions.clearRetainingCapacity(); |
| | 1197 | self.exports.clearRetainingCapacity(); |
| 1096 | self.segments.clearRetainingCapacity(); | 1198 | self.segments.clearRetainingCapacity(); |
| 1097 | self.segment_info.clearRetainingCapacity(); | 1199 | self.segment_info.clearRetainingCapacity(); |
| 1098 | self.data_segments.clearRetainingCapacity(); | 1200 | self.data_segments.clearRetainingCapacity(); |
| 1099 | self.atoms.clearRetainingCapacity(); | 1201 | self.atoms.clearRetainingCapacity(); |
| | 1202 | self.symbol_atom.clearRetainingCapacity(); |
| 1100 | self.code_section_index = null; | 1203 | self.code_section_index = null; |
| 1101 | } | 1204 | } |
| 1102 | | 1205 | |
| ... | @@ -1121,6 +1224,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1121,6 +1224,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1121 | const tracy = trace(@src()); | 1224 | const tracy = trace(@src()); |
| 1122 | defer tracy.end(); | 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 | // Used for all temporary memory allocated during flushin | 1234 | // Used for all temporary memory allocated during flushin |
| 1125 | var arena_instance = std.heap.ArenaAllocator.init(self.base.allocator); | 1235 | var arena_instance = std.heap.ArenaAllocator.init(self.base.allocator); |
| 1126 | defer arena_instance.deinit(); | 1236 | defer arena_instance.deinit(); |
| ... | @@ -1148,6 +1258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1148,6 +1258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1148 | // When we finish/error we reset the state of the linker | 1258 | // When we finish/error we reset the state of the linker |
| 1149 | // So we can rebuild the binary file on each incremental update | 1259 | // So we can rebuild the binary file on each incremental update |
| 1150 | defer self.resetState(); | 1260 | defer self.resetState(); |
| | 1261 | try self.setupStart(); |
| 1151 | try self.setupImports(); | 1262 | try self.setupImports(); |
| 1152 | var decl_it = self.decls.keyIterator(); | 1263 | var decl_it = self.decls.keyIterator(); |
| 1153 | while (decl_it.next()) |decl| { | 1264 | while (decl_it.next()) |decl| { |
| ... | @@ -1186,7 +1297,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1186,7 +1297,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1186 | try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); | 1297 | try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); |
| 1187 | | 1298 | |
| 1188 | // Type section | 1299 | // Type section |
| 1189 | { | 1300 | if (self.func_types.items.len != 0) { |
| 1190 | const header_offset = try reserveVecSectionHeader(file); | 1301 | const header_offset = try reserveVecSectionHeader(file); |
| 1191 | const writer = file.writer(); | 1302 | const writer = file.writer(); |
| 1192 | log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len}); | 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,11 +1316,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1205 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1316 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1206 | @intCast(u32, self.func_types.items.len), | 1317 | @intCast(u32, self.func_types.items.len), |
| 1207 | ); | 1318 | ); |
| | 1319 | section_count += 1; |
| 1208 | } | 1320 | } |
| 1209 | | 1321 | |
| 1210 | // Import section | 1322 | // Import section |
| 1211 | const import_memory = self.base.options.import_memory; | 1323 | const import_memory = self.base.options.import_memory or is_obj; |
| 1212 | const import_table = self.base.options.import_table; | 1324 | const import_table = self.base.options.import_table or is_obj; |
| 1213 | if (self.imports.count() != 0 or import_memory or import_table) { | 1325 | if (self.imports.count() != 0 or import_memory or import_table) { |
| 1214 | const header_offset = try reserveVecSectionHeader(file); | 1326 | const header_offset = try reserveVecSectionHeader(file); |
| 1215 | const writer = file.writer(); | 1327 | const writer = file.writer(); |
| ... | @@ -1242,7 +1354,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1242,7 +1354,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1242 | if (import_memory) { | 1354 | if (import_memory) { |
| 1243 | const mem_imp: wasm.Import = .{ | 1355 | const mem_imp: wasm.Import = .{ |
| 1244 | .module_name = self.host_name, | 1356 | .module_name = self.host_name, |
| 1245 | .name = "memory", | 1357 | .name = "__linear_memory", |
| 1246 | .kind = .{ .memory = self.memories.limits }, | 1358 | .kind = .{ .memory = self.memories.limits }, |
| 1247 | }; | 1359 | }; |
| 1248 | try emitImport(writer, mem_imp); | 1360 | try emitImport(writer, mem_imp); |
| ... | @@ -1255,10 +1367,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1255,10 +1367,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1255 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1367 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1256 | @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), | 1368 | @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)), |
| 1257 | ); | 1369 | ); |
| | 1370 | section_count += 1; |
| 1258 | } | 1371 | } |
| 1259 | | 1372 | |
| 1260 | // Function section | 1373 | // Function section |
| 1261 | { | 1374 | if (self.functions.items.len != 0) { |
| 1262 | const header_offset = try reserveVecSectionHeader(file); | 1375 | const header_offset = try reserveVecSectionHeader(file); |
| 1263 | const writer = file.writer(); | 1376 | const writer = file.writer(); |
| 1264 | for (self.functions.items) |function| { | 1377 | for (self.functions.items) |function| { |
| ... | @@ -1272,11 +1385,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1272,11 +1385,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1272 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1385 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1273 | @intCast(u32, self.functions.items.len), | 1386 | @intCast(u32, self.functions.items.len), |
| 1274 | ); | 1387 | ); |
| | 1388 | section_count += 1; |
| 1275 | } | 1389 | } |
| 1276 | | 1390 | |
| 1277 | // Table section | 1391 | // Table section |
| 1278 | const export_table = self.base.options.export_table; | 1392 | const export_table = self.base.options.export_table; |
| 1279 | if (!import_table) { | 1393 | if (!import_table and self.function_table.count() != 0) { |
| 1280 | const header_offset = try reserveVecSectionHeader(file); | 1394 | const header_offset = try reserveVecSectionHeader(file); |
| 1281 | const writer = file.writer(); | 1395 | const writer = file.writer(); |
| 1282 | | 1396 | |
| ... | @@ -1293,10 +1407,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1293,10 +1407,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1293 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1407 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1294 | @as(u32, 1), | 1408 | @as(u32, 1), |
| 1295 | ); | 1409 | ); |
| | 1410 | section_count += 1; |
| 1296 | } | 1411 | } |
| 1297 | | 1412 | |
| 1298 | // Memory section | 1413 | // Memory section |
| 1299 | if (!self.base.options.import_memory) { | 1414 | if (!import_memory) { |
| 1300 | const header_offset = try reserveVecSectionHeader(file); | 1415 | const header_offset = try reserveVecSectionHeader(file); |
| 1301 | const writer = file.writer(); | 1416 | const writer = file.writer(); |
| 1302 | | 1417 | |
| ... | @@ -1308,6 +1423,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1308,6 +1423,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1308 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1423 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1309 | @as(u32, 1), // wasm currently only supports 1 linear memory segment | 1424 | @as(u32, 1), // wasm currently only supports 1 linear memory segment |
| 1310 | ); | 1425 | ); |
| | 1426 | section_count += 1; |
| 1311 | } | 1427 | } |
| 1312 | | 1428 | |
| 1313 | // Global section (used to emit stack pointer) | 1429 | // Global section (used to emit stack pointer) |
| ... | @@ -1328,43 +1444,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1328,43 +1444,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1328 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1444 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1329 | @intCast(u32, self.wasm_globals.items.len), | 1445 | @intCast(u32, self.wasm_globals.items.len), |
| 1330 | ); | 1446 | ); |
| | 1447 | section_count += 1; |
| 1331 | } | 1448 | } |
| 1332 | | 1449 | |
| 1333 | // Export section | 1450 | // Export section |
| 1334 | if (self.base.options.module) |module| { | 1451 | if (self.exports.items.len != 0 or export_table or !import_memory) { |
| 1335 | const header_offset = try reserveVecSectionHeader(file); | 1452 | const header_offset = try reserveVecSectionHeader(file); |
| 1336 | const writer = file.writer(); | 1453 | const writer = file.writer(); |
| 1337 | var count: u32 = 0; | 1454 | for (self.exports.items) |exp| { |
| 1338 | for (module.decl_exports.values()) |exports| { | 1455 | try leb.writeULEB128(writer, @intCast(u32, exp.name.len)); |
| 1339 | for (exports) |exprt| { | 1456 | try writer.writeAll(exp.name); |
| 1340 | // Export name length + name | 1457 | try leb.writeULEB128(writer, @enumToInt(exp.kind)); |
| 1341 | try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len)); | 1458 | try leb.writeULEB128(writer, exp.index); |
| 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; | | |
| 1368 | } | 1459 | } |
| 1369 | | 1460 | |
| 1370 | if (export_table) { | 1461 | if (export_table) { |
| ... | @@ -1372,7 +1463,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1372,7 +1463,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1372 | try writer.writeAll("__indirect_function_table"); | 1463 | try writer.writeAll("__indirect_function_table"); |
| 1373 | try writer.writeByte(wasm.externalKind(.table)); | 1464 | try writer.writeByte(wasm.externalKind(.table)); |
| 1374 | try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table | 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 | try writeVecSectionHeader( | 1475 | try writeVecSectionHeader( |
| ... | @@ -1380,8 +1477,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1380,8 +1477,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1380 | header_offset, | 1477 | header_offset, |
| 1381 | .@"export", | 1478 | .@"export", |
| 1382 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 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 | // element section (function table) | 1485 | // element section (function table) |
| ... | @@ -1407,6 +1505,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1407,6 +1505,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1407 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1505 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1408 | @as(u32, 1), | 1506 | @as(u32, 1), |
| 1409 | ); | 1507 | ); |
| | 1508 | section_count += 1; |
| 1410 | } | 1509 | } |
| 1411 | | 1510 | |
| 1412 | // Code section | 1511 | // Code section |
| ... | @@ -1429,6 +1528,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1429,6 +1528,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1429 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1528 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1430 | @intCast(u32, self.functions.items.len), | 1529 | @intCast(u32, self.functions.items.len), |
| 1431 | ); | 1530 | ); |
| | 1531 | code_section_index = section_count; |
| | 1532 | section_count += 1; |
| 1432 | } | 1533 | } |
| 1433 | | 1534 | |
| 1434 | // Data section | 1535 | // Data section |
| ... | @@ -1493,6 +1594,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1493,6 +1594,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1493 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1594 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1494 | @intCast(u32, segment_count), | 1595 | @intCast(u32, segment_count), |
| 1495 | ); | 1596 | ); |
| | 1597 | data_section_index = section_count; |
| | 1598 | section_count += 1; |
| 1496 | } | 1599 | } |
| 1497 | | 1600 | |
| 1498 | if (is_obj) { | 1601 | if (is_obj) { |
| ... | @@ -1501,8 +1604,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1501,8 +1604,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1501 | // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. | 1604 | // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. |
| 1502 | var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); | 1605 | var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); |
| 1503 | try self.emitLinkSection(file, arena, &symbol_table); | 1606 | try self.emitLinkSection(file, arena, &symbol_table); |
| 1504 | try self.emitCodeRelocations(file, arena, 6, symbol_table); | 1607 | if (code_section_index) |code_index| { |
| 1505 | try self.emitDataRelocations(file, arena, 7, symbol_table); | 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 | } else { | 1613 | } else { |
| 1507 | try self.emitNameSection(file, arena); | 1614 | try self.emitNameSection(file, arena); |
| 1508 | } | 1615 | } |
| ... | @@ -1563,6 +1670,7 @@ fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: a | ... | @@ -1563,6 +1670,7 @@ fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: a |
| 1563 | | 1670 | |
| 1564 | try leb.writeULEB128(sub_writer, @intCast(u32, names.len)); | 1671 | try leb.writeULEB128(sub_writer, @intCast(u32, names.len)); |
| 1565 | for (names) |name| { | 1672 | for (names) |name| { |
| | 1673 | log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) }); |
| 1566 | try leb.writeULEB128(sub_writer, name.index); | 1674 | try leb.writeULEB128(sub_writer, name.index); |
| 1567 | try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len)); | 1675 | try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len)); |
| 1568 | try sub_writer.writeAll(name.name); | 1676 | try sub_writer.writeAll(name.name); |
| ... | @@ -2194,6 +2302,18 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { | ... | @@ -2194,6 +2302,18 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 2194 | try file.writevAll(&.{iovec}); | 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 | /// For each relocatable section, emits a custom "relocation.<section_name>" section | 2317 | /// For each relocatable section, emits a custom "relocation.<section_name>" section |
| 2198 | fn emitCodeRelocations( | 2318 | fn emitCodeRelocations( |
| 2199 | self: *Wasm, | 2319 | self: *Wasm, |
| ... | @@ -2215,17 +2335,22 @@ fn emitCodeRelocations( | ... | @@ -2215,17 +2335,22 @@ fn emitCodeRelocations( |
| 2215 | | 2335 | |
| 2216 | var count: u32 = 0; | 2336 | var count: u32 = 0; |
| 2217 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); | 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 | while (true) { | 2340 | while (true) { |
| | 2341 | size_offset += getULEB128Size(atom.size); |
| 2219 | for (atom.relocs.items) |relocation| { | 2342 | for (atom.relocs.items) |relocation| { |
| 2220 | count += 1; | 2343 | count += 1; |
| 2221 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index }; | 2344 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index }; |
| 2222 | const symbol_index = symbol_table.get(sym_loc).?; | 2345 | const symbol_index = symbol_table.get(sym_loc).?; |
| 2223 | try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type)); | 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 | try leb.writeULEB128(writer, symbol_index); | 2349 | try leb.writeULEB128(writer, symbol_index); |
| 2226 | if (relocation.relocation_type.addendIsPresent()) { | 2350 | if (relocation.relocation_type.addendIsPresent()) { |
| 2227 | try leb.writeULEB128(writer, relocation.addend orelse 0); | 2351 | try leb.writeULEB128(writer, relocation.addend orelse 0); |
| 2228 | } | 2352 | } |
| | 2353 | log.debug("Emit relocation: {}", .{relocation}); |
| 2229 | } | 2354 | } |
| 2230 | atom = atom.next orelse break; | 2355 | atom = atom.next orelse break; |
| 2231 | } | 2356 | } |
| ... | @@ -2262,9 +2387,12 @@ fn emitDataRelocations( | ... | @@ -2262,9 +2387,12 @@ fn emitDataRelocations( |
| 2262 | const reloc_start = payload.items.len; | 2387 | const reloc_start = payload.items.len; |
| 2263 | | 2388 | |
| 2264 | var count: u32 = 0; | 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 | for (self.data_segments.values()) |segment_index| { | 2392 | for (self.data_segments.values()) |segment_index| { |
| 2266 | var atom: *Atom = self.atoms.get(segment_index).?.getFirst(); | 2393 | var atom: *Atom = self.atoms.get(segment_index).?.getFirst(); |
| 2267 | while (true) { | 2394 | while (true) { |
| | 2395 | size_offset += getULEB128Size(atom.size); |
| 2268 | for (atom.relocs.items) |relocation| { | 2396 | for (atom.relocs.items) |relocation| { |
| 2269 | count += 1; | 2397 | count += 1; |
| 2270 | const sym_loc: SymbolLoc = .{ | 2398 | const sym_loc: SymbolLoc = .{ |
| ... | @@ -2273,11 +2401,13 @@ fn emitDataRelocations( | ... | @@ -2273,11 +2401,13 @@ fn emitDataRelocations( |
| 2273 | }; | 2401 | }; |
| 2274 | const symbol_index = symbol_table.get(sym_loc).?; | 2402 | const symbol_index = symbol_table.get(sym_loc).?; |
| 2275 | try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type)); | 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 | try leb.writeULEB128(writer, symbol_index); | 2406 | try leb.writeULEB128(writer, symbol_index); |
| 2278 | if (relocation.relocation_type.addendIsPresent()) { | 2407 | if (relocation.relocation_type.addendIsPresent()) { |
| 2279 | try leb.writeULEB128(writer, relocation.addend orelse 0); | 2408 | try leb.writeULEB128(writer, relocation.addend orelse 0); |
| 2280 | } | 2409 | } |
| | 2410 | log.debug("Emit relocation: {}", .{relocation}); |
| 2281 | } | 2411 | } |
| 2282 | atom = atom.next orelse break; | 2412 | atom = atom.next orelse break; |
| 2283 | } | 2413 | } |