Commit f1cc5f33e8
Changed files (4)
src
link
src/link/Wasm/Atom.zig
@@ -50,7 +50,7 @@ pub fn deinit(self: *Atom, gpa: Allocator) void {
self.relocs.deinit(gpa);
self.code.deinit(gpa);
- while (self.locals.items) |*local| {
+ for (self.locals.items) |*local| {
local.deinit(gpa);
}
self.locals.deinit(gpa);
@@ -93,7 +93,7 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
/// Resolves the relocations within the atom, writing the new value
/// at the calculated offset.
pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
- const symbol: Symbol = wasm_bin.symbols.items[self.sym_index];
+ const symbol: Symbol = wasm_bin.managed_symbols.items[self.sym_index];
log.debug("Resolving relocs in atom '{s}' count({d})", .{
symbol.name,
self.relocs.items.len,
@@ -102,7 +102,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
for (self.relocs.items) |reloc| {
const value = try relocationValue(reloc, wasm_bin);
log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
- wasm_bin.symbols.items[reloc.index].name,
+ wasm_bin.managed_symbols.items[reloc.index].name,
symbol.name,
reloc.offset,
value,
@@ -139,7 +139,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
/// All values will be represented as a `u64` as all values can fit within it.
/// The final value must be casted to the correct size.
fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
- const symbol: Symbol = wasm_bin.symbols.items[relocation.index];
+ const symbol: Symbol = wasm_bin.managed_symbols.items[relocation.index];
return switch (relocation.relocation_type) {
.R_WASM_FUNCTION_INDEX_LEB => symbol.index,
.R_WASM_TABLE_NUMBER_LEB => symbol.index,
src/link/Wasm/Object.zig
@@ -6,14 +6,14 @@ const Object = @This();
const Atom = @import("Atom.zig");
const types = @import("types.zig");
const std = @import("std");
-const Wasm = @import("Wasm.zig");
+const Wasm = @import("../Wasm.zig");
const Symbol = @import("Symbol.zig");
const Allocator = std.mem.Allocator;
const leb = std.leb;
const meta = std.meta;
-const log = std.log.scoped(.zwld);
+const log = std.log.scoped(.link);
/// Wasm spec version used for this `Object`
version: u32 = 0,
@@ -26,7 +26,7 @@ file: ?std.fs.File = null,
/// Name (read path) of the object file.
name: []const u8,
/// Parsed type section
-types: []const std.wasm.Type = &.{},
+func_types: []const std.wasm.Type = &.{},
/// A list of all imports for this module
imports: []std.wasm.Import = &.{},
/// Parsed function section
@@ -104,7 +104,8 @@ const RelocatableData = struct {
pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
/// Initializes a new `Object` from a wasm object file.
-pub fn init(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Object {
+/// This also parses and verifies the object file.
+pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Object {
var object: Object = .{
.file = file,
.name = path,
@@ -161,7 +162,7 @@ pub fn getTable(self: *const Object, id: u32) *std.wasm.Table {
/// we initialize a new table symbol that corresponds to that import and return that symbol.
///
/// When the object file is *NOT* MVP, we return `null`.
-fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {
+fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {
var table_count: usize = 0;
for (self.symtable) |sym| {
if (sym.tag == .table) table_count += 1;
@@ -204,7 +205,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object) !?Symbol {
var table_symbol: Symbol = .{
.flags = 0,
- .name = table_import.name,
+ .name = try gpa.dupeZ(u8, table_import.name),
.tag = .table,
.index = 0,
};
@@ -310,7 +311,7 @@ fn Parser(comptime ReaderType: type) type {
}
},
.type => {
- for (try readVec(&self.object.types, reader, gpa)) |*type_val| {
+ for (try readVec(&self.object.func_types, reader, gpa)) |*type_val| {
if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType;
for (try readVec(&type_val.params, reader, gpa)) |*param| {
@@ -636,7 +637,7 @@ fn Parser(comptime ReaderType: type) type {
// we found all symbols, check for indirect function table
// in case of an MVP object file
- if (try self.object.checkLegacyIndirectFunctionTable()) |symbol| {
+ if (try self.object.checkLegacyIndirectFunctionTable(gpa)) |symbol| {
try symbols.append(symbol);
log.debug("Found legacy indirect function table. Created symbol", .{});
}
@@ -662,7 +663,7 @@ fn Parser(comptime ReaderType: type) type {
switch (tag) {
.data => {
const name_len = try leb.readULEB128(u32, reader);
- const name = try gpa.alloc(u8, name_len);
+ const name = try gpa.allocSentinel(u8, name_len, 0);
try reader.readNoEof(name);
symbol.name = name;
@@ -684,16 +685,16 @@ fn Parser(comptime ReaderType: type) type {
const is_undefined = symbol.isUndefined();
if (is_undefined) {
- maybe_import = self.object.findImport(symbol.externalType(), symbol.index);
+ maybe_import = self.object.findImport(symbol.tag.externalType(), symbol.index);
}
const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);
if (!(is_undefined and !explicit_name)) {
const name_len = try leb.readULEB128(u32, reader);
- const name = try gpa.alloc(u8, name_len);
+ const name = try gpa.allocSentinel(u8, name_len, 0);
try reader.readNoEof(name);
symbol.name = name;
} else {
- symbol.name = maybe_import.?.name;
+ symbol.name = try gpa.dupeZ(u8, maybe_import.?.name);
}
},
}
@@ -818,7 +819,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
}
}
- // TODO: Replace `atom.code` from an existing slice to a pointer to the data
try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
src/link/Wasm/Symbol.zig
@@ -38,6 +38,7 @@ pub const Tag = enum {
.data => .memory,
.section => unreachable, // Not an external type
.event => unreachable, // Not an external type
+ .dead => unreachable, // Dead symbols should not be referenced
.table => .table,
};
}
src/link/Wasm.zig
@@ -45,20 +45,29 @@ host_name: []const u8 = "env",
/// This is ment for bookkeeping so we can safely cleanup all codegen memory
/// when calling `deinit`
decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{},
-/// List of all symbols.
+/// List of all symbols generated by Zig code.
symbols: std.ArrayListUnmanaged(Symbol) = .{},
/// List of symbol indexes which are free to be used.
symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
/// Maps atoms to their segment index
atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
+/// Atoms managed and created by the linker. This contains atoms
+/// from object files, and not Atoms generated by a Decl.
+managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
/// Represents the index into `segments` where the 'code' section
/// lives.
code_section_index: ?u32 = null,
/// The count of imported functions. This number will be appended
/// to the function indexes as their index starts at the lowest non-extern function.
imported_functions_count: u32 = 0,
-/// Map of symbol indexes, represented by its `wasm.Import`
-imports: std.AutoHashMapUnmanaged(u32, wasm.Import) = .{},
+/// The count of imported wasm globals. This number will be appended
+/// to the global indexes when sections are merged.
+imported_globals_count: u32 = 0,
+/// The count of imported tables. This number will be appended
+/// to the table indexes when sections are merged.
+imported_tables_count: u32 = 0,
+/// Map of symbol locations, represented by its `wasm.Import`
+imports: std.AutoHashMapUnmanaged(SymbolLoc, wasm.Import) = .{},
/// Represents non-synthetic section entries.
/// Used for code, data and custom sections.
segments: std.ArrayListUnmanaged(Segment) = .{},
@@ -77,6 +86,10 @@ functions: std.ArrayListUnmanaged(wasm.Func) = .{},
wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{},
/// Memory section
memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
+/// Output table section
+tables: std.ArrayListUnmanaged(wasm.Table) = .{},
+/// Output export section
+exports: std.ArrayListUnmanaged(wasm.Export) = .{},
/// Indirect function table, used to call function pointers
/// When this is non-zero, we must emit a table entry,
@@ -87,14 +100,14 @@ function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},
/// All object files and their data which are linked into the final binary
objects: std.ArrayListUnmanaged(Object) = .{},
+/// A map of global names to their symbol location
+globals: std.StringHashMapUnmanaged(SymbolLoc) = .{},
/// Maps discarded symbols and their positions to the location of the symbol
/// it was resolved to
discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
-/// Mapping between symbol names and their respective location.
-/// This map contains all symbols that will be written into the final binary
-/// and were either defined, or resolved.
-/// TODO: Use string interning and make the key an index, rather than a unique string.
-symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
+/// List of all symbol locations which have been resolved by the linker and will be emit
+/// into the final binary.
+resolved_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
pub const Segment = struct {
alignment: u32,
@@ -116,6 +129,18 @@ pub const SymbolLoc = struct {
/// The index of the object file where the symbol resides.
/// When this is `null` the symbol comes from a non-object file.
file: ?u16,
+
+ /// From a given location, returns the corresponding symbol in the wasm binary
+ pub fn getSymbol(self: SymbolLoc, wasm_bin: *const Wasm) *Symbol {
+ if (self.file) |object_index| {
+ if (wasm_bin.discarded.get(self)) |old_loc| {
+ return old_loc.getSymbol(wasm_bin);
+ }
+ const object = wasm_bin.objects.items[object_index];
+ return &object.symtable[self.index];
+ }
+ return &wasm_bin.symbols.items[self.index];
+ }
};
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 {
const file = try fs.cwd().openFile(path, .{});
errdefer file.close();
- var object = Object.init(self.base.allocator, file, path) catch |err| {
- if (err == error.InvalidMagicByte) {
+ var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) {
+ error.InvalidMagicByte, error.NotObjectFile => {
log.warn("Self hosted linker does not support non-object file parsing", .{});
return false;
- } else return err;
+ },
+ else => |e| return e,
};
errdefer object.deinit(self.base.allocator);
try self.objects.append(self.base.allocator, object);
return true;
}
+fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
+ const object: Object = self.objects.items[object_index];
+ log.debug("Resolving symbols in object: '{s}'", .{object.name});
+
+ for (object.symtable) |symbol, i| {
+ const sym_index = @intCast(u32, i);
+ const location: SymbolLoc = .{
+ .file = object_index,
+ .index = sym_index,
+ };
+
+ if (symbol.isLocal()) {
+ if (symbol.isUndefined()) {
+ log.err("Local symbols are not allowed to reference imports", .{});
+ log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });
+ return error.undefinedLocal;
+ }
+ try self.resolved_symbols.append(self.base.allocator, location);
+ continue;
+ }
+
+ // TODO: locals are allowed to have duplicate symbol names
+ // TODO: Store undefined symbols so we can verify at the end if they've all been found
+ // if not, emit an error (unless --allow-undefined is enabled).
+ const maybe_existing = try self.globals.getOrPut(self.base.allocator, std.mem.sliceTo(symbol.name, 0));
+ if (!maybe_existing.found_existing) {
+ maybe_existing.value_ptr.* = location;
+
+ try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
+ continue;
+ }
+
+ const existing_loc = maybe_existing.value_ptr.*;
+ const existing_sym: *Symbol = existing_loc.getSymbol(self);
+
+ if (!existing_sym.isUndefined()) {
+ if (!symbol.isUndefined()) {
+ log.err("symbol '{s}' defined multiple times", .{existing_sym.name});
+ log.err(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
+ log.err(" next definition in '{s}'", .{object.name});
+ return error.SymbolCollision;
+ }
+
+ continue; // Do not overwrite defined symbols with undefined symbols
+ }
+
+ // simply overwrite with the new symbol
+ log.info("Overwriting symbol '{s}'", .{symbol.name});
+ log.info(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
+ log.info(" next definition in '{s}'", .{object.name});
+ try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
+ maybe_existing.value_ptr.* = location;
+ try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
+ }
+}
+
pub fn deinit(self: *Wasm) void {
+ const gpa = self.base.allocator;
if (build_options.have_llvm) {
- if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
+ if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
}
var decl_it = self.decls.keyIterator();
while (decl_it.next()) |decl_ptr| {
const decl = decl_ptr.*;
- decl.link.wasm.deinit(self.base.allocator);
+ decl.link.wasm.deinit(gpa);
}
for (self.func_types.items) |*func_type| {
- func_type.deinit(self.base.allocator);
+ func_type.deinit(gpa);
}
for (self.segment_info.items) |segment_info| {
- self.base.allocator.free(segment_info.name);
+ gpa.free(segment_info.name);
+ }
+ for (self.objects.items) |*object| {
+ object.file.?.close();
+ object.deinit(gpa);
}
- self.decls.deinit(self.base.allocator);
- self.symbols.deinit(self.base.allocator);
- self.symbols_free_list.deinit(self.base.allocator);
- self.atoms.deinit(self.base.allocator);
- self.segments.deinit(self.base.allocator);
- self.data_segments.deinit(self.base.allocator);
- self.segment_info.deinit(self.base.allocator);
+ self.decls.deinit(gpa);
+ self.symbols.deinit(gpa);
+ self.symbols_free_list.deinit(gpa);
+ self.globals.deinit(gpa);
+ self.resolved_symbols.deinit(gpa);
+ self.discarded.deinit(gpa);
+ self.atoms.deinit(gpa);
+ self.managed_atoms.deinit(gpa);
+ self.segments.deinit(gpa);
+ self.data_segments.deinit(gpa);
+ self.segment_info.deinit(gpa);
+ self.objects.deinit(gpa);
// free output sections
- self.imports.deinit(self.base.allocator);
- self.func_types.deinit(self.base.allocator);
- self.functions.deinit(self.base.allocator);
- self.wasm_globals.deinit(self.base.allocator);
- self.function_table.deinit(self.base.allocator);
+ self.imports.deinit(gpa);
+ self.func_types.deinit(gpa);
+ self.functions.deinit(gpa);
+ self.wasm_globals.deinit(gpa);
+ self.function_table.deinit(gpa);
+ self.tables.deinit(gpa);
+ self.exports.deinit(gpa);
}
pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
@@ -453,7 +547,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
}
if (decl.isExtern()) {
- const import = self.imports.fetchRemove(atom.sym_index).?.value;
+ const import = self.imports.fetchRemove(.{ .file = null, .index = atom.sym_index }).?.value;
switch (import.kind) {
.function => self.imported_functions_count -= 1,
else => unreachable,
@@ -469,6 +563,9 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
try self.function_table.put(self.base.allocator, symbol_index, index);
}
+/// Assigns indexes to all indirect functions.
+/// Starts at offset 1, where the value `0` represents an unresolved function pointer
+/// or null-pointer
fn mapFunctionTable(self: *Wasm) void {
var it = self.function_table.valueIterator();
var index: u32 = 1;
@@ -484,7 +581,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
symbol.setUndefined(true);
switch (decl.ty.zigTypeTag()) {
.Fn => {
- const gop = try self.imports.getOrPut(self.base.allocator, symbol_index);
+ const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {
break :blk std.mem.sliceTo(lib_name, 0);
} else self.host_name;
@@ -599,21 +696,176 @@ fn allocateAtoms(self: *Wasm) !void {
}
fn setupImports(self: *Wasm) void {
+ for (self.resolved_symbols.items) |symbol_loc| {
+ if (symbol_loc.file == null) {
+ // imports generated by Zig code are already in the `import` section
+ continue;
+ }
+
+ const symbol = symbol_loc.getSymbol(self);
+ if (symbol.tag == .data or !symbol.requiresImport()) {
+ continue;
+ }
+
+ log.debug("Symbol '{s}' will be imported from the host", .{symbol.name});
+ const import = self.objects.items[symbol_loc.file.?].findImport(symbol.externalType(), symbol.index);
+ // TODO: De-duplicate imports
+ try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
+ }
+
+ // Assign all indexes of the imports to their representing symbols
var function_index: u32 = 0;
+ var global_index: u32 = 0;
+ var table_index: u32 = 0;
var it = self.imports.iterator();
while (it.next()) |entry| {
- const symbol = &self.symbols.items[entry.key_ptr.*];
+ const symbol = entry.key_ptr.*.getSymbol(self);
const import: wasm.Import = entry.value_ptr.*;
switch (import.kind) {
.function => {
symbol.index = function_index;
function_index += 1;
},
+ .global => {
+ symbol.index = global_index;
+ global_index += 1;
+ },
+ .table => {
+ symbol.index = table_index;
+ table_index += 1;
+ },
else => unreachable,
}
}
}
+/// Takes the global, function and table section from each linked object file
+/// and merges it into a single section for each.
+fn mergeSections(self: *Wasm) !void {
+ // append the indirect function table if initialized
+ if (self.globals.get("__indirect_function_table")) |sym_loc| {
+ const table: wasm.Table = .{
+ .limits = .{ .min = @intCast(u32, self.function_table.count()), .max = null },
+ .reftype = .funcref,
+ };
+ sym_loc.getSymbol(self).index = @intCast(u32, self.tables.items.len) + self.imported_tables_count;
+ try self.tables.append(self.base.allocator, table);
+ }
+
+ for (self.resolved_symbols.items) |sym_loc| {
+ if (sym_loc.file == null) {
+ // Zig code-generated symbols are already within the sections and do not
+ // require to be merged
+ continue;
+ }
+
+ const object = self.objects.items[sym_loc.file.?];
+ const symbol = &object.symtable[sym_loc.index];
+ if (symbol.isUndefined()) {
+ // Skip undefined symbols as they go in the `import` section
+ continue;
+ }
+
+ const offset = object.importedCountByKind(symbol.externalType());
+ const index = symbol.index - offset;
+ switch (symbol.tag) {
+ .function => {
+ const original_func = object.functions[index];
+ symbol.index = @intCast(u32, self.functions.items.len) + self.imported_functions_count;
+ try self.functions.append(self.base.allocator, original_func);
+ },
+ .global => {
+ const original_global = object.globals[index];
+ symbol.index = @intCast(u32, self.globals.items.len) + self.imported_globals_count;
+ try self.wasm_globals.append(self.base.allocator, original_global);
+ },
+ .table => {
+ const original_table = object.tables[index];
+ symbol.index = @intCast(u32, self.tables.items.len) + self.imported_tables_count;
+ try self.tables.append(self.base.allocator, original_table);
+ },
+ else => {},
+ }
+ }
+
+ log.debug("Merged ({d}) functions", .{self.functions.items.len});
+ log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len});
+ log.debug("Merged ({d}) tables", .{self.tables.tems.len});
+}
+
+/// Merges function types of all object files into the final
+/// 'types' section, while assigning the type index to the representing
+/// section (import, export, function).
+fn mergeTypes(self: *Wasm) !void {
+ for (self.resolved_symbols.items) |sym_loc| {
+ if (sym_loc.file == null) {
+ // zig code-generated symbols are already present in final type section
+ continue;
+ }
+ const object = self.objects.items[sym_loc.file.?];
+ const symbol = object.symtable[sym_loc.index];
+ if (symbol.tag != .function) {
+ // Only functions have types
+ continue;
+ }
+
+ if (symbol.isUndefined()) {
+ log.debug("Adding type from extern function '{s}'", .{symbol.name});
+ const import: *wasm.Import = self.imports.getPtr(sym_loc);
+ const original_type = object.types[import.kind.function];
+ import.kind.function = try self.putOrGetFuncType(original_type);
+ } else {
+ log.debug("Adding type from function '{s}'", .{symbol.name});
+ const func = &self.functions.items[symbol.index - self.imported_functions_count];
+ func.type_index = try self.putOrGetFuncType(object.types[func.type_index]);
+ }
+ }
+ log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len});
+}
+
+fn setupExports(self: *Wasm) !void {
+ log.debug("Building exports from symbols", .{});
+
+ // When importing memory option if false, we export it instead
+ if (!self.base.options.import_memory) {
+ try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });
+ }
+
+ for (self.resolved_symbols.items) |sym_loc| {
+ const symbol = sym_loc.getSymbol(self);
+ if (!symbol.isExported()) continue;
+
+ const exp: wasm.Export = .{ .name = symbol.name, .kind = symbol.externalType(), .index = symbol.index };
+ log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });
+ try self.exports.append(self.base.allocator, exp);
+ }
+
+ log.debug("Completed building exports. Total count: ({d})", .{self.exports.items.len});
+}
+
+fn setupStart(self: *Wasm) !void {
+ const entry_name = self.base.options.entry orelse "_start";
+
+ const symbol_loc = self.globals.get(entry_name) orelse {
+ if (self.base.options.output_mode == .Exe) {
+ if (self.base.options.wasi_exec_model == .reactor) return; // Not required for reactors
+ } else {
+ return; // No entry point needed for non-executable wasm files
+ }
+ log.err("Entry symbol '{s}' missing", .{entry_name});
+ return error.MissingSymbol;
+ };
+
+ const symbol = symbol_loc.getSymbol(self);
+ if (symbol.tag != .function) {
+ log.err("Entry symbol '{s}' is not a function", .{entry_name});
+ return error.InvalidEntryKind;
+ }
+
+ // Ensure the symbol is exported so host environment can access it
+ symbol.setFlag(.WASM_SYM_EXPORTED);
+}
+
/// Sets up the memory section of the wasm module, as well as the stack.
fn setupMemory(self: *Wasm) !void {
log.debug("Setting up memory layout", .{});
@@ -754,6 +1006,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// TODO: Also link with other objects such as compiler-rt
try self.parseInputFiles(positionals.items);
+ var object_index: u16 = 0;
+ while (object_index < self.objects.items.len) : (object_index += 1) {
+ try self.resolveSymbolsInObject(object_index);
+ try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
+ }
+
// When we finish/error we reset the state of the linker
// So we can rebuild the binary file on each incremental update
defer self.resetState();
@@ -777,6 +1035,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
try self.setupMemory();
try self.allocateAtoms();
self.mapFunctionTable();
+ try self.mergeSections();
+ try self.mergeTypes();
+ try self.setupExports();
const file = self.base.file.?;
const header_size = 5 + 1;