| ... | @@ -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 memory | 45 | /// This is ment for bookkeeping so we can safely cleanup all codegen memory |
| 46 | /// when calling `deinit` | 46 | /// when calling `deinit` |
| 47 | decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{}, | 47 | decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{}, |
| 48 | /// List of all symbols. | 48 | /// List of all symbols generated by Zig code. |
| 49 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, | 49 | symbols: 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. |
| 51 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, | 51 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 52 | /// Maps atoms to their segment index | 52 | /// Maps atoms to their segment index |
| 53 | atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | 53 | atoms: 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. |
| | 56 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
| 54 | /// Represents the index into `segments` where the 'code' section | 57 | /// Represents the index into `segments` where the 'code' section |
| 55 | /// lives. | 58 | /// lives. |
| 56 | code_section_index: ?u32 = null, | 59 | code_section_index: ?u32 = null, |
| 57 | /// The count of imported functions. This number will be appended | 60 | /// 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. |
| 59 | imported_functions_count: u32 = 0, | 62 | imported_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 |
| 61 | imports: std.AutoHashMapUnmanaged(u32, wasm.Import) = .{}, | 64 | /// to the global indexes when sections are merged. |
| | 65 | imported_globals_count: u32 = 0, |
| | 66 | /// The count of imported tables. This number will be appended |
| | 67 | /// to the table indexes when sections are merged. |
| | 68 | imported_tables_count: u32 = 0, |
| | 69 | /// Map of symbol locations, represented by its `wasm.Import` |
| | 70 | imports: 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. |
| 64 | segments: std.ArrayListUnmanaged(Segment) = .{}, | 73 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| ... | @@ -77,6 +86,10 @@ functions: std.ArrayListUnmanaged(wasm.Func) = .{}, | ... | @@ -77,6 +86,10 @@ functions: std.ArrayListUnmanaged(wasm.Func) = .{}, |
| 77 | wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{}, | 86 | wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{}, |
| 78 | /// Memory section | 87 | /// Memory section |
| 79 | memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, | 88 | memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, |
| | 89 | /// Output table section |
| | 90 | tables: std.ArrayListUnmanaged(wasm.Table) = .{}, |
| | 91 | /// Output export section |
| | 92 | exports: std.ArrayListUnmanaged(wasm.Export) = .{}, |
| 80 | | 93 | |
| 81 | /// Indirect function table, used to call function pointers | 94 | /// 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) = .{}, |
| 87 | | 100 | |
| 88 | /// All object files and their data which are linked into the final binary | 101 | /// All object files and their data which are linked into the final binary |
| 89 | objects: std.ArrayListUnmanaged(Object) = .{}, | 102 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| | 103 | /// A map of global names to their symbol location |
| | 104 | globals: std.StringHashMapUnmanaged(SymbolLoc) = .{}, |
| 90 | /// Maps discarded symbols and their positions to the location of the symbol | 105 | /// Maps discarded symbols and their positions to the location of the symbol |
| 91 | /// it was resolved to | 106 | /// it was resolved to |
| 92 | discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{}, | 107 | discarded: 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 binary | 109 | /// into the final binary. |
| 95 | /// and were either defined, or resolved. | 110 | resolved_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{}, |
| 96 | /// TODO: Use string interning and make the key an index, rather than a unique string. | | |
| 97 | symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{}, | | |
| 98 | | 111 | |
| 99 | pub const Segment = struct { | 112 | pub 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 | }; |
| 120 | | 145 | |
| 121 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { | 146 | pub 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(); |
| 188 | | 213 | |
| 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 | } |
| 199 | | 225 | |
| | 226 | fn 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 | |
| 200 | pub fn deinit(self: *Wasm) void { | 282 | pub 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 | } |
| 204 | | 287 | |
| 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 | } |
| 210 | | 293 | |
| 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 | } |
| 217 | | 304 | |
| 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); |
| 225 | | 317 | |
| 226 | // free output sections | 318 | // 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 | } |
| 233 | | 327 | |
| 234 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | 328 | pub 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 | } |
| 454 | | 548 | |
| 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 | } |
| 471 | | 565 | |
| | 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 |
| 472 | fn mapFunctionTable(self: *Wasm) void { | 569 | fn 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 | } |
| 600 | | 697 | |
| 601 | fn setupImports(self: *Wasm) void { | 698 | fn 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 | } |
| 616 | | 741 | |
| | 742 | /// Takes the global, function and table section from each linked object file |
| | 743 | /// and merges it into a single section for each. |
| | 744 | fn 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). |
| | 799 | fn 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 | |
| | 826 | fn 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 | |
| | 846 | fn 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. |
| 618 | fn setupMemory(self: *Wasm) !void { | 870 | fn 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-rt | 1006 | // TODO: Also link with other objects such as compiler-rt |
| 755 | try self.parseInputFiles(positionals.items); | 1007 | try self.parseInputFiles(positionals.items); |
| 756 | | 1008 | |
| | 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 linker | 1015 | // When we finish/error we reset the state of the linker |
| 758 | // So we can rebuild the binary file on each incremental update | 1016 | // 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(); |
| 780 | | 1041 | |
| 781 | const file = self.base.file.?; | 1042 | const file = self.base.file.?; |
| 782 | const header_size = 5 + 1; | 1043 | const header_size = 5 + 1; |