authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-15 22:27:58+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-17 18:11:48+01:00
loga4622501bdae96d43f26d1897c1f4de87b8daa31
treef6894652cc4dcafce3d367a81d9b1fee64f3d209
parentf1cc5f33e88a64e30695c682177e00af4310a119

wasm-linker: Allocate atoms and handle imports

We now correctly allocate and create atoms for symbols from other object files. Imports are now also resolved and appended when required. Besides those changes, we now duplicate all symbol names, so we can correctly generate unique names for unnamed constants. TODO: String interning

4 files changed, 136 insertions(+), 63 deletions(-)

src/link/Wasm.zig+100-41
...@@ -34,6 +34,8 @@ pub const base_tag = link.File.Tag.wasm;...@@ -34,6 +34,8 @@ pub const base_tag = link.File.Tag.wasm;
34pub const DeclBlock = Atom;34pub const DeclBlock = Atom;
3535
36base: link.File,36base: link.File,
37/// Output name of the file
38name: []const u8,
37/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.39/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
38llvm_object: ?*LlvmObject = null,40llvm_object: ?*LlvmObject = null,
39/// When importing objects from the host environment, a name must be supplied.41/// When importing objects from the host environment, a name must be supplied.
...@@ -156,6 +158,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -156,6 +158,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
156 // TODO: read the file and keep valid parts instead of truncating158 // TODO: read the file and keep valid parts instead of truncating
157 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });159 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
158 wasm_bin.base.file = file;160 wasm_bin.base.file = file;
161 wasm_bin.name = sub_path;
159162
160 try file.writeAll(&(wasm.magic ++ wasm.version));163 try file.writeAll(&(wasm.magic ++ wasm.version));
161164
...@@ -170,7 +173,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -170,7 +173,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
170 };173 };
171 const symbol = try wasm_bin.symbols.addOne(allocator);174 const symbol = try wasm_bin.symbols.addOne(allocator);
172 symbol.* = .{175 symbol.* = .{
173 .name = "__stack_pointer",176 .name = try allocator.dupeZ(u8, "__stack_pointer"),
174 .tag = .global,177 .tag = .global,
175 .flags = 0,178 .flags = 0,
176 .index = 0,179 .index = 0,
...@@ -188,6 +191,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {...@@ -188,6 +191,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
188 .file = null,191 .file = null,
189 .allocator = gpa,192 .allocator = gpa,
190 },193 },
194 .name = undefined,
191 };195 };
192 const use_llvm = build_options.have_llvm and options.use_llvm;196 const use_llvm = build_options.have_llvm and options.use_llvm;
193 const use_stage1 = build_options.is_stage1 and options.use_stage1;197 const use_stage1 = build_options.is_stage1 and options.use_stage1;
...@@ -233,6 +237,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -233,6 +237,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
233 .file = object_index,237 .file = object_index,
234 .index = sym_index,238 .index = sym_index,
235 };239 };
240 const sym_name = std.mem.sliceTo(symbol.name, 0);
236241
237 if (symbol.isLocal()) {242 if (symbol.isLocal()) {
238 if (symbol.isUndefined()) {243 if (symbol.isUndefined()) {
...@@ -247,21 +252,23 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -247,21 +252,23 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
247 // TODO: locals are allowed to have duplicate symbol names252 // TODO: locals are allowed to have duplicate symbol names
248 // TODO: Store undefined symbols so we can verify at the end if they've all been found253 // TODO: Store undefined symbols so we can verify at the end if they've all been found
249 // if not, emit an error (unless --allow-undefined is enabled).254 // if not, emit an error (unless --allow-undefined is enabled).
250 const maybe_existing = try self.globals.getOrPut(self.base.allocator, std.mem.sliceTo(symbol.name, 0));255 const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);
251 if (!maybe_existing.found_existing) {256 if (!maybe_existing.found_existing) {
252 maybe_existing.value_ptr.* = location;257 maybe_existing.value_ptr.* = location;
253
254 try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
255 continue;258 continue;
256 }259 }
257260
258 const existing_loc = maybe_existing.value_ptr.*;261 const existing_loc = maybe_existing.value_ptr.*;
259 const existing_sym: *Symbol = existing_loc.getSymbol(self);262 const existing_sym: *Symbol = existing_loc.getSymbol(self);
260263
264 const existing_file_path = if (existing_loc.file) |file| blk: {
265 break :blk self.objects.items[file].name;
266 } else self.name;
267
261 if (!existing_sym.isUndefined()) {268 if (!existing_sym.isUndefined()) {
262 if (!symbol.isUndefined()) {269 if (!symbol.isUndefined()) {
263 log.err("symbol '{s}' defined multiple times", .{existing_sym.name});270 log.err("symbol '{s}' defined multiple times", .{existing_sym.name});
264 log.err(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});271 log.err(" first definition in '{s}'", .{existing_file_path});
265 log.err(" next definition in '{s}'", .{object.name});272 log.err(" next definition in '{s}'", .{object.name});
266 return error.SymbolCollision;273 return error.SymbolCollision;
267 }274 }
...@@ -271,11 +278,11 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -271,11 +278,11 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
271278
272 // simply overwrite with the new symbol279 // simply overwrite with the new symbol
273 log.info("Overwriting symbol '{s}'", .{symbol.name});280 log.info("Overwriting symbol '{s}'", .{symbol.name});
274 log.info(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});281 log.info(" old definition in '{s}'", .{existing_file_path});
275 log.info(" next definition in '{s}'", .{object.name});282 log.info(" new definition in '{s}'", .{object.name});
276 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);283 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
277 maybe_existing.value_ptr.* = location;284 maybe_existing.value_ptr.* = location;
278 try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);285 try self.globals.put(self.base.allocator, sym_name, location);
279 }286 }
280}287}
281288
...@@ -302,6 +309,12 @@ pub fn deinit(self: *Wasm) void {...@@ -302,6 +309,12 @@ pub fn deinit(self: *Wasm) void {
302 object.deinit(gpa);309 object.deinit(gpa);
303 }310 }
304311
312 for (self.symbols.items) |symbol| {
313 if (symbol.tag != .dead) {
314 gpa.free(mem.sliceTo(symbol.name, 0));
315 }
316 }
317
305 self.decls.deinit(gpa);318 self.decls.deinit(gpa);
306 self.symbols.deinit(gpa);319 self.symbols.deinit(gpa);
307 self.symbols_free_list.deinit(gpa);320 self.symbols_free_list.deinit(gpa);
...@@ -441,7 +454,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {...@@ -441,7 +454,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
441 const atom: *Atom = &decl.link.wasm;454 const atom: *Atom = &decl.link.wasm;
442 atom.size = @intCast(u32, code.len);455 atom.size = @intCast(u32, code.len);
443 atom.alignment = decl.ty.abiAlignment(self.base.options.target);456 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
444 self.symbols.items[atom.sym_index].name = decl.name;457 self.symbols.items[atom.sym_index].name = try self.base.allocator.dupeZ(u8, std.mem.sliceTo(decl.name, 0));
445 try atom.code.appendSlice(self.base.allocator, code);458 try atom.code.appendSlice(self.base.allocator, code);
446}459}
447460
...@@ -449,8 +462,10 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {...@@ -449,8 +462,10 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
449/// and then append it as a 'contained' atom onto the Decl.462/// and then append it as a 'contained' atom onto the Decl.
450pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {463pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
451 assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions464 assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions
465 const local_index = decl.link.wasm.locals.items.len;
466 const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });
452 var symbol: Symbol = .{467 var symbol: Symbol = .{
453 .name = "unnamed_local",468 .name = name,
454 .flags = 0,469 .flags = 0,
455 .tag = .data,470 .tag = .data,
456 .index = undefined,471 .index = undefined,
...@@ -494,7 +509,7 @@ pub fn getDeclVAddr(...@@ -494,7 +509,7 @@ pub fn getDeclVAddr(
494 const atom = decl.link.wasm.symbolAtom(symbol_index);509 const atom = decl.link.wasm.symbolAtom(symbol_index);
495 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;510 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
496 if (ty.zigTypeTag() == .Fn) {511 if (ty.zigTypeTag() == .Fn) {
497 std.debug.assert(addend == 0); // addend not allowed for function relocations512 assert(addend == 0); // addend not allowed for function relocations
498 // We found a function pointer, so add it to our table,513 // We found a function pointer, so add it to our table,
499 // as function pointers are not allowed to be stored inside the data section.514 // as function pointers are not allowed to be stored inside the data section.
500 // They are instead stored in a function table which are called by index.515 // They are instead stored in a function table which are called by index.
...@@ -543,15 +558,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {...@@ -543,15 +558,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
543 self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section558 self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section
544 for (atom.locals.items) |local_atom| {559 for (atom.locals.items) |local_atom| {
545 self.symbols.items[local_atom.sym_index].tag = .dead; // also for any local symbol560 self.symbols.items[local_atom.sym_index].tag = .dead; // also for any local symbol
561 // self.base.allocator.free(mem.sliceTo(self.symbols.items[local_atom.sym_index].name, 0));
546 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};562 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
547 }563 }
564 // self.base.allocator.free(mem.sliceTo(self.symbols.items[atom.sym_index].name, 0));
548565
549 if (decl.isExtern()) {566 if (decl.isExtern()) {
550 const import = self.imports.fetchRemove(.{ .file = null, .index = atom.sym_index }).?.value;567 assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));
551 switch (import.kind) {
552 .function => self.imported_functions_count -= 1,
553 else => unreachable,
554 }
555 }568 }
556569
557 atom.deinit(self.base.allocator);570 atom.deinit(self.base.allocator);
...@@ -577,16 +590,18 @@ fn mapFunctionTable(self: *Wasm) void {...@@ -577,16 +590,18 @@ fn mapFunctionTable(self: *Wasm) void {
577fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {590fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
578 const symbol_index = decl.link.wasm.sym_index;591 const symbol_index = decl.link.wasm.sym_index;
579 const symbol: *Symbol = &self.symbols.items[symbol_index];592 const symbol: *Symbol = &self.symbols.items[symbol_index];
580 symbol.name = decl.name;593 const decl_name = mem.sliceTo(decl.name, 0);
594 symbol.name = try self.base.allocator.dupeZ(u8, decl_name);
581 symbol.setUndefined(true);595 symbol.setUndefined(true);
596 // also add it as a global so it can be resolved
597 try self.globals.put(self.base.allocator, decl_name, .{ .file = null, .index = symbol_index });
582 switch (decl.ty.zigTypeTag()) {598 switch (decl.ty.zigTypeTag()) {
583 .Fn => {599 .Fn => {
584 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });600 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
585 const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {601 const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {
586 break :blk std.mem.sliceTo(lib_name, 0);602 break :blk mem.sliceTo(lib_name, 0);
587 } else self.host_name;603 } else self.host_name;
588 if (!gop.found_existing) {604 if (!gop.found_existing) {
589 self.imported_functions_count += 1;
590 gop.value_ptr.* = .{605 gop.value_ptr.* = .{
591 .module_name = module_name,606 .module_name = module_name,
592 .name = std.mem.span(symbol.name),607 .name = std.mem.span(symbol.name),
...@@ -608,9 +623,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -608,9 +623,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
608 const symbol: *Symbol = &self.symbols.items[atom.sym_index];623 const symbol: *Symbol = &self.symbols.items[atom.sym_index];
609 const final_index: u32 = switch (kind) {624 const final_index: u32 = switch (kind) {
610 .function => |fn_data| result: {625 .function => |fn_data| result: {
611 const type_index = fn_data.type_index;
612 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);626 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
613 try self.functions.append(self.base.allocator, .{ .type_index = type_index });627 try self.functions.append(self.base.allocator, .{ .type_index = fn_data.type_index });
614 symbol.tag = .function;628 symbol.tag = .function;
615 symbol.index = index;629 symbol.index = index;
616630
...@@ -641,6 +655,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -641,6 +655,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
641 break :blk index;655 break :blk index;
642 };656 };
643 const info_index = @intCast(u32, self.segment_info.items.len);657 const info_index = @intCast(u32, self.segment_info.items.len);
658 // TODO: Add mutables global decls to .bss section instead
644 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{659 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
645 ".rodata.",660 ".rodata.",
646 std.mem.span(symbol.name),661 std.mem.span(symbol.name),
...@@ -684,7 +699,7 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -684,7 +699,7 @@ fn allocateAtoms(self: *Wasm) !void {
684 offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);699 offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);
685 atom.offset = offset;700 atom.offset = offset;
686 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{701 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
687 self.symbols.items[atom.sym_index].name,702 (SymbolLoc{ .file = atom.file, .index = atom.sym_index }).getSymbol(self).name,
688 offset,703 offset,
689 offset + atom.size,704 offset + atom.size,
690 atom.size,705 atom.size,
...@@ -695,7 +710,7 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -695,7 +710,7 @@ fn allocateAtoms(self: *Wasm) !void {
695 }710 }
696}711}
697712
698fn setupImports(self: *Wasm) void {713fn setupImports(self: *Wasm) !void {
699 for (self.resolved_symbols.items) |symbol_loc| {714 for (self.resolved_symbols.items) |symbol_loc| {
700 if (symbol_loc.file == null) {715 if (symbol_loc.file == null) {
701 // imports generated by Zig code are already in the `import` section716 // imports generated by Zig code are already in the `import` section
...@@ -708,7 +723,7 @@ fn setupImports(self: *Wasm) void {...@@ -708,7 +723,7 @@ fn setupImports(self: *Wasm) void {
708 }723 }
709724
710 log.debug("Symbol '{s}' will be imported from the host", .{symbol.name});725 log.debug("Symbol '{s}' will be imported from the host", .{symbol.name});
711 const import = self.objects.items[symbol_loc.file.?].findImport(symbol.externalType(), symbol.index);726 const import = self.objects.items[symbol_loc.file.?].findImport(symbol.tag.externalType(), symbol.index);
712 // TODO: De-duplicate imports727 // TODO: De-duplicate imports
713 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);728 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
714 }729 }
...@@ -737,6 +752,9 @@ fn setupImports(self: *Wasm) void {...@@ -737,6 +752,9 @@ fn setupImports(self: *Wasm) void {
737 else => unreachable,752 else => unreachable,
738 }753 }
739 }754 }
755 self.imported_functions_count = function_index;
756 self.imported_globals_count = global_index;
757 self.imported_tables_count = table_index;
740}758}
741759
742/// Takes the global, function and table section from each linked object file760/// Takes the global, function and table section from each linked object file
...@@ -761,12 +779,13 @@ fn mergeSections(self: *Wasm) !void {...@@ -761,12 +779,13 @@ fn mergeSections(self: *Wasm) !void {
761779
762 const object = self.objects.items[sym_loc.file.?];780 const object = self.objects.items[sym_loc.file.?];
763 const symbol = &object.symtable[sym_loc.index];781 const symbol = &object.symtable[sym_loc.index];
764 if (symbol.isUndefined()) {782 if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) {
765 // Skip undefined symbols as they go in the `import` section783 // Skip undefined symbols as they go in the `import` section
784 // Also skip symbols that do not need to have a section merged.
766 continue;785 continue;
767 }786 }
768787
769 const offset = object.importedCountByKind(symbol.externalType());788 const offset = object.importedCountByKind(symbol.tag.externalType());
770 const index = symbol.index - offset;789 const index = symbol.index - offset;
771 switch (symbol.tag) {790 switch (symbol.tag) {
772 .function => {791 .function => {
...@@ -776,7 +795,7 @@ fn mergeSections(self: *Wasm) !void {...@@ -776,7 +795,7 @@ fn mergeSections(self: *Wasm) !void {
776 },795 },
777 .global => {796 .global => {
778 const original_global = object.globals[index];797 const original_global = object.globals[index];
779 symbol.index = @intCast(u32, self.globals.items.len) + self.imported_globals_count;798 symbol.index = @intCast(u32, self.wasm_globals.items.len) + self.imported_globals_count;
780 try self.wasm_globals.append(self.base.allocator, original_global);799 try self.wasm_globals.append(self.base.allocator, original_global);
781 },800 },
782 .table => {801 .table => {
...@@ -790,7 +809,7 @@ fn mergeSections(self: *Wasm) !void {...@@ -790,7 +809,7 @@ fn mergeSections(self: *Wasm) !void {
790809
791 log.debug("Merged ({d}) functions", .{self.functions.items.len});810 log.debug("Merged ({d}) functions", .{self.functions.items.len});
792 log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len});811 log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len});
793 log.debug("Merged ({d}) tables", .{self.tables.tems.len});812 log.debug("Merged ({d}) tables", .{self.tables.items.len});
794}813}
795814
796/// Merges function types of all object files into the final815/// Merges function types of all object files into the final
...@@ -811,13 +830,13 @@ fn mergeTypes(self: *Wasm) !void {...@@ -811,13 +830,13 @@ fn mergeTypes(self: *Wasm) !void {
811830
812 if (symbol.isUndefined()) {831 if (symbol.isUndefined()) {
813 log.debug("Adding type from extern function '{s}'", .{symbol.name});832 log.debug("Adding type from extern function '{s}'", .{symbol.name});
814 const import: *wasm.Import = self.imports.getPtr(sym_loc);833 const import: *wasm.Import = self.imports.getPtr(sym_loc).?;
815 const original_type = object.types[import.kind.function];834 const original_type = object.func_types[import.kind.function];
816 import.kind.function = try self.putOrGetFuncType(original_type);835 import.kind.function = try self.putOrGetFuncType(original_type);
817 } else {836 } else {
818 log.debug("Adding type from function '{s}'", .{symbol.name});837 log.debug("Adding type from function '{s}'", .{symbol.name});
819 const func = &self.functions.items[symbol.index - self.imported_functions_count];838 const func = &self.functions.items[symbol.index - self.imported_functions_count];
820 func.type_index = try self.putOrGetFuncType(object.types[func.type_index]);839 func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]);
821 }840 }
822 }841 }
823 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len});842 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len});
...@@ -835,7 +854,11 @@ fn setupExports(self: *Wasm) !void {...@@ -835,7 +854,11 @@ fn setupExports(self: *Wasm) !void {
835 const symbol = sym_loc.getSymbol(self);854 const symbol = sym_loc.getSymbol(self);
836 if (!symbol.isExported()) continue;855 if (!symbol.isExported()) continue;
837856
838 const exp: wasm.Export = .{ .name = symbol.name, .kind = symbol.externalType(), .index = symbol.index };857 const exp: wasm.Export = .{
858 .name = mem.sliceTo(symbol.name, 0),
859 .kind = symbol.tag.externalType(),
860 .index = symbol.index,
861 };
839 log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });862 log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });
840 try self.exports.append(self.base.allocator, exp);863 try self.exports.append(self.base.allocator, exp);
841 }864 }
...@@ -948,6 +971,41 @@ fn setupMemory(self: *Wasm) !void {...@@ -948,6 +971,41 @@ fn setupMemory(self: *Wasm) !void {
948 }971 }
949}972}
950973
974/// From a given object's index and the index of the segment, returns the corresponding
975/// index of the segment within the final data section. When the segment does not yet
976/// exist, a new one will be initialized and appended. The new index will be returned in that case.
977pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 {
978 const object: Object = self.objects.items[object_index];
979 const relocatable_data = object.relocatable_data[relocatable_index];
980 const index = @intCast(u32, self.segments.items.len);
981
982 switch (relocatable_data.type) {
983 .data => {
984 const segment_info = object.segment_info[relocatable_data.index];
985 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName());
986 if (!result.found_existing) {
987 result.value_ptr.* = index;
988 try self.segments.append(self.base.allocator, .{
989 .alignment = 1,
990 .size = 0,
991 .offset = 0,
992 });
993 return index;
994 } else return result.value_ptr.*;
995 },
996 .code => return self.code_section_index orelse blk: {
997 self.code_section_index = index;
998 try self.segments.append(self.base.allocator, .{
999 .alignment = 1,
1000 .size = 0,
1001 .offset = 0,
1002 });
1003 break :blk index;
1004 },
1005 .custom => return error.@"TODO: Custom section relocations for wasm",
1006 }
1007}
1008
951fn resetState(self: *Wasm) void {1009fn resetState(self: *Wasm) void {
952 for (self.segment_info.items) |*segment_info| {1010 for (self.segment_info.items) |*segment_info| {
953 self.base.allocator.free(segment_info.name);1011 self.base.allocator.free(segment_info.name);
...@@ -1015,7 +1073,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1015,7 +1073,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1015 // When we finish/error we reset the state of the linker1073 // When we finish/error we reset the state of the linker
1016 // So we can rebuild the binary file on each incremental update1074 // So we can rebuild the binary file on each incremental update
1017 defer self.resetState();1075 defer self.resetState();
1018 self.setupImports();1076 try self.setupImports();
1019 var decl_it = self.decls.keyIterator();1077 var decl_it = self.decls.keyIterator();
1020 while (decl_it.next()) |decl| {1078 while (decl_it.next()) |decl| {
1021 if (decl.*.isExtern()) continue;1079 if (decl.*.isExtern()) continue;
...@@ -1050,7 +1108,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1050,7 +1108,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1050 {1108 {
1051 const header_offset = try reserveVecSectionHeader(file);1109 const header_offset = try reserveVecSectionHeader(file);
1052 const writer = file.writer();1110 const writer = file.writer();
10531111 log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len});
1054 for (self.func_types.items) |func_type| {1112 for (self.func_types.items) |func_type| {
1055 try leb.writeULEB128(writer, wasm.function_type);1113 try leb.writeULEB128(writer, wasm.function_type);
1056 try leb.writeULEB128(writer, @intCast(u32, func_type.params.len));1114 try leb.writeULEB128(writer, @intCast(u32, func_type.params.len));
...@@ -1095,8 +1153,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1095,8 +1153,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
10951153
1096 var it = self.imports.iterator();1154 var it = self.imports.iterator();
1097 while (it.next()) |entry| {1155 while (it.next()) |entry| {
1098 const import_symbol = self.symbols.items[entry.key_ptr.*];1156 assert(entry.key_ptr.*.getSymbol(self).isUndefined());
1099 std.debug.assert(import_symbol.isUndefined());
1100 const import = entry.value_ptr.*;1157 const import = entry.value_ptr.*;
1101 try emitImport(writer, import);1158 try emitImport(writer, import);
1102 }1159 }
...@@ -1207,7 +1264,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1207,7 +1264,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1207 .Fn => {1264 .Fn => {
1208 const target = exprt.exported_decl.link.wasm.sym_index;1265 const target = exprt.exported_decl.link.wasm.sym_index;
1209 const target_symbol = self.symbols.items[target];1266 const target_symbol = self.symbols.items[target];
1210 std.debug.assert(target_symbol.tag == .function);1267 assert(target_symbol.tag == .function);
1211 // Type of the export1268 // Type of the export
1212 try writer.writeByte(wasm.externalKind(.function));1269 try writer.writeByte(wasm.externalKind(.function));
1213 // Exported function index1270 // Exported function index
...@@ -1323,8 +1380,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1323,8 +1380,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1323 try writer.writeByteNTimes(0, diff);1380 try writer.writeByteNTimes(0, diff);
1324 current_offset += diff;1381 current_offset += diff;
1325 }1382 }
1326 std.debug.assert(current_offset == atom.offset);1383 assert(current_offset == atom.offset);
1327 std.debug.assert(atom.code.items.len == atom.size);1384 assert(atom.code.items.len == atom.size);
1328 try writer.writeAll(atom.code.items);1385 try writer.writeAll(atom.code.items);
13291386
1330 current_offset += atom.size;1387 current_offset += atom.size;
...@@ -1335,10 +1392,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1335,10 +1392,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1335 // segments are aligned.1392 // segments are aligned.
1336 if (current_offset != segment.size) {1393 if (current_offset != segment.size) {
1337 try writer.writeByteNTimes(0, segment.size - current_offset);1394 try writer.writeByteNTimes(0, segment.size - current_offset);
1395 current_offset += segment.size - current_offset;
1338 }1396 }
1339 break;1397 break;
1340 }1398 }
1341 }1399 }
1400 assert(current_offset == segment.size);
1342 }1401 }
13431402
1344 try writeVecSectionHeader(1403 try writeVecSectionHeader(
...@@ -1371,8 +1430,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1371,8 +1430,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
13711430
1372 for (self.symbols.items) |symbol| {1431 for (self.symbols.items) |symbol| {
1373 switch (symbol.tag) {1432 switch (symbol.tag) {
1374 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),1433 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1375 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),1434 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1376 else => {},1435 else => {},
1377 }1436 }
1378 }1437 }
src/link/Wasm/Atom.zig+11-5
...@@ -23,6 +23,9 @@ alignment: u32,...@@ -23,6 +23,9 @@ alignment: u32,
23/// Offset into the section where the atom lives, this already accounts23/// Offset into the section where the atom lives, this already accounts
24/// for alignment.24/// for alignment.
25offset: u32,25offset: u32,
26/// Represents the index of the file this atom was generated from.
27/// This is 'null' when the atom was generated by a Decl from Zig code.
28file: ?u16,
2629
27/// Next atom in relation to this atom.30/// Next atom in relation to this atom.
28/// When null, this atom is the last atom31/// When null, this atom is the last atom
...@@ -38,6 +41,7 @@ locals: std.ArrayListUnmanaged(Atom) = .{},...@@ -38,6 +41,7 @@ locals: std.ArrayListUnmanaged(Atom) = .{},
38/// Represents a default empty wasm `Atom`41/// Represents a default empty wasm `Atom`
39pub const empty: Atom = .{42pub const empty: Atom = .{
40 .alignment = 0,43 .alignment = 0,
44 .file = null,
41 .next = null,45 .next = null,
42 .offset = 0,46 .offset = 0,
43 .prev = null,47 .prev = null,
...@@ -93,16 +97,17 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {...@@ -93,16 +97,17 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
93/// Resolves the relocations within the atom, writing the new value97/// Resolves the relocations within the atom, writing the new value
94/// at the calculated offset.98/// at the calculated offset.
95pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {99pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
96 const symbol: Symbol = wasm_bin.managed_symbols.items[self.sym_index];100 const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };
101 const symbol = loc.getSymbol(wasm_bin).*;
97 log.debug("Resolving relocs in atom '{s}' count({d})", .{102 log.debug("Resolving relocs in atom '{s}' count({d})", .{
98 symbol.name,103 symbol.name,
99 self.relocs.items.len,104 self.relocs.items.len,
100 });105 });
101106
102 for (self.relocs.items) |reloc| {107 for (self.relocs.items) |reloc| {
103 const value = try relocationValue(reloc, wasm_bin);108 const value = try self.relocationValue(reloc, wasm_bin);
104 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{109 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
105 wasm_bin.managed_symbols.items[reloc.index].name,110 (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getSymbol(wasm_bin).name,
106 symbol.name,111 symbol.name,
107 reloc.offset,112 reloc.offset,
108 value,113 value,
...@@ -138,8 +143,9 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {...@@ -138,8 +143,9 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
138/// From a given `relocation` will return the new value to be written.143/// From a given `relocation` will return the new value to be written.
139/// All values will be represented as a `u64` as all values can fit within it.144/// All values will be represented as a `u64` as all values can fit within it.
140/// The final value must be casted to the correct size.145/// The final value must be casted to the correct size.
141fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {146fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
142 const symbol: Symbol = wasm_bin.managed_symbols.items[relocation.index];147 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };
148 const symbol = target_loc.getSymbol(wasm_bin).*;
143 return switch (relocation.relocation_type) {149 return switch (relocation.relocation_type) {
144 .R_WASM_FUNCTION_INDEX_LEB => symbol.index,150 .R_WASM_FUNCTION_INDEX_LEB => symbol.index,
145 .R_WASM_TABLE_NUMBER_LEB => symbol.index,151 .R_WASM_TABLE_NUMBER_LEB => symbol.index,
src/link/Wasm/Object.zig+24-16
...@@ -131,9 +131,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -131,9 +131,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
131131
132/// Finds the import within the list of imports from a given kind and index of that kind.132/// Finds the import within the list of imports from a given kind and index of that kind.
133/// Asserts the import exists133/// Asserts the import exists
134pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) *std.wasm.Import {134pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) std.wasm.Import {
135 var i: u32 = 0;135 var i: u32 = 0;
136 return for (self.imports) |*import| {136 return for (self.imports) |import| {
137 if (std.meta.activeTag(import.kind) == import_kind) {137 if (std.meta.activeTag(import.kind) == import_kind) {
138 if (i == index) return import;138 if (i == index) return import;
139 i += 1;139 i += 1;
...@@ -681,7 +681,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -681,7 +681,7 @@ fn Parser(comptime ReaderType: type) type {
681 },681 },
682 else => {682 else => {
683 symbol.index = try leb.readULEB128(u32, reader);683 symbol.index = try leb.readULEB128(u32, reader);
684 var maybe_import: ?*std.wasm.Import = null;684 var maybe_import: ?std.wasm.Import = null;
685685
686 const is_undefined = symbol.isUndefined();686 const is_undefined = symbol.isUndefined();
687 if (is_undefined) {687 if (is_undefined) {
...@@ -791,10 +791,14 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -791,10 +791,14 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
791 .kind = relocatable_data.getSymbolKind(),791 .kind = relocatable_data.getSymbolKind(),
792 .index = @intCast(u32, relocatable_data.index),792 .index = @intCast(u32, relocatable_data.index),
793 }) orelse continue; // encountered a segment we do not create an atom for793 }) orelse continue; // encountered a segment we do not create an atom for
794 const final_index = try wasm_bin.getMatchingSegment(gpa, object_index, @intCast(u32, index));794 const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index));
795795
796 const atom = try Atom.create(gpa);796 const atom = try gpa.create(Atom);
797 errdefer atom.deinit(gpa);797 atom.* = Atom.empty;
798 errdefer {
799 atom.deinit(gpa);
800 gpa.destroy(atom);
801 }
798802
799 try wasm_bin.managed_atoms.append(gpa, atom);803 try wasm_bin.managed_atoms.append(gpa, atom);
800 atom.file = object_index;804 atom.file = object_index;
...@@ -803,19 +807,23 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -803,19 +807,23 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
803 atom.sym_index = sym_index;807 atom.sym_index = sym_index;
804808
805 const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};809 const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};
806 for (relocations) |*relocation| {810 for (relocations) |relocation| {
807 if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) {811 if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) {
808 // set the offset relative to the offset of the segment itself,812 // set the offset relative to the offset of the segment itself,
809 // rather than within the entire section.813 // rather than within the entire section.
810 relocation.offset -= relocatable_data.offset;814 var reloc = relocation;
811 try atom.relocs.append(gpa, relocation.*);815 reloc.offset -= relocatable_data.offset;
812816 try atom.relocs.append(gpa, reloc);
813 if (relocation.isTableIndex()) {817
814 try wasm_bin.elements.appendSymbol(gpa, .{818 // TODO: Automatically append the target symbol to the indirect
815 .file = object_index,819 // function table when the relocation is a table index.
816 .sym_index = relocation.index,820 //
817 });821 // if (relocation.isTableIndex()) {
818 }822 // try wasm_bin.elements.appendSymbol(gpa, .{
823 // .file = object_index,
824 // .sym_index = relocation.index,
825 // });
826 // }
819 }827 }
820 }828 }
821829
src/link/Wasm/Symbol.zig+1-1
...@@ -78,7 +78,7 @@ pub const Flag = enum(u32) {...@@ -78,7 +78,7 @@ pub const Flag = enum(u32) {
78pub fn requiresImport(self: Symbol) bool {78pub fn requiresImport(self: Symbol) bool {
79 if (!self.isUndefined()) return false;79 if (!self.isUndefined()) return false;
80 if (self.isWeak()) return false;80 if (self.isWeak()) return false;
81 if (self.kind == .data) return false;81 if (self.tag == .data) return false;
82 // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib82 // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib
8383
84 return true;84 return true;