authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-14 21:50:55+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-17 18:11:48+01:00
logf1cc5f33e88a64e30695c682177e00af4310a119
treef19af2ca6d49ba46d747bd5f1d91a08a3435d25b
parente7be0bef43e5fc7d19bbe184b9dc5209f52f745c

wasm-linker: Implement section merging

This implements the merging of all sections, to generate a valid wasm binary where all symbols have been resolved and their respective sections have been merged into the final binary.

4 files changed, 309 insertions(+), 47 deletions(-)

src/link/Wasm.zig+291-30
...@@ -45,20 +45,29 @@ host_name: []const u8 = "env",...@@ -45,20 +45,29 @@ host_name: []const u8 = "env",
45/// This is ment for bookkeeping so we can safely cleanup all codegen memory45/// This is ment for bookkeeping so we can safely cleanup all codegen memory
46/// when calling `deinit`46/// when calling `deinit`
47decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{},47decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{},
48/// List of all symbols.48/// List of all symbols generated by Zig code.
49symbols: std.ArrayListUnmanaged(Symbol) = .{},49symbols: std.ArrayListUnmanaged(Symbol) = .{},
50/// List of symbol indexes which are free to be used.50/// List of symbol indexes which are free to be used.
51symbols_free_list: std.ArrayListUnmanaged(u32) = .{},51symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
52/// Maps atoms to their segment index52/// Maps atoms to their segment index
53atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},53atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
54/// Atoms managed and created by the linker. This contains atoms
55/// from object files, and not Atoms generated by a Decl.
56managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
54/// Represents the index into `segments` where the 'code' section57/// Represents the index into `segments` where the 'code' section
55/// lives.58/// lives.
56code_section_index: ?u32 = null,59code_section_index: ?u32 = null,
57/// The count of imported functions. This number will be appended60/// The count of imported functions. This number will be appended
58/// to the function indexes as their index starts at the lowest non-extern function.61/// to the function indexes as their index starts at the lowest non-extern function.
59imported_functions_count: u32 = 0,62imported_functions_count: u32 = 0,
60/// Map of symbol indexes, represented by its `wasm.Import`63/// The count of imported wasm globals. This number will be appended
61imports: std.AutoHashMapUnmanaged(u32, wasm.Import) = .{},64/// to the global indexes when sections are merged.
65imported_globals_count: u32 = 0,
66/// The count of imported tables. This number will be appended
67/// to the table indexes when sections are merged.
68imported_tables_count: u32 = 0,
69/// Map of symbol locations, represented by its `wasm.Import`
70imports: std.AutoHashMapUnmanaged(SymbolLoc, wasm.Import) = .{},
62/// Represents non-synthetic section entries.71/// Represents non-synthetic section entries.
63/// Used for code, data and custom sections.72/// Used for code, data and custom sections.
64segments: std.ArrayListUnmanaged(Segment) = .{},73segments: std.ArrayListUnmanaged(Segment) = .{},
...@@ -77,6 +86,10 @@ functions: std.ArrayListUnmanaged(wasm.Func) = .{},...@@ -77,6 +86,10 @@ functions: std.ArrayListUnmanaged(wasm.Func) = .{},
77wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{},86wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{},
78/// Memory section87/// Memory section
79memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },88memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
89/// Output table section
90tables: std.ArrayListUnmanaged(wasm.Table) = .{},
91/// Output export section
92exports: std.ArrayListUnmanaged(wasm.Export) = .{},
8093
81/// Indirect function table, used to call function pointers94/// Indirect function table, used to call function pointers
82/// When this is non-zero, we must emit a table entry,95/// When this is non-zero, we must emit a table entry,
...@@ -87,14 +100,14 @@ function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},...@@ -87,14 +100,14 @@ function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},
87100
88/// All object files and their data which are linked into the final binary101/// All object files and their data which are linked into the final binary
89objects: std.ArrayListUnmanaged(Object) = .{},102objects: std.ArrayListUnmanaged(Object) = .{},
103/// A map of global names to their symbol location
104globals: std.StringHashMapUnmanaged(SymbolLoc) = .{},
90/// Maps discarded symbols and their positions to the location of the symbol105/// Maps discarded symbols and their positions to the location of the symbol
91/// it was resolved to106/// it was resolved to
92discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},107discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
93/// Mapping between symbol names and their respective location.108/// List of all symbol locations which have been resolved by the linker and will be emit
94/// This map contains all symbols that will be written into the final binary109/// into the final binary.
95/// and were either defined, or resolved.110resolved_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
96/// TODO: Use string interning and make the key an index, rather than a unique string.
97symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
98111
99pub const Segment = struct {112pub const Segment = struct {
100 alignment: u32,113 alignment: u32,
...@@ -116,6 +129,18 @@ pub const SymbolLoc = struct {...@@ -116,6 +129,18 @@ pub const SymbolLoc = struct {
116 /// The index of the object file where the symbol resides.129 /// The index of the object file where the symbol resides.
117 /// When this is `null` the symbol comes from a non-object file.130 /// When this is `null` the symbol comes from a non-object file.
118 file: ?u16,131 file: ?u16,
132
133 /// From a given location, returns the corresponding symbol in the wasm binary
134 pub fn getSymbol(self: SymbolLoc, wasm_bin: *const Wasm) *Symbol {
135 if (self.file) |object_index| {
136 if (wasm_bin.discarded.get(self)) |old_loc| {
137 return old_loc.getSymbol(wasm_bin);
138 }
139 const object = wasm_bin.objects.items[object_index];
140 return &object.symtable[self.index];
141 }
142 return &wasm_bin.symbols.items[self.index];
143 }
119};144};
120145
121pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {146pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
...@@ -186,49 +211,118 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool {...@@ -186,49 +211,118 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool {
186 const file = try fs.cwd().openFile(path, .{});211 const file = try fs.cwd().openFile(path, .{});
187 errdefer file.close();212 errdefer file.close();
188213
189 var object = Object.init(self.base.allocator, file, path) catch |err| {214 var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) {
190 if (err == error.InvalidMagicByte) {215 error.InvalidMagicByte, error.NotObjectFile => {
191 log.warn("Self hosted linker does not support non-object file parsing", .{});216 log.warn("Self hosted linker does not support non-object file parsing", .{});
192 return false;217 return false;
193 } else return err;218 },
219 else => |e| return e,
194 };220 };
195 errdefer object.deinit(self.base.allocator);221 errdefer object.deinit(self.base.allocator);
196 try self.objects.append(self.base.allocator, object);222 try self.objects.append(self.base.allocator, object);
197 return true;223 return true;
198}224}
199225
226fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
227 const object: Object = self.objects.items[object_index];
228 log.debug("Resolving symbols in object: '{s}'", .{object.name});
229
230 for (object.symtable) |symbol, i| {
231 const sym_index = @intCast(u32, i);
232 const location: SymbolLoc = .{
233 .file = object_index,
234 .index = sym_index,
235 };
236
237 if (symbol.isLocal()) {
238 if (symbol.isUndefined()) {
239 log.err("Local symbols are not allowed to reference imports", .{});
240 log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });
241 return error.undefinedLocal;
242 }
243 try self.resolved_symbols.append(self.base.allocator, location);
244 continue;
245 }
246
247 // 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 found
249 // 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));
251 if (!maybe_existing.found_existing) {
252 maybe_existing.value_ptr.* = location;
253
254 try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
255 continue;
256 }
257
258 const existing_loc = maybe_existing.value_ptr.*;
259 const existing_sym: *Symbol = existing_loc.getSymbol(self);
260
261 if (!existing_sym.isUndefined()) {
262 if (!symbol.isUndefined()) {
263 log.err("symbol '{s}' defined multiple times", .{existing_sym.name});
264 log.err(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
265 log.err(" next definition in '{s}'", .{object.name});
266 return error.SymbolCollision;
267 }
268
269 continue; // Do not overwrite defined symbols with undefined symbols
270 }
271
272 // simply overwrite with the new symbol
273 log.info("Overwriting symbol '{s}'", .{symbol.name});
274 log.info(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
275 log.info(" next definition in '{s}'", .{object.name});
276 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
277 maybe_existing.value_ptr.* = location;
278 try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
279 }
280}
281
200pub fn deinit(self: *Wasm) void {282pub fn deinit(self: *Wasm) void {
283 const gpa = self.base.allocator;
201 if (build_options.have_llvm) {284 if (build_options.have_llvm) {
202 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);285 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
203 }286 }
204287
205 var decl_it = self.decls.keyIterator();288 var decl_it = self.decls.keyIterator();
206 while (decl_it.next()) |decl_ptr| {289 while (decl_it.next()) |decl_ptr| {
207 const decl = decl_ptr.*;290 const decl = decl_ptr.*;
208 decl.link.wasm.deinit(self.base.allocator);291 decl.link.wasm.deinit(gpa);
209 }292 }
210293
211 for (self.func_types.items) |*func_type| {294 for (self.func_types.items) |*func_type| {
212 func_type.deinit(self.base.allocator);295 func_type.deinit(gpa);
213 }296 }
214 for (self.segment_info.items) |segment_info| {297 for (self.segment_info.items) |segment_info| {
215 self.base.allocator.free(segment_info.name);298 gpa.free(segment_info.name);
299 }
300 for (self.objects.items) |*object| {
301 object.file.?.close();
302 object.deinit(gpa);
216 }303 }
217304
218 self.decls.deinit(self.base.allocator);305 self.decls.deinit(gpa);
219 self.symbols.deinit(self.base.allocator);306 self.symbols.deinit(gpa);
220 self.symbols_free_list.deinit(self.base.allocator);307 self.symbols_free_list.deinit(gpa);
221 self.atoms.deinit(self.base.allocator);308 self.globals.deinit(gpa);
222 self.segments.deinit(self.base.allocator);309 self.resolved_symbols.deinit(gpa);
223 self.data_segments.deinit(self.base.allocator);310 self.discarded.deinit(gpa);
224 self.segment_info.deinit(self.base.allocator);311 self.atoms.deinit(gpa);
312 self.managed_atoms.deinit(gpa);
313 self.segments.deinit(gpa);
314 self.data_segments.deinit(gpa);
315 self.segment_info.deinit(gpa);
316 self.objects.deinit(gpa);
225317
226 // free output sections318 // free output sections
227 self.imports.deinit(self.base.allocator);319 self.imports.deinit(gpa);
228 self.func_types.deinit(self.base.allocator);320 self.func_types.deinit(gpa);
229 self.functions.deinit(self.base.allocator);321 self.functions.deinit(gpa);
230 self.wasm_globals.deinit(self.base.allocator);322 self.wasm_globals.deinit(gpa);
231 self.function_table.deinit(self.base.allocator);323 self.function_table.deinit(gpa);
324 self.tables.deinit(gpa);
325 self.exports.deinit(gpa);
232}326}
233327
234pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {328pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
...@@ -453,7 +547,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {...@@ -453,7 +547,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
453 }547 }
454548
455 if (decl.isExtern()) {549 if (decl.isExtern()) {
456 const import = self.imports.fetchRemove(atom.sym_index).?.value;550 const import = self.imports.fetchRemove(.{ .file = null, .index = atom.sym_index }).?.value;
457 switch (import.kind) {551 switch (import.kind) {
458 .function => self.imported_functions_count -= 1,552 .function => self.imported_functions_count -= 1,
459 else => unreachable,553 else => unreachable,
...@@ -469,6 +563,9 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {...@@ -469,6 +563,9 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
469 try self.function_table.put(self.base.allocator, symbol_index, index);563 try self.function_table.put(self.base.allocator, symbol_index, index);
470}564}
471565
566/// Assigns indexes to all indirect functions.
567/// Starts at offset 1, where the value `0` represents an unresolved function pointer
568/// or null-pointer
472fn mapFunctionTable(self: *Wasm) void {569fn mapFunctionTable(self: *Wasm) void {
473 var it = self.function_table.valueIterator();570 var it = self.function_table.valueIterator();
474 var index: u32 = 1;571 var index: u32 = 1;
...@@ -484,7 +581,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {...@@ -484,7 +581,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
484 symbol.setUndefined(true);581 symbol.setUndefined(true);
485 switch (decl.ty.zigTypeTag()) {582 switch (decl.ty.zigTypeTag()) {
486 .Fn => {583 .Fn => {
487 const gop = try self.imports.getOrPut(self.base.allocator, symbol_index);584 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
488 const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {585 const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {
489 break :blk std.mem.sliceTo(lib_name, 0);586 break :blk std.mem.sliceTo(lib_name, 0);
490 } else self.host_name;587 } else self.host_name;
...@@ -599,21 +696,176 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -599,21 +696,176 @@ fn allocateAtoms(self: *Wasm) !void {
599}696}
600697
601fn setupImports(self: *Wasm) void {698fn setupImports(self: *Wasm) void {
699 for (self.resolved_symbols.items) |symbol_loc| {
700 if (symbol_loc.file == null) {
701 // imports generated by Zig code are already in the `import` section
702 continue;
703 }
704
705 const symbol = symbol_loc.getSymbol(self);
706 if (symbol.tag == .data or !symbol.requiresImport()) {
707 continue;
708 }
709
710 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);
712 // TODO: De-duplicate imports
713 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
714 }
715
716 // Assign all indexes of the imports to their representing symbols
602 var function_index: u32 = 0;717 var function_index: u32 = 0;
718 var global_index: u32 = 0;
719 var table_index: u32 = 0;
603 var it = self.imports.iterator();720 var it = self.imports.iterator();
604 while (it.next()) |entry| {721 while (it.next()) |entry| {
605 const symbol = &self.symbols.items[entry.key_ptr.*];722 const symbol = entry.key_ptr.*.getSymbol(self);
606 const import: wasm.Import = entry.value_ptr.*;723 const import: wasm.Import = entry.value_ptr.*;
607 switch (import.kind) {724 switch (import.kind) {
608 .function => {725 .function => {
609 symbol.index = function_index;726 symbol.index = function_index;
610 function_index += 1;727 function_index += 1;
611 },728 },
729 .global => {
730 symbol.index = global_index;
731 global_index += 1;
732 },
733 .table => {
734 symbol.index = table_index;
735 table_index += 1;
736 },
612 else => unreachable,737 else => unreachable,
613 }738 }
614 }739 }
615}740}
616741
742/// Takes the global, function and table section from each linked object file
743/// and merges it into a single section for each.
744fn mergeSections(self: *Wasm) !void {
745 // append the indirect function table if initialized
746 if (self.globals.get("__indirect_function_table")) |sym_loc| {
747 const table: wasm.Table = .{
748 .limits = .{ .min = @intCast(u32, self.function_table.count()), .max = null },
749 .reftype = .funcref,
750 };
751 sym_loc.getSymbol(self).index = @intCast(u32, self.tables.items.len) + self.imported_tables_count;
752 try self.tables.append(self.base.allocator, table);
753 }
754
755 for (self.resolved_symbols.items) |sym_loc| {
756 if (sym_loc.file == null) {
757 // Zig code-generated symbols are already within the sections and do not
758 // require to be merged
759 continue;
760 }
761
762 const object = self.objects.items[sym_loc.file.?];
763 const symbol = &object.symtable[sym_loc.index];
764 if (symbol.isUndefined()) {
765 // Skip undefined symbols as they go in the `import` section
766 continue;
767 }
768
769 const offset = object.importedCountByKind(symbol.externalType());
770 const index = symbol.index - offset;
771 switch (symbol.tag) {
772 .function => {
773 const original_func = object.functions[index];
774 symbol.index = @intCast(u32, self.functions.items.len) + self.imported_functions_count;
775 try self.functions.append(self.base.allocator, original_func);
776 },
777 .global => {
778 const original_global = object.globals[index];
779 symbol.index = @intCast(u32, self.globals.items.len) + self.imported_globals_count;
780 try self.wasm_globals.append(self.base.allocator, original_global);
781 },
782 .table => {
783 const original_table = object.tables[index];
784 symbol.index = @intCast(u32, self.tables.items.len) + self.imported_tables_count;
785 try self.tables.append(self.base.allocator, original_table);
786 },
787 else => {},
788 }
789 }
790
791 log.debug("Merged ({d}) functions", .{self.functions.items.len});
792 log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len});
793 log.debug("Merged ({d}) tables", .{self.tables.tems.len});
794}
795
796/// Merges function types of all object files into the final
797/// 'types' section, while assigning the type index to the representing
798/// section (import, export, function).
799fn mergeTypes(self: *Wasm) !void {
800 for (self.resolved_symbols.items) |sym_loc| {
801 if (sym_loc.file == null) {
802 // zig code-generated symbols are already present in final type section
803 continue;
804 }
805 const object = self.objects.items[sym_loc.file.?];
806 const symbol = object.symtable[sym_loc.index];
807 if (symbol.tag != .function) {
808 // Only functions have types
809 continue;
810 }
811
812 if (symbol.isUndefined()) {
813 log.debug("Adding type from extern function '{s}'", .{symbol.name});
814 const import: *wasm.Import = self.imports.getPtr(sym_loc);
815 const original_type = object.types[import.kind.function];
816 import.kind.function = try self.putOrGetFuncType(original_type);
817 } else {
818 log.debug("Adding type from function '{s}'", .{symbol.name});
819 const func = &self.functions.items[symbol.index - self.imported_functions_count];
820 func.type_index = try self.putOrGetFuncType(object.types[func.type_index]);
821 }
822 }
823 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len});
824}
825
826fn setupExports(self: *Wasm) !void {
827 log.debug("Building exports from symbols", .{});
828
829 // When importing memory option if false, we export it instead
830 if (!self.base.options.import_memory) {
831 try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });
832 }
833
834 for (self.resolved_symbols.items) |sym_loc| {
835 const symbol = sym_loc.getSymbol(self);
836 if (!symbol.isExported()) continue;
837
838 const exp: wasm.Export = .{ .name = symbol.name, .kind = symbol.externalType(), .index = symbol.index };
839 log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });
840 try self.exports.append(self.base.allocator, exp);
841 }
842
843 log.debug("Completed building exports. Total count: ({d})", .{self.exports.items.len});
844}
845
846fn setupStart(self: *Wasm) !void {
847 const entry_name = self.base.options.entry orelse "_start";
848
849 const symbol_loc = self.globals.get(entry_name) orelse {
850 if (self.base.options.output_mode == .Exe) {
851 if (self.base.options.wasi_exec_model == .reactor) return; // Not required for reactors
852 } else {
853 return; // No entry point needed for non-executable wasm files
854 }
855 log.err("Entry symbol '{s}' missing", .{entry_name});
856 return error.MissingSymbol;
857 };
858
859 const symbol = symbol_loc.getSymbol(self);
860 if (symbol.tag != .function) {
861 log.err("Entry symbol '{s}' is not a function", .{entry_name});
862 return error.InvalidEntryKind;
863 }
864
865 // Ensure the symbol is exported so host environment can access it
866 symbol.setFlag(.WASM_SYM_EXPORTED);
867}
868
617/// Sets up the memory section of the wasm module, as well as the stack.869/// Sets up the memory section of the wasm module, as well as the stack.
618fn setupMemory(self: *Wasm) !void {870fn setupMemory(self: *Wasm) !void {
619 log.debug("Setting up memory layout", .{});871 log.debug("Setting up memory layout", .{});
...@@ -754,6 +1006,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -754,6 +1006,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
754 // TODO: Also link with other objects such as compiler-rt1006 // TODO: Also link with other objects such as compiler-rt
755 try self.parseInputFiles(positionals.items);1007 try self.parseInputFiles(positionals.items);
7561008
1009 var object_index: u16 = 0;
1010 while (object_index < self.objects.items.len) : (object_index += 1) {
1011 try self.resolveSymbolsInObject(object_index);
1012 try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
1013 }
1014
757 // When we finish/error we reset the state of the linker1015 // When we finish/error we reset the state of the linker
758 // So we can rebuild the binary file on each incremental update1016 // So we can rebuild the binary file on each incremental update
759 defer self.resetState();1017 defer self.resetState();
...@@ -777,6 +1035,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -777,6 +1035,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
777 try self.setupMemory();1035 try self.setupMemory();
778 try self.allocateAtoms();1036 try self.allocateAtoms();
779 self.mapFunctionTable();1037 self.mapFunctionTable();
1038 try self.mergeSections();
1039 try self.mergeTypes();
1040 try self.setupExports();
7801041
781 const file = self.base.file.?;1042 const file = self.base.file.?;
782 const header_size = 5 + 1;1043 const header_size = 5 + 1;
src/link/Wasm/Atom.zig+4-4
...@@ -50,7 +50,7 @@ pub fn deinit(self: *Atom, gpa: Allocator) void {...@@ -50,7 +50,7 @@ pub fn deinit(self: *Atom, gpa: Allocator) void {
50 self.relocs.deinit(gpa);50 self.relocs.deinit(gpa);
51 self.code.deinit(gpa);51 self.code.deinit(gpa);
5252
53 while (self.locals.items) |*local| {53 for (self.locals.items) |*local| {
54 local.deinit(gpa);54 local.deinit(gpa);
55 }55 }
56 self.locals.deinit(gpa);56 self.locals.deinit(gpa);
...@@ -93,7 +93,7 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {...@@ -93,7 +93,7 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
93/// Resolves the relocations within the atom, writing the new value93/// Resolves the relocations within the atom, writing the new value
94/// at the calculated offset.94/// at the calculated offset.
95pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {95pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
96 const symbol: Symbol = wasm_bin.symbols.items[self.sym_index];96 const symbol: Symbol = wasm_bin.managed_symbols.items[self.sym_index];
97 log.debug("Resolving relocs in atom '{s}' count({d})", .{97 log.debug("Resolving relocs in atom '{s}' count({d})", .{
98 symbol.name,98 symbol.name,
99 self.relocs.items.len,99 self.relocs.items.len,
...@@ -102,7 +102,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {...@@ -102,7 +102,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
102 for (self.relocs.items) |reloc| {102 for (self.relocs.items) |reloc| {
103 const value = try relocationValue(reloc, wasm_bin);103 const value = try relocationValue(reloc, wasm_bin);
104 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{104 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
105 wasm_bin.symbols.items[reloc.index].name,105 wasm_bin.managed_symbols.items[reloc.index].name,
106 symbol.name,106 symbol.name,
107 reloc.offset,107 reloc.offset,
108 value,108 value,
...@@ -139,7 +139,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {...@@ -139,7 +139,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
139/// All values will be represented as a `u64` as all values can fit within it.139/// 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.140/// The final value must be casted to the correct size.
141fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {141fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
142 const symbol: Symbol = wasm_bin.symbols.items[relocation.index];142 const symbol: Symbol = wasm_bin.managed_symbols.items[relocation.index];
143 return switch (relocation.relocation_type) {143 return switch (relocation.relocation_type) {
144 .R_WASM_FUNCTION_INDEX_LEB => symbol.index,144 .R_WASM_FUNCTION_INDEX_LEB => symbol.index,
145 .R_WASM_TABLE_NUMBER_LEB => symbol.index,145 .R_WASM_TABLE_NUMBER_LEB => symbol.index,
src/link/Wasm/Object.zig+13-13
...@@ -6,14 +6,14 @@ const Object = @This();...@@ -6,14 +6,14 @@ const Object = @This();
6const Atom = @import("Atom.zig");6const Atom = @import("Atom.zig");
7const types = @import("types.zig");7const types = @import("types.zig");
8const std = @import("std");8const std = @import("std");
9const Wasm = @import("Wasm.zig");9const Wasm = @import("../Wasm.zig");
10const Symbol = @import("Symbol.zig");10const Symbol = @import("Symbol.zig");
1111
12const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
13const leb = std.leb;13const leb = std.leb;
14const meta = std.meta;14const meta = std.meta;
1515
16const log = std.log.scoped(.zwld);16const log = std.log.scoped(.link);
1717
18/// Wasm spec version used for this `Object`18/// Wasm spec version used for this `Object`
19version: u32 = 0,19version: u32 = 0,
...@@ -26,7 +26,7 @@ file: ?std.fs.File = null,...@@ -26,7 +26,7 @@ file: ?std.fs.File = null,
26/// Name (read path) of the object file.26/// Name (read path) of the object file.
27name: []const u8,27name: []const u8,
28/// Parsed type section28/// Parsed type section
29types: []const std.wasm.Type = &.{},29func_types: []const std.wasm.Type = &.{},
30/// A list of all imports for this module30/// A list of all imports for this module
31imports: []std.wasm.Import = &.{},31imports: []std.wasm.Import = &.{},
32/// Parsed function section32/// Parsed function section
...@@ -104,7 +104,8 @@ const RelocatableData = struct {...@@ -104,7 +104,8 @@ const RelocatableData = struct {
104pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;104pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
105105
106/// Initializes a new `Object` from a wasm object file.106/// Initializes a new `Object` from a wasm object file.
107pub fn init(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Object {107/// This also parses and verifies the object file.
108pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Object {
108 var object: Object = .{109 var object: Object = .{
109 .file = file,110 .file = file,
110 .name = path,111 .name = path,
...@@ -161,7 +162,7 @@ pub fn getTable(self: *const Object, id: u32) *std.wasm.Table {...@@ -161,7 +162,7 @@ pub fn getTable(self: *const Object, id: u32) *std.wasm.Table {
161/// we initialize a new table symbol that corresponds to that import and return that symbol.162/// we initialize a new table symbol that corresponds to that import and return that symbol.
162///163///
163/// When the object file is *NOT* MVP, we return `null`.164/// When the object file is *NOT* MVP, we return `null`.
164fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {165fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {
165 var table_count: usize = 0;166 var table_count: usize = 0;
166 for (self.symtable) |sym| {167 for (self.symtable) |sym| {
167 if (sym.tag == .table) table_count += 1;168 if (sym.tag == .table) table_count += 1;
...@@ -204,7 +205,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {...@@ -204,7 +205,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {
204205
205 var table_symbol: Symbol = .{206 var table_symbol: Symbol = .{
206 .flags = 0,207 .flags = 0,
207 .name = table_import.name,208 .name = try gpa.dupeZ(u8, table_import.name),
208 .tag = .table,209 .tag = .table,
209 .index = 0,210 .index = 0,
210 };211 };
...@@ -310,7 +311,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -310,7 +311,7 @@ fn Parser(comptime ReaderType: type) type {
310 }311 }
311 },312 },
312 .type => {313 .type => {
313 for (try readVec(&self.object.types, reader, gpa)) |*type_val| {314 for (try readVec(&self.object.func_types, reader, gpa)) |*type_val| {
314 if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType;315 if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType;
315316
316 for (try readVec(&type_val.params, reader, gpa)) |*param| {317 for (try readVec(&type_val.params, reader, gpa)) |*param| {
...@@ -636,7 +637,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -636,7 +637,7 @@ fn Parser(comptime ReaderType: type) type {
636637
637 // we found all symbols, check for indirect function table638 // we found all symbols, check for indirect function table
638 // in case of an MVP object file639 // in case of an MVP object file
639 if (try self.object.checkLegacyIndirectFunctionTable()) |symbol| {640 if (try self.object.checkLegacyIndirectFunctionTable(gpa)) |symbol| {
640 try symbols.append(symbol);641 try symbols.append(symbol);
641 log.debug("Found legacy indirect function table. Created symbol", .{});642 log.debug("Found legacy indirect function table. Created symbol", .{});
642 }643 }
...@@ -662,7 +663,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -662,7 +663,7 @@ fn Parser(comptime ReaderType: type) type {
662 switch (tag) {663 switch (tag) {
663 .data => {664 .data => {
664 const name_len = try leb.readULEB128(u32, reader);665 const name_len = try leb.readULEB128(u32, reader);
665 const name = try gpa.alloc(u8, name_len);666 const name = try gpa.allocSentinel(u8, name_len, 0);
666 try reader.readNoEof(name);667 try reader.readNoEof(name);
667 symbol.name = name;668 symbol.name = name;
668669
...@@ -684,16 +685,16 @@ fn Parser(comptime ReaderType: type) type {...@@ -684,16 +685,16 @@ fn Parser(comptime ReaderType: type) type {
684685
685 const is_undefined = symbol.isUndefined();686 const is_undefined = symbol.isUndefined();
686 if (is_undefined) {687 if (is_undefined) {
687 maybe_import = self.object.findImport(symbol.externalType(), symbol.index);688 maybe_import = self.object.findImport(symbol.tag.externalType(), symbol.index);
688 }689 }
689 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);690 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);
690 if (!(is_undefined and !explicit_name)) {691 if (!(is_undefined and !explicit_name)) {
691 const name_len = try leb.readULEB128(u32, reader);692 const name_len = try leb.readULEB128(u32, reader);
692 const name = try gpa.alloc(u8, name_len);693 const name = try gpa.allocSentinel(u8, name_len, 0);
693 try reader.readNoEof(name);694 try reader.readNoEof(name);
694 symbol.name = name;695 symbol.name = name;
695 } else {696 } else {
696 symbol.name = maybe_import.?.name;697 symbol.name = try gpa.dupeZ(u8, maybe_import.?.name);
697 }698 }
698 },699 },
699 }700 }
...@@ -818,7 +819,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -818,7 +819,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
818 }819 }
819 }820 }
820821
821 // TODO: Replace `atom.code` from an existing slice to a pointer to the data
822 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);822 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
823823
824 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];824 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
src/link/Wasm/Symbol.zig+1
...@@ -38,6 +38,7 @@ pub const Tag = enum {...@@ -38,6 +38,7 @@ pub const Tag = enum {
38 .data => .memory,38 .data => .memory,
39 .section => unreachable, // Not an external type39 .section => unreachable, // Not an external type
40 .event => unreachable, // Not an external type40 .event => unreachable, // Not an external type
41 .dead => unreachable, // Dead symbols should not be referenced
41 .table => .table,42 .table => .table,
42 };43 };
43 }44 }