authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-21 21:43:38+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-23 16:07:36+01:00
logacec06cfaf9a82ec8037a23993ff36fa72eb6e82
tree6fbabbcf1bedaf2b46ee42c170f187c2a34648f5
parent0a48a763fd3548a7b8609555cecbafc21ebe1fc3

wasm-linker: Implement `updateDeclExports`

We now correctly implement exporting decls. This means it is possible to export a decl with a different name than the decl that is doing the export. This also sets the symbols with the correct flags, so when we emit a relocatable object file, a linker can correctly resolve symbols and/or export the symbol to the host environment. This commit also includes fixes to ensure relocations have the correct offset to how other linkers will expect the offset, rather than what we use internally. Other linkers accept the offset, relative to the section. Internally we use an offset relative to the atom.

8 files changed, 235 insertions(+), 84 deletions(-)

src/Module.zig+3
...@@ -4516,6 +4516,9 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {...@@ -4516,6 +4516,9 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
4516 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {4516 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
4517 macho.deleteExport(exp.link.macho);4517 macho.deleteExport(exp.link.macho);
4518 }4518 }
4519 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
4520 wasm.deleteExport(exp.link.wasm);
4521 }
4519 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {4522 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
4520 failed_kv.value.destroy(mod.gpa);4523 failed_kv.value.destroy(mod.gpa);
4521 }4524 }
src/Sema.zig+1-1
...@@ -3919,7 +3919,7 @@ pub fn analyzeExport(...@@ -3919,7 +3919,7 @@ pub fn analyzeExport(
3919 .macho => .{ .macho = .{} },3919 .macho => .{ .macho = .{} },
3920 .plan9 => .{ .plan9 = null },3920 .plan9 => .{ .plan9 = null },
3921 .c => .{ .c = {} },3921 .c => .{ .c = {} },
3922 .wasm => .{ .wasm = {} },3922 .wasm => .{ .wasm = .{} },
3923 .spirv => .{ .spirv = {} },3923 .spirv => .{ .spirv = {} },
3924 .nvptx => .{ .nvptx = {} },3924 .nvptx => .{ .nvptx = {} },
3925 },3925 },
src/arch/wasm/CodeGen.zig+12-8
...@@ -1320,14 +1320,18 @@ pub const DeclGen = struct {...@@ -1320,14 +1320,18 @@ pub const DeclGen = struct {
1320 }1320 }
13211321
1322 decl.markAlive();1322 decl.markAlive();
1323 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(1323 if (decl.link.wasm.sym_index == 0) {
1324 self.decl, // The decl containing the source symbol index1324 try writer.writeIntLittle(u32, 0);
1325 decl.ty, // type we generate the address of1325 } else {
1326 self.symbol_index, // source symbol index1326 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
1327 decl.link.wasm.sym_index, // target symbol index1327 self.decl, // The decl containing the source symbol index
1328 @intCast(u32, self.code.items.len), // offset1328 decl.ty, // type we generate the address of
1329 @intCast(u32, offset), // addend1329 self.symbol_index, // source symbol index
1330 ));1330 decl.link.wasm.sym_index, // target symbol index
1331 @intCast(u32, self.code.items.len), // offset
1332 @intCast(u32, offset), // addend
1333 ));
1334 }
1331 return Result{ .appended = {} };1335 return Result{ .appended = {} };
1332 }1336 }
1333};1337};
src/arch/wasm/Emit.zig+23-16
...@@ -242,6 +242,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -242,6 +242,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
242 const global_offset = emit.offset();242 const global_offset = emit.offset();
243 try emit.code.appendSlice(&buf);243 try emit.code.appendSlice(&buf);
244244
245 // globals can have index 0 as it represents the stack pointer
245 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{246 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
246 .index = label,247 .index = label,
247 .offset = global_offset,248 .offset = global_offset,
...@@ -294,11 +295,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -294,11 +295,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
294 leb128.writeUnsignedFixed(5, &buf, label);295 leb128.writeUnsignedFixed(5, &buf, label);
295 try emit.code.appendSlice(&buf);296 try emit.code.appendSlice(&buf);
296297
297 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{298 if (label != 0) {
298 .offset = call_offset,299 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
299 .index = label,300 .offset = call_offset,
300 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,301 .index = label,
301 });302 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
303 });
304 }
302}305}
303306
304fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {307fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
...@@ -318,11 +321,13 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -318,11 +321,13 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
318 leb128.writeUnsignedFixed(5, &buf, symbol_index);321 leb128.writeUnsignedFixed(5, &buf, symbol_index);
319 try emit.code.appendSlice(&buf);322 try emit.code.appendSlice(&buf);
320323
321 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{324 if (symbol_index != 0) {
322 .offset = index_offset,325 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
323 .index = symbol_index,326 .offset = index_offset,
324 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,327 .index = symbol_index,
325 });328 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
329 });
330 }
326}331}
327332
328fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {333fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
...@@ -342,12 +347,14 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -342,12 +347,14 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
342 try emit.code.appendSlice(&buf);347 try emit.code.appendSlice(&buf);
343 }348 }
344349
345 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{350 if (mem.pointer != 0) {
346 .offset = mem_offset,351 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
347 .index = mem.pointer,352 .offset = mem_offset,
348 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,353 .index = mem.pointer,
349 .addend = mem.offset,354 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
350 });355 .addend = mem.offset,
356 });
357 }
351}358}
352359
353fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {360fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
src/link.zig+1-1
...@@ -237,7 +237,7 @@ pub const File = struct {...@@ -237,7 +237,7 @@ pub const File = struct {
237 macho: MachO.Export,237 macho: MachO.Export,
238 plan9: Plan9.Export,238 plan9: Plan9.Export,
239 c: void,239 c: void,
240 wasm: void,240 wasm: Wasm.Export,
241 spirv: void,241 spirv: void,
242 nvptx: void,242 nvptx: void,
243 };243 };
src/link/Wasm.zig+187-57
...@@ -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.
116symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},116symbol_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.
119export_names: std.AutoHashMapUnmanaged(SymbolLoc, []const u8) = .{},
117120
118pub const Segment = struct {121pub 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};
131134
135pub const Export = struct {
136 sym_index: ?u32 = null,
137};
138
132pub const SymbolLoc = struct {139pub const SymbolLoc = struct {
133 /// The index of the symbol within the specified file140 /// 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 {
372381
373 var symbol: Symbol = .{382 var symbol: Symbol = .{
374 .name = undefined, // will be set after updateDecl383 .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 updateDecl385 .tag = undefined, // will be set after updateDecl
377 .index = undefined, // will be set after updateDecl386 .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}
491499
...@@ -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}
571580
581pub 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
572pub fn updateDeclExports(595pub 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}
585684
586pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {685pub 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 symbol695 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 }
600700
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}
607707
...@@ -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` section885 // 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}
9171019
918fn setupExports(self: *Wasm) !void {1020fn 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", .{});
9201023
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;
9291027
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 }
9381037
...@@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void {...@@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void {
959 }1058 }
9601059
961 // Ensure the symbol is exported so host environment can access it1060 // 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}
9641065
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}
11021205
...@@ -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();
11231226
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 flushin1234 // 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 linker1258 // When we finish/error we reset the state of the linker
1149 // So we can rebuild the binary file on each incremental update1259 // 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)));
11871298
1188 // Type section1299 // 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 }
12091321
1210 // Import section1322 // 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 }
12591372
1260 // Function section1373 // 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 }
12761390
1277 // Table section1391 // 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();
12821396
...@@ -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 }
12971412
1298 // Memory section1413 // 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();
13021417
...@@ -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 segment1424 @as(u32, 1), // wasm currently only supports 1 linear memory segment
1310 );1425 );
1426 section_count += 1;
1311 }1427 }
13121428
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 }
13321449
1333 // Export section1450 // 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 + name1457 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 }
13691460
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 table1465 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 }
13771474
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 }
13861484
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 }
14111510
1412 // Code section1511 // 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 }
14331534
1434 // Data section1535 // 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 }
14971600
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
15631670
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}
21962304
2305fn 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>" section2317/// For each relocatable section, emits a custom "relocation.<section_name>" section
2198fn emitCodeRelocations(2318fn emitCodeRelocations(
2199 self: *Wasm,2319 self: *Wasm,
...@@ -2215,17 +2335,22 @@ fn emitCodeRelocations(...@@ -2215,17 +2335,22 @@ fn emitCodeRelocations(
22152335
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;
22632388
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 }
src/link/Wasm/Atom.zig-1
...@@ -173,7 +173,6 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -173,7 +173,6 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
173 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {173 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {
174 return 0;174 return 0;
175 }175 }
176
177 const merge_segment = wasm_bin.base.options.output_mode != .Obj;176 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
178 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);177 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);
179 const atom_index = wasm_bin.data_segments.get(segment_name).?;178 const atom_index = wasm_bin.data_segments.get(segment_name).?;
src/link/Wasm/Symbol.zig+8
...@@ -104,6 +104,14 @@ pub fn setUndefined(self: *Symbol, is_undefined: bool) void {...@@ -104,6 +104,14 @@ pub fn setUndefined(self: *Symbol, is_undefined: bool) void {
104 }104 }
105}105}
106106
107pub fn setGlobal(self: *Symbol, is_global: bool) void {
108 if (is_global) {
109 self.flags &= ~@enumToInt(Flag.WASM_SYM_BINDING_LOCAL);
110 } else {
111 self.setFlag(.WASM_SYM_BINDING_LOCAL);
112 }
113}
114
107pub fn isDefined(self: Symbol) bool {115pub fn isDefined(self: Symbol) bool {
108 return !self.isUndefined();116 return !self.isUndefined();
109}117}