Commit ba2d006634

Andrew Kelley <andrew@ziglang.org>
2024-10-30 21:08:02
link.File.Wasm: remove the "files" abstraction
Removes the `files` field from the Wasm linker, storing the ZigObject as its own field instead using a tagged union. This removes a layer of indirection when accessing the ZigObject, and untangles logic so that we can introduce a "pre-link" phase that prepares the linker state to handle only incremental updates to the ZigObject and then minimize logic inside flush(). Furthermore, don't make array elements store their own indexes, that's always a waste. Flattens some of the file system hierarchy and unifies variable names for easier refactoring. Introduces type safety for optional object indexes.
1 parent 17a87d7
src/arch/wasm/CodeGen.zig
@@ -1291,7 +1291,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
         var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
         defer prologue.deinit();
 
-        const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym);
+        const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym);
         // load stack pointer
         try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
         // store stack pointer so we can restore it when we return from the function
@@ -1511,7 +1511,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
     try func.emitWValue(func.initial_stack_value);
 
     // save its value in the global stack pointer
-    try func.addLabel(.global_set, @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym));
+    try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym));
 }
 
 /// From a given type, will create space on the virtual stack to store the value of such type.
@@ -2262,7 +2262,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
     }
 
     if (callee) |direct| {
-        const atom_index = func.bin_file.zigObjectPtr().?.navs.get(direct).?.atom;
+        const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom;
         try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
     } else {
         // in this case we call a function pointer
@@ -2274,7 +2274,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
         var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
         defer fn_type.deinit(func.gpa);
 
-        const fn_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, fn_type);
+        const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type);
         try func.addLabel(.call_indirect, fn_type_index);
     }
 
@@ -7178,7 +7178,7 @@ fn callIntrinsic(
     const zcu = pt.zcu;
     var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);
     defer func_type.deinit(func.gpa);
-    const func_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, func_type);
+    const func_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, func_type);
     try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
 
     const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);
src/arch/wasm/Emit.zig
@@ -310,7 +310,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
     const global_offset = emit.offset();
     try emit.code.appendSlice(&buf);
 
-    const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom;
+    const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
     const atom = emit.bin_file.getAtomPtr(atom_index);
     try atom.relocs.append(gpa, .{
         .index = label,
@@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
     try emit.code.appendSlice(&buf);
 
     if (label != 0) {
-        const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom;
+        const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
         const atom = emit.bin_file.getAtomPtr(atom_index);
         try atom.relocs.append(gpa, .{
             .offset = call_offset,
@@ -390,7 +390,7 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
     leb128.writeUnsignedFixed(5, &buf, type_index);
     try emit.code.appendSlice(&buf);
     if (type_index != 0) {
-        const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom;
+        const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
         const atom = emit.bin_file.getAtomPtr(atom_index);
         try atom.relocs.append(emit.bin_file.base.comp.gpa, .{
             .offset = call_offset,
@@ -412,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
     try emit.code.appendSlice(&buf);
 
     if (symbol_index != 0) {
-        const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom;
+        const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
         const atom = emit.bin_file.getAtomPtr(atom_index);
         try atom.relocs.append(gpa, .{
             .offset = index_offset,
@@ -443,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
     }
 
     if (mem.pointer != 0) {
-        const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom;
+        const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;
         const atom = emit.bin_file.getAtomPtr(atom_index);
         try atom.relocs.append(gpa, .{
             .offset = mem_offset,
src/link/Wasm/Atom.zig
@@ -1,204 +0,0 @@
-/// Represents the index of the file this atom was generated from.
-/// This is 'null' when the atom was generated by a synthetic linker symbol.
-file: FileIndex,
-/// symbol index of the symbol representing this atom
-sym_index: Symbol.Index,
-/// Size of the atom, used to calculate section sizes in the final binary
-size: u32 = 0,
-/// List of relocations belonging to this atom
-relocs: std.ArrayListUnmanaged(types.Relocation) = .empty,
-/// Contains the binary data of an atom, which can be non-relocated
-code: std.ArrayListUnmanaged(u8) = .empty,
-/// For code this is 1, for data this is set to the highest value of all segments
-alignment: Wasm.Alignment = .@"1",
-/// Offset into the section where the atom lives, this already accounts
-/// for alignment.
-offset: u32 = 0,
-/// The original offset within the object file. This value is subtracted from
-/// relocation offsets to determine where in the `data` to rewrite the value
-original_offset: u32 = 0,
-/// Previous atom in relation to this atom.
-/// is null when this atom is the first in its order
-prev: Atom.Index = .null,
-/// Contains atoms local to a decl, all managed by this `Atom`.
-/// When the parent atom is being freed, it will also do so for all local atoms.
-locals: std.ArrayListUnmanaged(Atom.Index) = .empty,
-
-/// Represents the index of an Atom where `null` is considered
-/// an invalid atom.
-pub const Index = enum(u32) {
-    null = std.math.maxInt(u32),
-    _,
-};
-
-/// Frees all resources owned by this `Atom`.
-pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
-    atom.relocs.deinit(gpa);
-    atom.code.deinit(gpa);
-    atom.locals.deinit(gpa);
-    atom.* = undefined;
-}
-
-/// Sets the length of relocations and code to '0',
-/// effectively resetting them and allowing them to be re-populated.
-pub fn clear(atom: *Atom) void {
-    atom.relocs.clearRetainingCapacity();
-    atom.code.clearRetainingCapacity();
-}
-
-pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-    _ = fmt;
-    _ = options;
-    try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
-        @intFromEnum(atom.sym_index),
-        atom.alignment,
-        atom.size,
-        atom.offset,
-    });
-}
-
-/// Returns the location of the symbol that represents this `Atom`
-pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
-    return .{ .file = atom.file, .index = atom.sym_index };
-}
-
-/// Resolves the relocations within the atom, writing the new value
-/// at the calculated offset.
-pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
-    if (atom.relocs.items.len == 0) return;
-    const symbol_name = atom.symbolLoc().getName(wasm_bin);
-    log.debug("Resolving relocs in atom '{s}' count({d})", .{
-        symbol_name,
-        atom.relocs.items.len,
-    });
-
-    for (atom.relocs.items) |reloc| {
-        const value = atom.relocationValue(reloc, wasm_bin);
-        log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
-            (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(reloc.index) }).getName(wasm_bin),
-            symbol_name,
-            reloc.offset,
-            value,
-        });
-
-        switch (reloc.relocation_type) {
-            .R_WASM_TABLE_INDEX_I32,
-            .R_WASM_FUNCTION_OFFSET_I32,
-            .R_WASM_GLOBAL_INDEX_I32,
-            .R_WASM_MEMORY_ADDR_I32,
-            .R_WASM_SECTION_OFFSET_I32,
-            => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
-            .R_WASM_TABLE_INDEX_I64,
-            .R_WASM_MEMORY_ADDR_I64,
-            => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
-            .R_WASM_GLOBAL_INDEX_LEB,
-            .R_WASM_EVENT_INDEX_LEB,
-            .R_WASM_FUNCTION_INDEX_LEB,
-            .R_WASM_MEMORY_ADDR_LEB,
-            .R_WASM_MEMORY_ADDR_SLEB,
-            .R_WASM_TABLE_INDEX_SLEB,
-            .R_WASM_TABLE_NUMBER_LEB,
-            .R_WASM_TYPE_INDEX_LEB,
-            .R_WASM_MEMORY_ADDR_TLS_SLEB,
-            => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
-            .R_WASM_MEMORY_ADDR_LEB64,
-            .R_WASM_MEMORY_ADDR_SLEB64,
-            .R_WASM_TABLE_INDEX_SLEB64,
-            .R_WASM_MEMORY_ADDR_TLS_SLEB64,
-            => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
-        }
-    }
-}
-
-/// From a given `relocation` will return the new value to be written.
-/// 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(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
-    const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(relocation.index) }).finalLoc(wasm_bin);
-    const symbol = target_loc.getSymbol(wasm_bin);
-    if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
-        symbol.tag != .section and
-        symbol.isDead())
-    {
-        const val = atom.thombstone(wasm_bin) orelse relocation.addend;
-        return @bitCast(val);
-    }
-    switch (relocation.relocation_type) {
-        .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
-        .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
-        .R_WASM_TABLE_INDEX_I32,
-        .R_WASM_TABLE_INDEX_I64,
-        .R_WASM_TABLE_INDEX_SLEB,
-        .R_WASM_TABLE_INDEX_SLEB64,
-        => return wasm_bin.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
-        .R_WASM_TYPE_INDEX_LEB => {
-            const obj_file = wasm_bin.file(atom.file) orelse return relocation.index;
-            const original_type = obj_file.funcTypes()[relocation.index];
-            return wasm_bin.getTypeIndex(original_type).?;
-        },
-        .R_WASM_GLOBAL_INDEX_I32,
-        .R_WASM_GLOBAL_INDEX_LEB,
-        => return symbol.index,
-        .R_WASM_MEMORY_ADDR_I32,
-        .R_WASM_MEMORY_ADDR_I64,
-        .R_WASM_MEMORY_ADDR_LEB,
-        .R_WASM_MEMORY_ADDR_LEB64,
-        .R_WASM_MEMORY_ADDR_SLEB,
-        .R_WASM_MEMORY_ADDR_SLEB64,
-        => {
-            std.debug.assert(symbol.tag == .data);
-            if (symbol.isUndefined()) {
-                return 0;
-            }
-            const va: i33 = @intCast(symbol.virtual_address);
-            return @intCast(va + relocation.addend);
-        },
-        .R_WASM_EVENT_INDEX_LEB => return symbol.index,
-        .R_WASM_SECTION_OFFSET_I32 => {
-            const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?;
-            const target_atom = wasm_bin.getAtom(target_atom_index);
-            const rel_value: i33 = @intCast(target_atom.offset);
-            return @intCast(rel_value + relocation.addend);
-        },
-        .R_WASM_FUNCTION_OFFSET_I32 => {
-            if (symbol.isUndefined()) {
-                const val = atom.thombstone(wasm_bin) orelse relocation.addend;
-                return @bitCast(val);
-            }
-            const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?;
-            const target_atom = wasm_bin.getAtom(target_atom_index);
-            const rel_value: i33 = @intCast(target_atom.offset);
-            return @intCast(rel_value + relocation.addend);
-        },
-        .R_WASM_MEMORY_ADDR_TLS_SLEB,
-        .R_WASM_MEMORY_ADDR_TLS_SLEB64,
-        => {
-            const va: i33 = @intCast(symbol.virtual_address);
-            return @intCast(va + relocation.addend);
-        },
-    }
-}
-
-// For a given `Atom` returns whether it has a thombstone value or not.
-/// This defines whether we want a specific value when a section is dead.
-fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
-    const atom_name = atom.symbolLoc().getName(wasm);
-    if (std.mem.eql(u8, atom_name, ".debug_ranges") or std.mem.eql(u8, atom_name, ".debug_loc")) {
-        return -2;
-    } else if (std.mem.startsWith(u8, atom_name, ".debug_")) {
-        return -1;
-    }
-    return null;
-}
-
-const leb = std.leb;
-const log = std.log.scoped(.link);
-const mem = std.mem;
-const std = @import("std");
-const types = @import("types.zig");
-
-const Allocator = mem.Allocator;
-const Atom = @This();
-const FileIndex = @import("file.zig").File.Index;
-const Symbol = @import("Symbol.zig");
-const Wasm = @import("../Wasm.zig");
src/link/Wasm/file.zig
@@ -1,132 +0,0 @@
-pub const File = union(enum) {
-    zig_object: *ZigObject,
-    object: *Object,
-
-    pub const Index = enum(u16) {
-        null = std.math.maxInt(u16),
-        _,
-    };
-
-    pub fn path(file: File) []const u8 {
-        return switch (file) {
-            inline else => |obj| obj.path,
-        };
-    }
-
-    pub fn segmentInfo(file: File) []const types.Segment {
-        return switch (file) {
-            .zig_object => |obj| obj.segment_info.items,
-            .object => |obj| obj.segment_info,
-        };
-    }
-
-    pub fn symbol(file: File, index: Symbol.Index) *Symbol {
-        return switch (file) {
-            .zig_object => |obj| &obj.symbols.items[@intFromEnum(index)],
-            .object => |obj| &obj.symtable[@intFromEnum(index)],
-        };
-    }
-
-    pub fn symbols(file: File) []const Symbol {
-        return switch (file) {
-            .zig_object => |obj| obj.symbols.items,
-            .object => |obj| obj.symtable,
-        };
-    }
-
-    pub fn symbolName(file: File, index: Symbol.Index) []const u8 {
-        switch (file) {
-            .zig_object => |obj| {
-                const sym = obj.symbols.items[@intFromEnum(index)];
-                return obj.string_table.get(sym.name).?;
-            },
-            .object => |obj| {
-                const sym = obj.symtable[@intFromEnum(index)];
-                return obj.string_table.get(sym.name);
-            },
-        }
-    }
-
-    pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: Symbol.Index) !AtomIndex {
-        return switch (file) {
-            inline else => |obj| obj.parseSymbolIntoAtom(wasm_file, index),
-        };
-    }
-
-    /// For a given symbol index, find its corresponding import.
-    /// Asserts import exists.
-    pub fn import(file: File, symbol_index: Symbol.Index) types.Import {
-        return switch (file) {
-            .zig_object => |obj| obj.imports.get(symbol_index).?,
-            .object => |obj| obj.findImport(obj.symtable[@intFromEnum(symbol_index)]),
-        };
-    }
-
-    /// For a given offset, returns its string value.
-    /// Asserts string exists in the object string table.
-    pub fn string(file: File, offset: u32) []const u8 {
-        return switch (file) {
-            .zig_object => |obj| obj.string_table.get(offset).?,
-            .object => |obj| obj.string_table.get(offset),
-        };
-    }
-
-    pub fn importedGlobals(file: File) u32 {
-        return switch (file) {
-            inline else => |obj| obj.imported_globals_count,
-        };
-    }
-
-    pub fn importedFunctions(file: File) u32 {
-        return switch (file) {
-            inline else => |obj| obj.imported_functions_count,
-        };
-    }
-
-    pub fn importedTables(file: File) u32 {
-        return switch (file) {
-            inline else => |obj| obj.imported_tables_count,
-        };
-    }
-
-    pub fn function(file: File, sym_index: Symbol.Index) std.wasm.Func {
-        switch (file) {
-            .zig_object => |obj| {
-                const sym = obj.symbols.items[@intFromEnum(sym_index)];
-                return obj.functions.items[sym.index];
-            },
-            .object => |obj| {
-                const sym = obj.symtable[@intFromEnum(sym_index)];
-                return obj.functions[sym.index - obj.imported_functions_count];
-            },
-        }
-    }
-
-    pub fn globals(file: File) []const std.wasm.Global {
-        return switch (file) {
-            .zig_object => |obj| obj.globals.items,
-            .object => |obj| obj.globals,
-        };
-    }
-
-    pub fn funcTypes(file: File) []const std.wasm.Type {
-        return switch (file) {
-            .zig_object => |obj| obj.func_types.items,
-            .object => |obj| obj.func_types,
-        };
-    }
-
-    pub const Entry = union(enum) {
-        zig_object: ZigObject,
-        object: Object,
-    };
-};
-
-const std = @import("std");
-const types = @import("types.zig");
-
-const AtomIndex = @import("Atom.zig").Index;
-const Object = @import("Object.zig");
-const Symbol = @import("Symbol.zig");
-const Wasm = @import("../Wasm.zig");
-const ZigObject = @import("ZigObject.zig");
src/link/Wasm/Object.zig
@@ -3,22 +3,18 @@
 //! the data on correctness. The result can then be used by the linker.
 const Object = @This();
 
-const Atom = @import("Atom.zig");
-const types = @import("types.zig");
-const std = @import("std");
 const Wasm = @import("../Wasm.zig");
+const Atom = Wasm.Atom;
+const Alignment = Wasm.Alignment;
 const Symbol = @import("Symbol.zig");
-const Alignment = types.Alignment;
-const File = @import("file.zig").File;
 
+const std = @import("std");
 const Allocator = std.mem.Allocator;
 const leb = std.leb;
 const meta = std.meta;
 
 const log = std.log.scoped(.object);
 
-/// Index into the list of relocatable object files within the linker driver.
-index: File.Index = .null,
 /// Wasm spec version used for this `Object`
 version: u32 = 0,
 /// The file descriptor that represents the wasm object file.
@@ -28,7 +24,7 @@ path: []const u8,
 /// Parsed type section
 func_types: []const std.wasm.Type = &.{},
 /// A list of all imports for this module
-imports: []const types.Import = &.{},
+imports: []const Wasm.Import = &.{},
 /// Parsed function section
 functions: []const std.wasm.Func = &.{},
 /// Parsed table section
@@ -38,7 +34,7 @@ memories: []const std.wasm.Memory = &.{},
 /// Parsed global section
 globals: []const std.wasm.Global = &.{},
 /// Parsed export section
-exports: []const types.Export = &.{},
+exports: []const Wasm.Export = &.{},
 /// Parsed element section
 elements: []const std.wasm.Element = &.{},
 /// Represents the function ID that must be called on startup.
@@ -48,18 +44,18 @@ start: ?u32 = null,
 /// A slice of features that tell the linker what features are mandatory,
 /// used (or therefore missing) and must generate an error when another
 /// object uses features that are not supported by the other.
-features: []const types.Feature = &.{},
+features: []const Wasm.Feature = &.{},
 /// A table that maps the relocations we must perform where the key represents
 /// the section that the list of relocations applies to.
-relocations: std.AutoArrayHashMapUnmanaged(u32, []types.Relocation) = .empty,
+relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty,
 /// Table of symbols belonging to this Object file
 symtable: []Symbol = &.{},
 /// Extra metadata about the linking section, such as alignment of segments and their name
-segment_info: []const types.Segment = &.{},
+segment_info: []const Wasm.NamedSegment = &.{},
 /// A sequence of function initializers that must be called on startup
-init_funcs: []const types.InitFunc = &.{},
+init_funcs: []const Wasm.InitFunc = &.{},
 /// Comdat information
-comdat_info: []const types.Comdat = &.{},
+comdat_info: []const Wasm.Comdat = &.{},
 /// Represents non-synthetic sections that can essentially be mem-cpy'd into place
 /// after performing relocations.
 relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty,
@@ -75,7 +71,7 @@ imported_globals_count: u32 = 0,
 imported_tables_count: u32 = 0,
 
 /// Represents a single item within a section (depending on its `type`)
-const RelocatableData = struct {
+pub const RelocatableData = struct {
     /// The type of the relocatable data
     type: Tag,
     /// Pointer to the data of the segment, where its length is written to `size`
@@ -209,7 +205,7 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
 
 /// Finds the import within the list of imports from a given kind and index of that kind.
 /// Asserts the import exists
-pub fn findImport(object: *const Object, sym: Symbol) types.Import {
+pub fn findImport(object: *const Object, sym: Symbol) Wasm.Import {
     var i: u32 = 0;
     return for (object.imports) |import| {
         if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {
@@ -261,7 +257,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
         return error.MissingTableSymbols;
     }
 
-    const table_import: types.Import = for (object.imports) |imp| {
+    const table_import: Wasm.Import = for (object.imports) |imp| {
         if (imp.kind == .table) {
             break imp;
         }
@@ -592,13 +588,13 @@ fn Parser(comptime ReaderType: type) type {
             const diags = &parser.wasm_file.base.comp.link_diags;
             const reader = parser.reader.reader();
             for (try readVec(&parser.object.features, reader, gpa)) |*feature| {
-                const prefix = try readEnum(types.Feature.Prefix, reader);
+                const prefix = try readEnum(Wasm.Feature.Prefix, reader);
                 const name_len = try leb.readUleb128(u32, reader);
                 const name = try gpa.alloc(u8, name_len);
                 defer gpa.free(name);
                 try reader.readNoEof(name);
 
-                const tag = types.known_features.get(name) orelse {
+                const tag = Wasm.known_features.get(name) orelse {
                     var err = try diags.addErrorWithNotes(1);
                     try err.addMsg("Object file contains unknown feature: {s}", .{name});
                     try err.addNote("defined in '{s}'", .{parser.object.path});
@@ -618,7 +614,7 @@ fn Parser(comptime ReaderType: type) type {
             const reader = parser.reader.reader();
             const section = try leb.readUleb128(u32, reader);
             const count = try leb.readUleb128(u32, reader);
-            const relocations = try gpa.alloc(types.Relocation, count);
+            const relocations = try gpa.alloc(Wasm.Relocation, count);
             errdefer gpa.free(relocations);
 
             log.debug("Found {d} relocations for section ({d})", .{
@@ -628,7 +624,7 @@ fn Parser(comptime ReaderType: type) type {
 
             for (relocations) |*relocation| {
                 const rel_type = try reader.readByte();
-                const rel_type_enum = std.meta.intToEnum(types.Relocation.RelocationType, rel_type) catch return error.MalformedSection;
+                const rel_type_enum = std.meta.intToEnum(Wasm.Relocation.RelocationType, rel_type) catch return error.MalformedSection;
                 relocation.* = .{
                     .relocation_type = rel_type_enum,
                     .offset = try leb.readUleb128(u32, reader),
@@ -671,7 +667,7 @@ fn Parser(comptime ReaderType: type) type {
         /// such as access to the `import` section to find the name of a symbol.
         fn parseSubsection(parser: *ObjectParser, gpa: Allocator, reader: anytype) !void {
             const sub_type = try leb.readUleb128(u8, reader);
-            log.debug("Found subsection: {s}", .{@tagName(@as(types.SubsectionType, @enumFromInt(sub_type)))});
+            log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))});
             const payload_len = try leb.readUleb128(u32, reader);
             if (payload_len == 0) return;
 
@@ -681,9 +677,9 @@ fn Parser(comptime ReaderType: type) type {
             // every subsection contains a 'count' field
             const count = try leb.readUleb128(u32, limited_reader);
 
-            switch (@as(types.SubsectionType, @enumFromInt(sub_type))) {
+            switch (@as(Wasm.SubsectionType, @enumFromInt(sub_type))) {
                 .WASM_SEGMENT_INFO => {
-                    const segments = try gpa.alloc(types.Segment, count);
+                    const segments = try gpa.alloc(Wasm.NamedSegment, count);
                     errdefer gpa.free(segments);
                     for (segments) |*segment| {
                         const name_len = try leb.readUleb128(u32, reader);
@@ -704,13 +700,13 @@ fn Parser(comptime ReaderType: type) type {
                         // support legacy object files that specified being TLS by the name instead of the TLS flag.
                         if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {
                             // set the flag so we can simply check for the flag in the rest of the linker.
-                            segment.flags |= @intFromEnum(types.Segment.Flags.WASM_SEG_FLAG_TLS);
+                            segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS);
                         }
                     }
                     parser.object.segment_info = segments;
                 },
                 .WASM_INIT_FUNCS => {
-                    const funcs = try gpa.alloc(types.InitFunc, count);
+                    const funcs = try gpa.alloc(Wasm.InitFunc, count);
                     errdefer gpa.free(funcs);
                     for (funcs) |*func| {
                         func.* = .{
@@ -722,7 +718,7 @@ fn Parser(comptime ReaderType: type) type {
                     parser.object.init_funcs = funcs;
                 },
                 .WASM_COMDAT_INFO => {
-                    const comdats = try gpa.alloc(types.Comdat, count);
+                    const comdats = try gpa.alloc(Wasm.Comdat, count);
                     errdefer gpa.free(comdats);
                     for (comdats) |*comdat| {
                         const name_len = try leb.readUleb128(u32, reader);
@@ -736,11 +732,11 @@ fn Parser(comptime ReaderType: type) type {
                         }
 
                         const symbol_count = try leb.readUleb128(u32, reader);
-                        const symbols = try gpa.alloc(types.ComdatSym, symbol_count);
+                        const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count);
                         errdefer gpa.free(symbols);
                         for (symbols) |*symbol| {
                             symbol.* = .{
-                                .kind = @as(types.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))),
+                                .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))),
                                 .index = try leb.readUleb128(u32, reader),
                             };
                         }
@@ -921,93 +917,3 @@ fn assertEnd(reader: anytype) !void {
     if (len != 0) return error.MalformedSection;
     if (reader.context.bytes_left != 0) return error.MalformedSection;
 }
-
-/// Parses an object file into atoms, for code and data sections
-pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: Symbol.Index) !Atom.Index {
-    const comp = wasm.base.comp;
-    const gpa = comp.gpa;
-    const symbol = &object.symtable[@intFromEnum(symbol_index)];
-    const relocatable_data: RelocatableData = switch (symbol.tag) {
-        .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
-        .data => object.relocatable_data.get(.data).?[symbol.index],
-        .section => blk: {
-            const data = object.relocatable_data.get(.custom).?;
-            for (data) |dat| {
-                if (dat.section_index == symbol.index) {
-                    break :blk dat;
-                }
-            }
-            unreachable;
-        },
-        else => unreachable,
-    };
-    const final_index = try wasm.getMatchingSegment(object.index, symbol_index);
-    const atom_index = try wasm.createAtom(symbol_index, object.index);
-    try wasm.appendAtomAtIndex(final_index, atom_index);
-
-    const atom = wasm.getAtomPtr(atom_index);
-    atom.size = relocatable_data.size;
-    atom.alignment = relocatable_data.getAlignment(object);
-    atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
-    atom.original_offset = relocatable_data.offset;
-
-    const segment: *Wasm.Segment = &wasm.segments.items[final_index];
-    if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
-        segment.alignment = segment.alignment.max(atom.alignment);
-    }
-
-    if (object.relocations.get(relocatable_data.section_index)) |relocations| {
-        const start = searchRelocStart(relocations, relocatable_data.offset);
-        const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
-        atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]);
-        for (atom.relocs.items) |reloc| {
-            switch (reloc.relocation_type) {
-                .R_WASM_TABLE_INDEX_I32,
-                .R_WASM_TABLE_INDEX_I64,
-                .R_WASM_TABLE_INDEX_SLEB,
-                .R_WASM_TABLE_INDEX_SLEB64,
-                => {
-                    try wasm.function_table.put(gpa, .{
-                        .file = object.index,
-                        .index = @enumFromInt(reloc.index),
-                    }, 0);
-                },
-                .R_WASM_GLOBAL_INDEX_I32,
-                .R_WASM_GLOBAL_INDEX_LEB,
-                => {
-                    const sym = object.symtable[reloc.index];
-                    if (sym.tag != .global) {
-                        try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) });
-                    }
-                },
-                else => {},
-            }
-        }
-    }
-
-    return atom_index;
-}
-
-fn searchRelocStart(relocs: []const types.Relocation, address: u32) usize {
-    var min: usize = 0;
-    var max: usize = relocs.len;
-    while (min < max) {
-        const index = (min + max) / 2;
-        const curr = relocs[index];
-        if (curr.offset < address) {
-            min = index + 1;
-        } else {
-            max = index;
-        }
-    }
-    return min;
-}
-
-fn searchRelocEnd(relocs: []const types.Relocation, address: u32) usize {
-    for (relocs, 0..relocs.len) |reloc, index| {
-        if (reloc.offset > address) {
-            return index;
-        }
-    }
-    return relocs.len;
-}
src/link/Wasm/Symbol.zig
@@ -206,5 +206,4 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO
 }
 
 const std = @import("std");
-const types = @import("types.zig");
 const Symbol = @This();
src/link/Wasm/types.zig
@@ -1,267 +0,0 @@
-//! This file contains all constants and related to wasm's object format.
-
-const std = @import("std");
-
-pub const Relocation = struct {
-    /// Represents the type of the `Relocation`
-    relocation_type: RelocationType,
-    /// Offset of the value to rewrite relative to the relevant section's contents.
-    /// When `offset` is zero, its position is immediately after the id and size of the section.
-    offset: u32,
-    /// The index of the symbol used.
-    /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type.
-    index: u32,
-    /// Addend to add to the address.
-    /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`.
-    addend: i32 = 0,
-
-    /// All possible relocation types currently existing.
-    /// This enum is exhaustive as the spec is WIP and new types
-    /// can be added which means that a generated binary will be invalid,
-    /// so instead we will show an error in such cases.
-    pub const RelocationType = enum(u8) {
-        R_WASM_FUNCTION_INDEX_LEB = 0,
-        R_WASM_TABLE_INDEX_SLEB = 1,
-        R_WASM_TABLE_INDEX_I32 = 2,
-        R_WASM_MEMORY_ADDR_LEB = 3,
-        R_WASM_MEMORY_ADDR_SLEB = 4,
-        R_WASM_MEMORY_ADDR_I32 = 5,
-        R_WASM_TYPE_INDEX_LEB = 6,
-        R_WASM_GLOBAL_INDEX_LEB = 7,
-        R_WASM_FUNCTION_OFFSET_I32 = 8,
-        R_WASM_SECTION_OFFSET_I32 = 9,
-        R_WASM_EVENT_INDEX_LEB = 10,
-        R_WASM_GLOBAL_INDEX_I32 = 13,
-        R_WASM_MEMORY_ADDR_LEB64 = 14,
-        R_WASM_MEMORY_ADDR_SLEB64 = 15,
-        R_WASM_MEMORY_ADDR_I64 = 16,
-        R_WASM_TABLE_INDEX_SLEB64 = 18,
-        R_WASM_TABLE_INDEX_I64 = 19,
-        R_WASM_TABLE_NUMBER_LEB = 20,
-        R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
-        R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
-
-        /// Returns true for relocation types where the `addend` field is present.
-        pub fn addendIsPresent(self: RelocationType) bool {
-            return switch (self) {
-                .R_WASM_MEMORY_ADDR_LEB,
-                .R_WASM_MEMORY_ADDR_SLEB,
-                .R_WASM_MEMORY_ADDR_I32,
-                .R_WASM_MEMORY_ADDR_LEB64,
-                .R_WASM_MEMORY_ADDR_SLEB64,
-                .R_WASM_MEMORY_ADDR_I64,
-                .R_WASM_MEMORY_ADDR_TLS_SLEB,
-                .R_WASM_MEMORY_ADDR_TLS_SLEB64,
-                .R_WASM_FUNCTION_OFFSET_I32,
-                .R_WASM_SECTION_OFFSET_I32,
-                => true,
-                else => false,
-            };
-        }
-    };
-
-    /// Verifies the relocation type of a given `Relocation` and returns
-    /// true when the relocation references a function call or address to a function.
-    pub fn isFunction(self: Relocation) bool {
-        return switch (self.relocation_type) {
-            .R_WASM_FUNCTION_INDEX_LEB,
-            .R_WASM_TABLE_INDEX_SLEB,
-            => true,
-            else => false,
-        };
-    }
-
-    pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = fmt;
-        _ = options;
-        try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{
-            @tagName(self.relocation_type),
-            self.offset,
-            self.index,
-        });
-    }
-};
-
-/// Unlike the `Import` object defined by the wasm spec, and existing
-/// in the std.wasm namespace, this construct saves the 'module name' and 'name'
-/// of the import using offsets into a string table, rather than the slices itself.
-/// This saves us (potentially) 24 bytes per import on 64bit machines.
-pub const Import = struct {
-    module_name: u32,
-    name: u32,
-    kind: std.wasm.Import.Kind,
-};
-
-/// Unlike the `Export` object defined by the wasm spec, and existing
-/// in the std.wasm namespace, this construct saves the 'name'
-/// of the export using offsets into a string table, rather than the slice itself.
-/// This saves us (potentially) 12 bytes per export on 64bit machines.
-pub const Export = struct {
-    name: u32,
-    index: u32,
-    kind: std.wasm.ExternalKind,
-};
-
-pub const SubsectionType = enum(u8) {
-    WASM_SEGMENT_INFO = 5,
-    WASM_INIT_FUNCS = 6,
-    WASM_COMDAT_INFO = 7,
-    WASM_SYMBOL_TABLE = 8,
-};
-
-pub const Alignment = @import("../../InternPool.zig").Alignment;
-
-pub const Segment = struct {
-    /// Segment's name, encoded as UTF-8 bytes.
-    name: []const u8,
-    /// The required alignment of the segment, encoded as a power of 2
-    alignment: Alignment,
-    /// Bitfield containing flags for a segment
-    flags: u32,
-
-    pub fn isTLS(segment: Segment) bool {
-        return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0;
-    }
-
-    /// Returns the name as how it will be output into the final object
-    /// file or binary. When `merge_segments` is true, this will return the
-    /// short name. i.e. ".rodata". When false, it returns the entire name instead.
-    pub fn outputName(segment: Segment, merge_segments: bool) []const u8 {
-        if (segment.isTLS()) {
-            return ".tdata";
-        } else if (!merge_segments) {
-            return segment.name;
-        } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
-            return ".rodata";
-        } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
-            return ".text";
-        } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
-            return ".data";
-        } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
-            return ".bss";
-        }
-        return segment.name;
-    }
-
-    pub const Flags = enum(u32) {
-        WASM_SEG_FLAG_STRINGS = 0x1,
-        WASM_SEG_FLAG_TLS = 0x2,
-    };
-};
-
-pub const InitFunc = struct {
-    /// Priority of the init function
-    priority: u32,
-    /// The symbol index of init function (not the function index).
-    symbol_index: u32,
-};
-
-pub const Comdat = struct {
-    name: []const u8,
-    /// Must be zero, no flags are currently defined by the tool-convention.
-    flags: u32,
-    symbols: []const ComdatSym,
-};
-
-pub const ComdatSym = struct {
-    kind: Type,
-    /// Index of the data segment/function/global/event/table within a WASM module.
-    /// The object must not be an import.
-    index: u32,
-
-    pub const Type = enum(u8) {
-        WASM_COMDAT_DATA = 0,
-        WASM_COMDAT_FUNCTION = 1,
-        WASM_COMDAT_GLOBAL = 2,
-        WASM_COMDAT_EVENT = 3,
-        WASM_COMDAT_TABLE = 4,
-        WASM_COMDAT_SECTION = 5,
-    };
-};
-
-pub const Feature = struct {
-    /// Provides information about the usage of the feature.
-    /// - '0x2b' (+): Object uses this feature, and the link fails if feature is not in the allowed set.
-    /// - '0x2d' (-): Object does not use this feature, and the link fails if this feature is in the allowed set.
-    /// - '0x3d' (=): Object uses this feature, and the link fails if this feature is not in the allowed set,
-    /// or if any object does not use this feature.
-    prefix: Prefix,
-    /// Type of the feature, must be unique in the sequence of features.
-    tag: Tag,
-
-    /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
-    pub const Tag = enum {
-        atomics,
-        bulk_memory,
-        exception_handling,
-        extended_const,
-        half_precision,
-        multimemory,
-        multivalue,
-        mutable_globals,
-        nontrapping_fptoint,
-        reference_types,
-        relaxed_simd,
-        sign_ext,
-        simd128,
-        tail_call,
-        shared_mem,
-
-        /// From a given cpu feature, returns its linker feature
-        pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
-            return @as(Tag, @enumFromInt(@intFromEnum(feature)));
-        }
-
-        pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
-            _ = fmt;
-            _ = opt;
-            try writer.writeAll(switch (tag) {
-                .atomics => "atomics",
-                .bulk_memory => "bulk-memory",
-                .exception_handling => "exception-handling",
-                .extended_const => "extended-const",
-                .half_precision => "half-precision",
-                .multimemory => "multimemory",
-                .multivalue => "multivalue",
-                .mutable_globals => "mutable-globals",
-                .nontrapping_fptoint => "nontrapping-fptoint",
-                .reference_types => "reference-types",
-                .relaxed_simd => "relaxed-simd",
-                .sign_ext => "sign-ext",
-                .simd128 => "simd128",
-                .tail_call => "tail-call",
-                .shared_mem => "shared-mem",
-            });
-        }
-    };
-
-    pub const Prefix = enum(u8) {
-        used = '+',
-        disallowed = '-',
-        required = '=',
-    };
-
-    pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = opt;
-        _ = fmt;
-        try writer.print("{c} {}", .{ feature.prefix, feature.tag });
-    }
-};
-
-pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{
-    .{ "atomics", .atomics },
-    .{ "bulk-memory", .bulk_memory },
-    .{ "exception-handling", .exception_handling },
-    .{ "extended-const", .extended_const },
-    .{ "half-precision", .half_precision },
-    .{ "multimemory", .multimemory },
-    .{ "multivalue", .multivalue },
-    .{ "mutable-globals", .mutable_globals },
-    .{ "nontrapping-fptoint", .nontrapping_fptoint },
-    .{ "reference-types", .reference_types },
-    .{ "relaxed-simd", .relaxed_simd },
-    .{ "sign-ext", .sign_ext },
-    .{ "simd128", .simd128 },
-    .{ "tail-call", .tail_call },
-    .{ "shared-mem", .shared_mem },
-});
src/link/Wasm/ZigObject.zig
@@ -4,8 +4,6 @@
 //! Think about this as fake in-memory Object file for the Zig module.
 
 path: []const u8,
-/// Index within the list of relocatable objects of the linker driver.
-index: File.Index,
 /// Map of all `Nav` that are currently alive.
 /// Each index maps to the corresponding `NavInfo`.
 navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .empty,
@@ -16,8 +14,8 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
 functions: std.ArrayListUnmanaged(std.wasm.Func) = .empty,
 /// List of indexes pointing to an entry within the `functions` list which has been removed.
 functions_free_list: std.ArrayListUnmanaged(u32) = .empty,
-/// Map of symbol locations, represented by its `types.Import`.
-imports: std.AutoHashMapUnmanaged(Symbol.Index, types.Import) = .empty,
+/// Map of symbol locations, represented by its `Wasm.Import`.
+imports: std.AutoHashMapUnmanaged(Symbol.Index, Wasm.Import) = .empty,
 /// List of WebAssembly globals.
 globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
 /// Mapping between an `Atom` and its type index representing the Wasm
@@ -30,7 +28,7 @@ global_syms: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .empty,
 /// List of symbol indexes which are free to be used.
 symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .empty,
 /// Extra metadata about the linking section, such as alignment of segments and their name.
-segment_info: std.ArrayListUnmanaged(types.Segment) = .empty,
+segment_info: std.ArrayListUnmanaged(Wasm.NamedSegment) = .empty,
 /// List of indexes which contain a free slot in the `segment_info` list.
 segment_free_list: std.ArrayListUnmanaged(u32) = .empty,
 /// File encapsulated string table, used to deduplicate strings within the generated file.
@@ -117,39 +115,39 @@ const NavInfo = struct {
 };
 
 /// Initializes the `ZigObject` with initial symbols.
-pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void {
+pub fn init(zig_object: *ZigObject, wasm: *Wasm) !void {
     // Initialize an undefined global with the name __stack_pointer. Codegen will use
     // this to generate relocations when moving the stack pointer. This symbol will be
     // resolved automatically by the final linking stage.
-    try zig_object.createStackPointer(wasm_file);
+    try zig_object.createStackPointer(wasm);
 
     // TODO: Initialize debug information when we reimplement Dwarf support.
 }
 
-fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void {
-    const gpa = wasm_file.base.comp.gpa;
+fn createStackPointer(zig_object: *ZigObject, wasm: *Wasm) !void {
+    const gpa = wasm.base.comp.gpa;
     const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer");
     const sym = zig_object.symbol(sym_index);
     sym.index = zig_object.imported_globals_count;
     sym.tag = .global;
-    const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
+    const is_wasm32 = wasm.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
     try zig_object.imports.putNoClobber(gpa, sym_index, .{
         .name = sym.name,
-        .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name),
+        .module_name = try zig_object.string_table.insert(gpa, wasm.host_name),
         .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } },
     });
     zig_object.imported_globals_count += 1;
     zig_object.stack_pointer_sym = sym_index;
 }
 
-fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
+pub fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
     return &zig_object.symbols.items[@intFromEnum(index)];
 }
 
 /// Frees and invalidates all memory of the incrementally compiled Zig module.
 /// It is illegal behavior to access the `ZigObject` after calling `deinit`.
-pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
-    const gpa = wasm_file.base.comp.gpa;
+pub fn deinit(zig_object: *ZigObject, wasm: *Wasm) void {
+    const gpa = wasm.base.comp.gpa;
     for (zig_object.segment_info.items) |segment_info| {
         gpa.free(segment_info.name);
     }
@@ -157,9 +155,9 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
     {
         var it = zig_object.navs.valueIterator();
         while (it.next()) |nav_info| {
-            const atom = wasm_file.getAtomPtr(nav_info.atom);
+            const atom = wasm.getAtomPtr(nav_info.atom);
             for (atom.locals.items) |local_index| {
-                const local_atom = wasm_file.getAtomPtr(local_index);
+                const local_atom = wasm.getAtomPtr(local_index);
                 local_atom.deinit(gpa);
             }
             atom.deinit(gpa);
@@ -168,24 +166,24 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
     }
     {
         for (zig_object.uavs.values()) |atom_index| {
-            const atom = wasm_file.getAtomPtr(atom_index);
+            const atom = wasm.getAtomPtr(atom_index);
             for (atom.locals.items) |local_index| {
-                const local_atom = wasm_file.getAtomPtr(local_index);
+                const local_atom = wasm.getAtomPtr(local_index);
                 local_atom.deinit(gpa);
             }
             atom.deinit(gpa);
         }
     }
     if (zig_object.findGlobalSymbol("__zig_errors_len")) |sym_index| {
-        const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index }).?;
-        wasm_file.getAtomPtr(atom_index).deinit(gpa);
+        const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index }).?;
+        wasm.getAtomPtr(atom_index).deinit(gpa);
     }
-    if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol })) |atom_index| {
-        const atom = wasm_file.getAtomPtr(atom_index);
+    if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol })) |atom_index| {
+        const atom = wasm.getAtomPtr(atom_index);
         atom.deinit(gpa);
     }
     for (zig_object.synthetic_functions.items) |atom_index| {
-        const atom = wasm_file.getAtomPtr(atom_index);
+        const atom = wasm.getAtomPtr(atom_index);
         atom.deinit(gpa);
     }
     zig_object.synthetic_functions.deinit(gpa);
@@ -193,7 +191,7 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
         ty.deinit(gpa);
     }
     if (zig_object.error_names_atom != .null) {
-        const atom = wasm_file.getAtomPtr(zig_object.error_names_atom);
+        const atom = wasm.getAtomPtr(zig_object.error_names_atom);
         atom.deinit(gpa);
     }
     zig_object.global_syms.deinit(gpa);
@@ -240,7 +238,7 @@ pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.In
 // the file on flush().
 pub fn updateNav(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     nav_index: InternPool.Nav.Index,
 ) !void {
@@ -260,19 +258,19 @@ pub fn updateNav(
     };
 
     if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
-        const gpa = wasm_file.base.comp.gpa;
-        const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
-        const atom = wasm_file.getAtomPtr(atom_index);
+        const gpa = wasm.base.comp.gpa;
+        const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
+        const atom = wasm.getAtomPtr(atom_index);
         atom.clear();
 
         if (is_extern)
-            return zig_object.addOrUpdateImport(wasm_file, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null);
+            return zig_object.addOrUpdateImport(wasm, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null);
 
         var code_writer = std.ArrayList(u8).init(gpa);
         defer code_writer.deinit();
 
         const res = try codegen.generateSymbol(
-            &wasm_file.base,
+            &wasm.base,
             pt,
             zcu.navSrcLoc(nav_index),
             nav_init,
@@ -288,13 +286,13 @@ pub fn updateNav(
             },
         };
 
-        try zig_object.finishUpdateNav(wasm_file, pt, nav_index, code);
+        try zig_object.finishUpdateNav(wasm, pt, nav_index, code);
     }
 }
 
 pub fn updateFunc(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     air: Air,
@@ -303,14 +301,14 @@ pub fn updateFunc(
     const zcu = pt.zcu;
     const gpa = zcu.gpa;
     const func = pt.zcu.funcInfo(func_index);
-    const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, func.owner_nav);
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, func.owner_nav);
+    const atom = wasm.getAtomPtr(atom_index);
     atom.clear();
 
     var code_writer = std.ArrayList(u8).init(gpa);
     defer code_writer.deinit();
     const result = try codegen.generateFunction(
-        &wasm_file.base,
+        &wasm.base,
         pt,
         zcu.navSrcLoc(func.owner_nav),
         func_index,
@@ -328,12 +326,12 @@ pub fn updateFunc(
         },
     };
 
-    return zig_object.finishUpdateNav(wasm_file, pt, func.owner_nav, code);
+    return zig_object.finishUpdateNav(wasm, pt, func.owner_nav, code);
 }
 
 fn finishUpdateNav(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     nav_index: InternPool.Nav.Index,
     code: []const u8,
@@ -345,7 +343,7 @@ fn finishUpdateNav(
     const nav_val = zcu.navValue(nav_index);
     const nav_info = zig_object.navs.get(nav_index).?;
     const atom_index = nav_info.atom;
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     const sym = zig_object.symbol(atom.sym_index);
     sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip));
     try atom.code.appendSlice(gpa, code);
@@ -376,7 +374,7 @@ fn finishUpdateNav(
             }
             break :name ".bss.";
         };
-        if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and
+        if ((wasm.base.isObject() or wasm.base.comp.config.import_memory) and
             std.mem.startsWith(u8, segment_name, ".bss"))
         {
             @memset(atom.code.items, 0);
@@ -422,7 +420,7 @@ fn createDataSegment(
 /// The newly created Atom is empty with default fields as specified by `Atom.empty`.
 pub fn getOrCreateAtomForNav(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     nav_index: InternPool.Nav.Index,
 ) !Atom.Index {
@@ -431,7 +429,7 @@ pub fn getOrCreateAtomForNav(
     const gop = try zig_object.navs.getOrPut(gpa, nav_index);
     if (!gop.found_existing) {
         const sym_index = try zig_object.allocateSymbol(gpa);
-        gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) };
+        gop.value_ptr.* = .{ .atom = try wasm.createAtom(sym_index, .zig_object) };
         const nav = ip.getNav(nav_index);
         const sym = zig_object.symbol(sym_index);
         sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip));
@@ -441,13 +439,13 @@ pub fn getOrCreateAtomForNav(
 
 pub fn lowerUav(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     uav: InternPool.Index,
     explicit_alignment: InternPool.Alignment,
     src_loc: Zcu.LazySrcLoc,
 ) !codegen.GenResult {
-    const gpa = wasm_file.base.comp.gpa;
+    const gpa = wasm.base.comp.gpa;
     const gop = try zig_object.uavs.getOrPut(gpa, uav);
     if (!gop.found_existing) {
         var name_buf: [32]u8 = undefined;
@@ -455,13 +453,13 @@ pub fn lowerUav(
             @intFromEnum(uav),
         }) catch unreachable;
 
-        switch (try zig_object.lowerConst(wasm_file, pt, name, Value.fromInterned(uav), src_loc)) {
+        switch (try zig_object.lowerConst(wasm, pt, name, Value.fromInterned(uav), src_loc)) {
             .ok => |atom_index| zig_object.uavs.values()[gop.index] = atom_index,
             .fail => |em| return .{ .fail = em },
         }
     }
 
-    const atom = wasm_file.getAtomPtr(zig_object.uavs.values()[gop.index]);
+    const atom = wasm.getAtomPtr(zig_object.uavs.values()[gop.index]);
     atom.alignment = switch (atom.alignment) {
         .none => explicit_alignment,
         else => switch (explicit_alignment) {
@@ -479,25 +477,25 @@ const LowerConstResult = union(enum) {
 
 fn lowerConst(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     name: []const u8,
     val: Value,
     src_loc: Zcu.LazySrcLoc,
 ) !LowerConstResult {
-    const gpa = wasm_file.base.comp.gpa;
-    const zcu = wasm_file.base.comp.zcu.?;
+    const gpa = wasm.base.comp.gpa;
+    const zcu = wasm.base.comp.zcu.?;
 
     const ty = val.typeOf(zcu);
 
     // Create and initialize a new local symbol and atom
     const sym_index = try zig_object.allocateSymbol(gpa);
-    const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
+    const atom_index = try wasm.createAtom(sym_index, .zig_object);
     var value_bytes = std.ArrayList(u8).init(gpa);
     defer value_bytes.deinit();
 
     const code = code: {
-        const atom = wasm_file.getAtomPtr(atom_index);
+        const atom = wasm.getAtomPtr(atom_index);
         atom.alignment = ty.abiAlignment(zcu);
         const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
         errdefer gpa.free(segment_name);
@@ -514,7 +512,7 @@ fn lowerConst(
         };
 
         const result = try codegen.generateSymbol(
-            &wasm_file.base,
+            &wasm.base,
             pt,
             src_loc,
             val,
@@ -529,7 +527,7 @@ fn lowerConst(
         };
     };
 
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     atom.size = @intCast(code.len);
     try atom.code.appendSlice(gpa, code);
     return .{ .ok = atom_index };
@@ -538,7 +536,7 @@ fn lowerConst(
 /// Returns the symbol index of the error name table.
 ///
 /// When the symbol does not yet exist, it will create a new one instead.
-pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.PerThread) !Symbol.Index {
+pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm: *Wasm, pt: Zcu.PerThread) !Symbol.Index {
     if (zig_object.error_table_symbol != .null) {
         return zig_object.error_table_symbol;
     }
@@ -546,10 +544,10 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.Per
     // no error was referenced yet, so create a new symbol and atom for it
     // and then return said symbol's index. The final table will be populated
     // during `flush` when we know all possible error names.
-    const gpa = wasm_file.base.comp.gpa;
+    const gpa = wasm.base.comp.gpa;
     const sym_index = try zig_object.allocateSymbol(gpa);
-    const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom_index = try wasm.createAtom(sym_index, .zig_object);
+    const atom = wasm.getAtomPtr(atom_index);
     const slice_ty = Type.slice_const_u8_sentinel_0;
     atom.alignment = slice_ty.abiAlignment(pt.zcu);
 
@@ -573,17 +571,17 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.Per
 ///
 /// This creates a table that consists of pointers and length to each error name.
 /// The table is what is being pointed to within the runtime bodies that are generated.
-fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.PerThread.Id) !void {
+fn populateErrorNameTable(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
     if (zig_object.error_table_symbol == .null) return;
-    const gpa = wasm_file.base.comp.gpa;
-    const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?;
+    const gpa = wasm.base.comp.gpa;
+    const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol }).?;
 
     // Rather than creating a symbol for each individual error name,
     // we create a symbol for the entire region of error names. We then calculate
     // the pointers into the list using addends which are appended to the relocation.
     const names_sym_index = try zig_object.allocateSymbol(gpa);
-    const names_atom_index = try wasm_file.createAtom(names_sym_index, zig_object.index);
-    const names_atom = wasm_file.getAtomPtr(names_atom_index);
+    const names_atom_index = try wasm.createAtom(names_sym_index, .zig_object);
+    const names_atom = wasm.getAtomPtr(names_atom_index);
     names_atom.alignment = .@"1";
     const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names");
     const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
@@ -600,9 +598,9 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per
 
     // Addend for each relocation to the table
     var addend: u32 = 0;
-    const pt: Zcu.PerThread = .{ .zcu = wasm_file.base.comp.zcu.?, .tid = tid };
+    const pt: Zcu.PerThread = .{ .zcu = wasm.base.comp.zcu.?, .tid = tid };
     const slice_ty = Type.slice_const_u8_sentinel_0;
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     {
         // TODO: remove this unreachable entry
         try atom.code.appendNTimes(gpa, 0, 4);
@@ -646,7 +644,7 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per
 /// In all other cases, a data-symbol will be created instead.
 pub fn addOrUpdateImport(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     /// Name of the import
     name: []const u8,
     /// Symbol index that is external
@@ -658,7 +656,7 @@ pub fn addOrUpdateImport(
     /// is asserted instead.
     type_index: ?u32,
 ) !void {
-    const gpa = wasm_file.base.comp.gpa;
+    const gpa = wasm.base.comp.gpa;
     std.debug.assert(symbol_index != .null);
     // For the import name, we use the decl's name, rather than the fully qualified name
     // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
@@ -682,7 +680,7 @@ pub fn addOrUpdateImport(
 
     if (type_index) |ty_index| {
         const gop = try zig_object.imports.getOrPut(gpa, symbol_index);
-        const module_name = if (lib_name) |l_name| l_name else wasm_file.host_name;
+        const module_name = if (lib_name) |l_name| l_name else wasm.host_name;
         if (!gop.found_existing) {
             zig_object.imported_functions_count += 1;
         }
@@ -733,7 +731,7 @@ pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []c
 /// Returns the given pointer address
 pub fn getNavVAddr(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     nav_index: InternPool.Nav.Index,
     reloc_info: link.File.RelocInfo,
@@ -744,12 +742,12 @@ pub fn getNavVAddr(
     const nav = ip.getNav(nav_index);
     const target = &zcu.navFileScope(nav_index).mod.resolved_target.result;
 
-    const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
-    const target_atom = wasm_file.getAtom(target_atom_index);
+    const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
+    const target_atom = wasm.getAtom(target_atom_index);
     const target_symbol_index = @intFromEnum(target_atom.sym_index);
     switch (ip.indexToKey(nav.status.resolved.val)) {
         .@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
-            wasm_file,
+            wasm,
             nav.name.toSlice(ip),
             target_atom.sym_index,
             @"extern".lib_name.toSlice(ip),
@@ -759,11 +757,11 @@ pub fn getNavVAddr(
     }
 
     std.debug.assert(reloc_info.parent.atom_index != 0);
-    const atom_index = wasm_file.symbol_atom.get(.{
-        .file = zig_object.index,
+    const atom_index = wasm.symbol_atom.get(.{
+        .file = .zig_object,
         .index = @enumFromInt(reloc_info.parent.atom_index),
     }).?;
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     const is_wasm32 = target.cpu.arch == .wasm32;
     if (ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) {
         std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
@@ -790,22 +788,22 @@ pub fn getNavVAddr(
 
 pub fn getUavVAddr(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     uav: InternPool.Index,
     reloc_info: link.File.RelocInfo,
 ) !u64 {
-    const gpa = wasm_file.base.comp.gpa;
-    const target = wasm_file.base.comp.root_mod.resolved_target.result;
+    const gpa = wasm.base.comp.gpa;
+    const target = wasm.base.comp.root_mod.resolved_target.result;
     const atom_index = zig_object.uavs.get(uav).?;
-    const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
+    const target_symbol_index = @intFromEnum(wasm.getAtom(atom_index).sym_index);
 
-    const parent_atom_index = wasm_file.symbol_atom.get(.{
-        .file = zig_object.index,
+    const parent_atom_index = wasm.symbol_atom.get(.{
+        .file = .zig_object,
         .index = @enumFromInt(reloc_info.parent.atom_index),
     }).?;
-    const parent_atom = wasm_file.getAtomPtr(parent_atom_index);
+    const parent_atom = wasm.getAtomPtr(parent_atom_index);
     const is_wasm32 = target.cpu.arch == .wasm32;
-    const zcu = wasm_file.base.comp.zcu.?;
+    const zcu = wasm.base.comp.zcu.?;
     const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav));
     if (ty.zigTypeTag(zcu) == .@"fn") {
         std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
@@ -832,11 +830,11 @@ pub fn getUavVAddr(
 
 pub fn deleteExport(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     exported: Zcu.Exported,
     name: InternPool.NullTerminatedString,
 ) void {
-    const zcu = wasm_file.base.comp.zcu.?;
+    const zcu = wasm.base.comp.zcu.?;
     const nav_index = switch (exported) {
         .nav => |nav_index| nav_index,
         .uav => @panic("TODO: implement Wasm linker code for exporting a constant value"),
@@ -846,15 +844,15 @@ pub fn deleteExport(
         const sym = zig_object.symbol(sym_index);
         nav_info.deleteExport(sym_index);
         std.debug.assert(zig_object.global_syms.remove(sym.name));
-        std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index }));
-        zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {};
+        std.debug.assert(wasm.symbol_atom.remove(.{ .file = .zig_object, .index = sym_index }));
+        zig_object.symbols_free_list.append(wasm.base.comp.gpa, sym_index) catch {};
         sym.tag = .dead;
     }
 }
 
 pub fn updateExports(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     pt: Zcu.PerThread,
     exported: Zcu.Exported,
     export_indices: []const u32,
@@ -869,10 +867,10 @@ pub fn updateExports(
         },
     };
     const nav = ip.getNav(nav_index);
-    const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
+    const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
     const nav_info = zig_object.navs.getPtr(nav_index).?;
-    const atom = wasm_file.getAtom(atom_index);
-    const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*;
+    const atom = wasm.getAtom(atom_index);
+    const atom_sym = wasm.symbolLocSymbol(atom.symbolLoc()).*;
     const gpa = zcu.gpa;
     log.debug("Updating exports for decl '{}'", .{nav.name.fmt(ip)});
 
@@ -926,17 +924,17 @@ pub fn updateExports(
         }
         log.debug("  with name '{s}' - {}", .{ export_string, sym });
         try zig_object.global_syms.put(gpa, export_name, sym_index);
-        try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index);
+        try wasm.symbol_atom.put(gpa, .{ .file = .zig_object, .index = sym_index }, atom_index);
     }
 }
 
-pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.Nav.Index) void {
-    const gpa = wasm_file.base.comp.gpa;
-    const zcu = wasm_file.base.comp.zcu.?;
+pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.Index) void {
+    const gpa = wasm.base.comp.gpa;
+    const zcu = wasm.base.comp.zcu.?;
     const ip = &zcu.intern_pool;
     const nav_info = zig_object.navs.getPtr(nav_index).?;
     const atom_index = nav_info.atom;
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
     for (nav_info.exports.items) |exp_sym_index| {
         const exp_sym = zig_object.symbol(exp_sym_index);
@@ -947,11 +945,11 @@ pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.N
     std.debug.assert(zig_object.navs.remove(nav_index));
     const sym = &zig_object.symbols.items[atom.sym_index];
     for (atom.locals.items) |local_atom_index| {
-        const local_atom = wasm_file.getAtom(local_atom_index);
+        const local_atom = wasm.getAtom(local_atom_index);
         const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
         std.debug.assert(local_symbol.tag == .data);
         zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
-        std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
+        std.debug.assert(wasm.symbol_atom.remove(local_atom.symbolLoc()));
         local_symbol.tag = .dead; // also for any local symbol
         const segment = &zig_object.segment_info.items[local_atom.sym_index];
         gpa.free(segment.name);
@@ -962,7 +960,7 @@ pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.N
     if (ip.indexToKey(nav_val) == .@"extern") {
         std.debug.assert(zig_object.imports.remove(atom.sym_index));
     }
-    std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc()));
+    std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
 
     // if (wasm.dwarf) |*dwarf| {
     //     dwarf.freeDecl(decl_index);
@@ -1014,15 +1012,15 @@ pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_typ
 
 /// Generates an atom containing the global error set' size.
 /// This will only be generated if the symbol exists.
-fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
-    const gpa = wasm_file.base.comp.gpa;
+fn setupErrorsLen(zig_object: *ZigObject, wasm: *Wasm) !void {
+    const gpa = wasm.base.comp.gpa;
     const sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return;
 
-    const errors_len = 1 + wasm_file.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
+    const errors_len = 1 + wasm.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
     // overwrite existing atom if it already exists (maybe the error set has increased)
     // if not, allocate a new atom.
-    const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: {
-        const atom = wasm_file.getAtomPtr(index);
+    const atom_index = if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index })) |index| blk: {
+        const atom = wasm.getAtomPtr(index);
         atom.prev = .null;
         atom.deinit(gpa);
         break :blk index;
@@ -1036,10 +1034,10 @@ fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
         sym.tag = .data;
         const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len");
         sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2");
-        break :idx try wasm_file.createAtom(sym_index, zig_object.index);
+        break :idx try wasm.createAtom(sym_index, .zig_object);
     };
 
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom = wasm.getAtomPtr(atom_index);
     atom.code.clearRetainingCapacity();
     atom.sym_index = sym_index;
     atom.size = 2;
@@ -1073,15 +1071,15 @@ pub fn initDebugSections(zig_object: *ZigObject) !void {
 /// From a given index variable, creates a new debug section.
 /// This initializes the index, appends a new segment,
 /// and finally, creates a managed `Atom`.
-pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
-    const gpa = wasm_file.base.comp.gpa;
+pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
+    const gpa = wasm.base.comp.gpa;
     const new_index: u32 = @intCast(zig_object.segments.items.len);
     index.* = new_index;
     try zig_object.appendDummySegment();
 
     const sym_index = try zig_object.allocateSymbol(gpa);
-    const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom_index = try wasm.createAtom(sym_index, .zig_object);
+    const atom = wasm.getAtomPtr(atom_index);
     zig_object.symbols.items[sym_index] = .{
         .tag = .section,
         .name = try zig_object.string_table.put(gpa, name),
@@ -1148,13 +1146,13 @@ pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, nav_index:
 /// The symbols in ZigObject are already represented by an atom as we need to store its data.
 /// So rather than creating a new Atom and returning its index, we use this opportunity to scan
 /// its relocations and create any GOT symbols or function table indexes it may require.
-pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symbol.Index) !Atom.Index {
-    const gpa = wasm_file.base.comp.gpa;
-    const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index };
-    const atom_index = wasm_file.symbol_atom.get(loc).?;
-    const final_index = try wasm_file.getMatchingSegment(zig_object.index, index);
-    try wasm_file.appendAtomAtIndex(final_index, atom_index);
-    const atom = wasm_file.getAtom(atom_index);
+pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm: *Wasm, index: Symbol.Index) !Atom.Index {
+    const gpa = wasm.base.comp.gpa;
+    const loc: Wasm.SymbolLoc = .{ .file = .zig_object, .index = index };
+    const atom_index = wasm.symbol_atom.get(loc).?;
+    const final_index = try wasm.getMatchingSegment(.zig_object, index);
+    try wasm.appendAtomAtIndex(final_index, atom_index);
+    const atom = wasm.getAtom(atom_index);
     for (atom.relocs.items) |reloc| {
         const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
         switch (reloc.relocation_type) {
@@ -1163,8 +1161,8 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb
             .R_WASM_TABLE_INDEX_SLEB,
             .R_WASM_TABLE_INDEX_SLEB64,
             => {
-                try wasm_file.function_table.put(gpa, .{
-                    .file = zig_object.index,
+                try wasm.function_table.put(gpa, .{
+                    .file = .zig_object,
                     .index = reloc_index,
                 }, 0);
             },
@@ -1173,8 +1171,8 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb
             => {
                 const sym = zig_object.symbol(reloc_index);
                 if (sym.tag != .global) {
-                    try wasm_file.got_symbols.append(gpa, .{
-                        .file = zig_object.index,
+                    try wasm.got_symbols.append(gpa, .{
+                        .file = .zig_object,
                         .index = reloc_index,
                     });
                 }
@@ -1189,13 +1187,13 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb
 /// Returns the symbol index of the new function.
 pub fn createFunction(
     zig_object: *ZigObject,
-    wasm_file: *Wasm,
+    wasm: *Wasm,
     symbol_name: []const u8,
     func_ty: std.wasm.Type,
     function_body: *std.ArrayList(u8),
-    relocations: *std.ArrayList(types.Relocation),
+    relocations: *std.ArrayList(Wasm.Relocation),
 ) !Symbol.Index {
-    const gpa = wasm_file.base.comp.gpa;
+    const gpa = wasm.base.comp.gpa;
     const sym_index = try zig_object.allocateSymbol(gpa);
     const sym = zig_object.symbol(sym_index);
     sym.tag = .function;
@@ -1203,8 +1201,8 @@ pub fn createFunction(
     const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
     sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
 
-    const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
-    const atom = wasm_file.getAtomPtr(atom_index);
+    const atom_index = try wasm.createAtom(sym_index, .zig_object);
+    const atom = wasm.getAtomPtr(atom_index);
     atom.size = @intCast(function_body.items.len);
     atom.code = function_body.moveToUnmanaged();
     atom.relocs = relocations.moveToUnmanaged();
@@ -1227,9 +1225,9 @@ fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm
     return index;
 }
 
-pub fn flushModule(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.PerThread.Id) !void {
-    try zig_object.populateErrorNameTable(wasm_file, tid);
-    try zig_object.setupErrorsLen(wasm_file);
+pub fn flushModule(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
+    try zig_object.populateErrorNameTable(wasm, tid);
+    try zig_object.setupErrorsLen(wasm);
 }
 
 const build_options = @import("build_options");
@@ -1238,12 +1236,10 @@ const codegen = @import("../../codegen.zig");
 const link = @import("../../link.zig");
 const log = std.log.scoped(.zig_object);
 const std = @import("std");
-const types = @import("types.zig");
 
 const Air = @import("../../Air.zig");
-const Atom = @import("Atom.zig");
+const Atom = Wasm.Atom;
 const Dwarf = @import("../Dwarf.zig");
-const File = @import("file.zig").File;
 const InternPool = @import("../../InternPool.zig");
 const Liveness = @import("../../Liveness.zig");
 const Zcu = @import("../../Zcu.zig");
src/link/Wasm.zig
@@ -15,7 +15,6 @@ const log = std.log.scoped(.link);
 const gc_log = std.log.scoped(.gc);
 const mem = std.mem;
 const trace = @import("../tracy.zig").trace;
-const types = @import("Wasm/types.zig");
 const wasi_libc = @import("../wasi_libc.zig");
 
 const Air = @import("../Air.zig");
@@ -26,7 +25,6 @@ const Path = Cache.Path;
 const CodeGen = @import("../arch/wasm/CodeGen.zig");
 const Compilation = @import("../Compilation.zig");
 const Dwarf = @import("Dwarf.zig");
-const File = @import("Wasm/file.zig").File;
 const InternPool = @import("../InternPool.zig");
 const Liveness = @import("../Liveness.zig");
 const LlvmObject = @import("../codegen/llvm.zig").Object;
@@ -37,9 +35,6 @@ const Type = @import("../Type.zig");
 const Value = @import("../Value.zig");
 const ZigObject = @import("Wasm/ZigObject.zig");
 
-pub const Atom = @import("Wasm/Atom.zig");
-pub const Relocation = types.Relocation;
-
 base: link.File,
 /// Symbol name of the entry function to export
 entry_name: ?[]const u8,
@@ -61,11 +56,9 @@ export_table: bool,
 name: []const u8,
 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
 llvm_object: ?LlvmObject.Ptr = null,
-/// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists,
-/// and the chosen backend is the Wasm backend.
-zig_object_index: File.Index = .null,
+zig_object: ?*ZigObject,
 /// List of relocatable files to be linked into the final binary.
-files: std.MultiArrayList(File.Entry) = .{},
+objects: std.ArrayListUnmanaged(Object) = .{},
 /// When importing objects from the host environment, a name must be supplied.
 /// LLVM uses "env" by default when none is given. This would be a good default for Zig
 /// to support existing code.
@@ -105,17 +98,17 @@ 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 `types.Import`
-imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .empty,
+/// Map of symbol locations, represented by its `Import`
+imports: std.AutoHashMapUnmanaged(SymbolLoc, Import) = .empty,
 /// Represents non-synthetic section entries.
 /// Used for code, data and custom sections.
 segments: std.ArrayListUnmanaged(Segment) = .empty,
 /// Maps a data segment key (such as .rodata) to the index into `segments`.
 data_segments: std.StringArrayHashMapUnmanaged(u32) = .empty,
-/// A table of `types.Segment` which provide meta data
+/// A table of `NamedSegment` which provide meta data
 /// about a data symbol such as its name where the key is
 /// the segment index, which can be found from `data_segments`
-segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .empty,
+segment_info: std.AutoArrayHashMapUnmanaged(u32, NamedSegment) = .empty,
 /// Deduplicated string table for strings used by symbols, imports and exports.
 string_table: StringTable = .{},
 
@@ -126,8 +119,15 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
 /// function index and the value is function.
 /// This allows us to map multiple symbols to the same function.
 functions: std.AutoArrayHashMapUnmanaged(
-    struct { file: File.Index, index: u32 },
-    struct { func: std.wasm.Func, sym_index: Symbol.Index },
+    struct {
+        /// `none` in the case of synthetic sections.
+        file: OptionalObjectId,
+        index: u32,
+    },
+    struct {
+        func: std.wasm.Func,
+        sym_index: Symbol.Index,
+    },
 ) = .{},
 /// Output global section
 wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
@@ -140,7 +140,7 @@ memories: std.wasm.Memory = .{ .limits = .{
 /// Output table section
 tables: std.ArrayListUnmanaged(std.wasm.Table) = .empty,
 /// Output export section
-exports: std.ArrayListUnmanaged(types.Export) = .empty,
+exports: std.ArrayListUnmanaged(Export) = .empty,
 /// List of initialization functions. These must be called in order of priority
 /// by the (synthetic) __wasm_call_ctors function.
 init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .empty,
@@ -154,8 +154,6 @@ entry: ?u32 = null,
 /// Note: Key is symbol location, value represents the index into the table
 function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .empty,
 
-/// All object files and their data which are linked into the final binary
-objects: std.ArrayListUnmanaged(File.Index) = .empty,
 /// All archive files that are lazy loaded.
 /// e.g. when an undefined symbol references a symbol from the archive.
 archives: std.ArrayListUnmanaged(Archive) = .empty,
@@ -178,7 +176,29 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .empty,
 /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
 symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .empty,
 
-pub const Alignment = types.Alignment;
+/// Index into objects array or the zig object.
+pub const ObjectId = enum(u16) {
+    zig_object = std.math.maxInt(u16) - 1,
+    _,
+
+    pub fn toOptional(i: ObjectId) OptionalObjectId {
+        const result: OptionalObjectId = @enumFromInt(@intFromEnum(i));
+        assert(result != .none);
+        return result;
+    }
+};
+
+/// Optional index into objects array or the zig object.
+pub const OptionalObjectId = enum(u16) {
+    zig_object = std.math.maxInt(u16) - 1,
+    none = std.math.maxInt(u16),
+    _,
+
+    pub fn unwrap(i: OptionalObjectId) ?ObjectId {
+        if (i == .none) return null;
+        return @enumFromInt(@intFromEnum(i));
+    }
+};
 
 pub const Segment = struct {
     alignment: Alignment,
@@ -208,43 +228,55 @@ pub const SymbolLoc = struct {
     /// The index of the symbol within the specified file
     index: Symbol.Index,
     /// The index of the object file where the symbol resides.
-    file: File.Index,
+    file: OptionalObjectId,
+};
 
-    /// From a given location, returns the corresponding symbol in the wasm binary
-    pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol {
-        if (wasm_file.discarded.get(loc)) |new_loc| {
-            return new_loc.getSymbol(wasm_file);
-        }
-        if (wasm_file.file(loc.file)) |obj_file| {
-            return obj_file.symbol(loc.index);
-        }
-        return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
+/// From a given location, returns the corresponding symbol in the wasm binary
+pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol {
+    if (wasm.discarded.get(loc)) |new_loc| {
+        return symbolLocSymbol(wasm, new_loc);
     }
+    return switch (loc.file) {
+        .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)],
+        .zig_object => wasm.zig_object.?.symbol(loc.index),
+        _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)],
+    };
+}
 
-    /// From a given location, returns the name of the symbol.
-    pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 {
-        if (wasm_file.discarded.get(loc)) |new_loc| {
-            return new_loc.getName(wasm_file);
-        }
-        if (wasm_file.file(loc.file)) |obj_file| {
-            return obj_file.symbolName(loc.index);
-        }
-        const sym = wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
-        return wasm_file.string_table.get(sym.name);
+/// From a given location, returns the name of the symbol.
+pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) []const u8 {
+    if (wasm.discarded.get(loc)) |new_loc| {
+        return wasm.symbolLocName(new_loc);
+    }
+    switch (loc.file) {
+        .none => {
+            const sym = wasm.synthetic_symbols.items[@intFromEnum(loc.index)];
+            return wasm.string_table.get(sym.name);
+        },
+        .zig_object => {
+            const zo = wasm.zig_object.?;
+            const sym = zo.symbols.items[@intFromEnum(loc.index)];
+            return zo.string_table.get(sym.name).?;
+        },
+        _ => {
+            const obj = &wasm.objects.items[@intFromEnum(loc.file)];
+            const sym = obj.symtable[@intFromEnum(loc.index)];
+            return obj.string_table.get(sym.name);
+        },
     }
+}
 
-    /// From a given symbol location, returns the final location.
-    /// e.g. when a symbol was resolved and replaced by the symbol
-    /// in a different file, this will return said location.
-    /// If the symbol wasn't replaced by another, this will return
-    /// the given location itwasm.
-    pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc {
-        if (wasm_file.discarded.get(loc)) |new_loc| {
-            return new_loc.finalLoc(wasm_file);
-        }
-        return loc;
+/// From a given symbol location, returns the final location.
+/// e.g. when a symbol was resolved and replaced by the symbol
+/// in a different file, this will return said location.
+/// If the symbol wasn't replaced by another, this will return
+/// the given location itwasm.
+pub fn symbolLocFinalLoc(wasm: *const Wasm, loc: SymbolLoc) SymbolLoc {
+    if (wasm.discarded.get(loc)) |new_loc| {
+        return symbolLocFinalLoc(wasm, new_loc);
     }
-};
+    return loc;
+}
 
 // Contains the location of the function symbol, as well as
 /// the priority itself of the initialization function.
@@ -252,7 +284,7 @@ pub const InitFuncLoc = struct {
     /// object file index in the list of objects.
     /// Unlike `SymbolLoc` this cannot be `null` as we never define
     /// our own ctors.
-    file: File.Index,
+    file: ObjectId,
     /// Symbol index within the corresponding object file.
     index: Symbol.Index,
     /// The priority in which the constructor must be called.
@@ -260,12 +292,15 @@ pub const InitFuncLoc = struct {
 
     /// From a given `InitFuncLoc` returns the corresponding function symbol
     fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol {
-        return getSymbolLoc(loc).getSymbol(wasm);
+        return wasm.symbolLocSymbol(getSymbolLoc(loc));
     }
 
     /// Turns the given `InitFuncLoc` into a `SymbolLoc`
     fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {
-        return .{ .file = loc.file, .index = loc.index };
+        return .{
+            .file = loc.file.toOptional(),
+            .index = loc.index,
+        };
     }
 
     /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.
@@ -307,7 +342,7 @@ pub const StringTable = struct {
         }
 
         try table.string_data.ensureUnusedCapacity(allocator, string.len + 1);
-        const offset = @as(u32, @intCast(table.string_data.items.len));
+        const offset: u32 = @intCast(table.string_data.items.len);
 
         log.debug("writing new string '{s}' at offset 0x{x}", .{ string, offset });
 
@@ -414,6 +449,7 @@ pub fn createEmpty(
             .enabled => defaultEntrySymbolName(wasi_exec_model),
             .named => |name| name,
         },
+        .zig_object = null,
     };
     if (use_llvm and comp.config.have_zcu) {
         wasm.llvm_object = try LlvmObject.create(arena, comp);
@@ -446,7 +482,7 @@ pub fn createEmpty(
     // create stack pointer symbol
     {
         const loc = try wasm.createSyntheticSymbol("__stack_pointer", .global);
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         // For object files we will import the stack pointer symbol
         if (output_mode == .Obj) {
             symbol.setUndefined(true);
@@ -478,7 +514,7 @@ pub fn createEmpty(
     // create indirect function pointer symbol
     {
         const loc = try wasm.createSyntheticSymbol("__indirect_function_table", .table);
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         const table: std.wasm.Table = .{
             .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable`
             .reftype = .funcref,
@@ -506,7 +542,7 @@ pub fn createEmpty(
     // create __wasm_call_ctors
     {
         const loc = try wasm.createSyntheticSymbol("__wasm_call_ctors", .function);
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
         // we do not know the function index until after we merged all sections.
         // Therefore we set `symbol.index` and create its corresponding references
@@ -517,7 +553,7 @@ pub fn createEmpty(
     if (shared_memory) {
         {
             const loc = try wasm.createSyntheticSymbol("__tls_base", .global);
-            const symbol = loc.getSymbol(wasm);
+            const symbol = wasm.symbolLocSymbol(loc);
             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
             symbol.mark();
@@ -528,7 +564,7 @@ pub fn createEmpty(
         }
         {
             const loc = try wasm.createSyntheticSymbol("__tls_size", .global);
-            const symbol = loc.getSymbol(wasm);
+            const symbol = wasm.symbolLocSymbol(loc);
             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
             symbol.mark();
@@ -539,7 +575,7 @@ pub fn createEmpty(
         }
         {
             const loc = try wasm.createSyntheticSymbol("__tls_align", .global);
-            const symbol = loc.getSymbol(wasm);
+            const symbol = wasm.symbolLocSymbol(loc);
             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
             symbol.mark();
@@ -550,42 +586,26 @@ pub fn createEmpty(
         }
         {
             const loc = try wasm.createSyntheticSymbol("__wasm_init_tls", .function);
-            const symbol = loc.getSymbol(wasm);
+            const symbol = wasm.symbolLocSymbol(loc);
             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
         }
     }
 
     if (comp.zcu) |zcu| {
         if (!use_llvm) {
-            const index: File.Index = @enumFromInt(wasm.files.len);
-            var zig_object: ZigObject = .{
-                .index = index,
+            const zig_object = try arena.create(ZigObject);
+            wasm.zig_object = zig_object;
+            zig_object.* = .{
                 .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}),
                 .stack_pointer_sym = .null,
             };
             try zig_object.init(wasm);
-            try wasm.files.append(gpa, .{ .zig_object = zig_object });
-            wasm.zig_object_index = index;
         }
     }
 
     return wasm;
 }
 
-pub fn file(wasm: *const Wasm, index: File.Index) ?File {
-    if (index == .null) return null;
-    const tag = wasm.files.items(.tags)[@intFromEnum(index)];
-    return switch (tag) {
-        .zig_object => .{ .zig_object = &wasm.files.items(.data)[@intFromEnum(index)].zig_object },
-        .object => .{ .object = &wasm.files.items(.data)[@intFromEnum(index)].object },
-    };
-}
-
-pub fn zigObjectPtr(wasm: *Wasm) ?*ZigObject {
-    if (wasm.zig_object_index == .null) return null;
-    return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object;
-}
-
 pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
     var index: u32 = 0;
     while (index < wasm.func_types.items.len) : (index += 1) {
@@ -610,7 +630,7 @@ pub fn addOrUpdateImport(
     /// is asserted instead.
     type_index: ?u32,
 ) !void {
-    return wasm.zigObjectPtr().?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index);
+    return wasm.zig_object.?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index);
 }
 
 /// For a given name, creates a new global synthetic symbol.
@@ -623,7 +643,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
 
 fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {
     const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
-    const loc: SymbolLoc = .{ .index = sym_index, .file = .null };
+    const loc: SymbolLoc = .{ .index = sym_index, .file = .none };
     const gpa = wasm.base.comp.gpa;
     try wasm.synthetic_symbols.append(gpa, .{
         .name = name_offset,
@@ -657,28 +677,29 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
         },
     };
     errdefer object.deinit(gpa);
-    object.index = @enumFromInt(wasm.files.len);
-    try wasm.files.append(gpa, .{ .object = object });
-    try wasm.objects.append(gpa, object.index);
+    try wasm.objects.append(gpa, object);
     return true;
 }
 
 /// Creates a new empty `Atom` and returns its `Atom.Index`
-pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, file_index: File.Index) !Atom.Index {
+pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, object_index: OptionalObjectId) !Atom.Index {
     const gpa = wasm.base.comp.gpa;
     const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len);
     const atom = try wasm.managed_atoms.addOne(gpa);
-    atom.* = .{ .file = file_index, .sym_index = sym_index };
+    atom.* = .{
+        .file = object_index,
+        .sym_index = sym_index,
+    };
     try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
 
     return index;
 }
 
-pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
+pub fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
     return wasm.managed_atoms.items[@intFromEnum(index)];
 }
 
-pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
+pub fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
     return &wasm.managed_atoms.items[@intFromEnum(index)];
 }
 
@@ -733,15 +754,13 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
     }
 
     for (offsets.keys()) |file_offset| {
-        var object = archive.parseObject(wasm, file_offset) catch |e| {
+        const object = archive.parseObject(wasm, file_offset) catch |e| {
             var err_note = try diags.addErrorWithNotes(1);
             try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
             try err_note.addNote("while parsing object in archive {s}", .{path});
             return error.FlushFailure;
         };
-        object.index = @enumFromInt(wasm.files.len);
-        try wasm.files.append(gpa, .{ .object = object });
-        try wasm.objects.append(gpa, object.index);
+        try wasm.objects.append(gpa, object);
     }
 
     return true;
@@ -749,23 +768,102 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
 
 fn requiresTLSReloc(wasm: *const Wasm) bool {
     for (wasm.got_symbols.items) |loc| {
-        if (loc.getSymbol(wasm).isTLS()) {
+        if (wasm.symbolLocSymbol(loc).isTLS()) {
             return true;
         }
     }
     return false;
 }
 
-fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
+fn objectPath(wasm: *const Wasm, object_id: ObjectId) []const u8 {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.path;
+    return obj.path;
+}
+
+fn objectSymbols(wasm: *const Wasm, object_id: ObjectId) []const Symbol {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items;
+    return obj.symtable;
+}
+
+fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol {
+    const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)];
+    return &obj.symtable[@intFromEnum(index)];
+}
+
+fn objectSymbolName(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) []const u8 {
+    const obj = wasm.objectById(object_id) orelse {
+        const zo = wasm.zig_object.?;
+        const sym = zo.symbols.items[@intFromEnum(index)];
+        return zo.string_table.get(sym.name).?;
+    };
+    const sym = obj.symtable[@intFromEnum(index)];
+    return obj.string_table.get(sym.name);
+}
+
+fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func {
+    const obj = wasm.objectById(object_id) orelse {
+        const zo = wasm.zig_object.?;
+        const sym = zo.symbols.items[@intFromEnum(sym_index)];
+        return zo.functions.items[sym.index];
+    };
+    const sym = obj.symtable[@intFromEnum(sym_index)];
+    return obj.functions[sym.index - obj.imported_functions_count];
+}
+
+fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count;
+    return obj.imported_functions_count;
+}
+
+fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items;
+    return obj.globals;
+}
+
+fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items;
+    return obj.func_types;
+}
+
+fn objectSegmentInfo(wasm: *const Wasm, object_id: ObjectId) []const NamedSegment {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items;
+    return obj.segment_info;
+}
+
+/// For a given symbol index, find its corresponding import.
+/// Asserts import exists.
+fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?;
+    return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]);
+}
+
+/// For a given offset, returns its string value.
+/// Asserts string exists in the object string table.
+fn objectString(wasm: *const Wasm, object_id: ObjectId, offset: u32) []const u8 {
+    const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.string_table.get(offset).?;
+    return obj.string_table.get(offset);
+}
+
+/// Returns the object element pointer, or null if it is the ZigObject.
+fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object {
+    if (object_id == .zig_object) return null;
+    return &wasm.objects.items[@intFromEnum(object_id)];
+}
+
+fn resolveSymbolsInObject(wasm: *Wasm, object_id: ObjectId) !void {
     const gpa = wasm.base.comp.gpa;
     const diags = &wasm.base.comp.link_diags;
-    const obj_file = wasm.file(file_index).?;
-    log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()});
+    const obj_path = objectPath(wasm, object_id);
+    log.debug("Resolving symbols in object: '{s}'", .{obj_path});
+    const symbols = objectSymbols(wasm, object_id);
 
-    for (obj_file.symbols(), 0..) |symbol, i| {
+    for (symbols, 0..) |symbol, i| {
         const sym_index: Symbol.Index = @enumFromInt(i);
-        const location: SymbolLoc = .{ .file = file_index, .index = sym_index };
-        const sym_name = obj_file.string(symbol.name);
+        const location: SymbolLoc = .{
+            .file = object_id.toOptional(),
+            .index = sym_index,
+        };
+        const sym_name = objectString(wasm, object_id, symbol.name);
         if (mem.eql(u8, sym_name, "__indirect_function_table")) {
             continue;
         }
@@ -775,7 +873,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
             if (symbol.isUndefined()) {
                 var err = try diags.addErrorWithNotes(1);
                 try err.addMsg("Local symbols are not allowed to reference imports", .{});
-                try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
+                try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_path });
             }
             try wasm.resolved_symbols.putNoClobber(gpa, location, {});
             continue;
@@ -793,13 +891,8 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
         }
 
         const existing_loc = maybe_existing.value_ptr.*;
-        const existing_sym: *Symbol = existing_loc.getSymbol(wasm);
-        const existing_file = wasm.file(existing_loc.file);
-
-        const existing_file_path = if (existing_file) |existing_obj_file|
-            existing_obj_file.path()
-        else
-            wasm.name;
+        const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc);
+        const existing_file_path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else wasm.name;
 
         if (!existing_sym.isUndefined()) outer: {
             if (!symbol.isUndefined()) inner: {
@@ -813,7 +906,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
                 var err = try diags.addErrorWithNotes(2);
                 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
                 try err.addNote("first definition in '{s}'", .{existing_file_path});
-                try err.addNote("next definition in '{s}'", .{obj_file.path()});
+                try err.addNote("next definition in '{s}'", .{obj_path});
             }
 
             try wasm.discarded.put(gpa, location, existing_loc);
@@ -824,22 +917,22 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
             var err = try diags.addErrorWithNotes(2);
             try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
             try err.addNote("first definition in '{s}'", .{existing_file_path});
-            try err.addNote("next definition in '{s}'", .{obj_file.path()});
+            try err.addNote("next definition in '{s}'", .{obj_path});
         }
 
         if (existing_sym.isUndefined() and symbol.isUndefined()) {
             // only verify module/import name for function symbols
             if (symbol.tag == .function) {
-                const existing_name = if (existing_file) |existing_obj| blk: {
-                    const imp = existing_obj.import(existing_loc.index);
-                    break :blk existing_obj.string(imp.module_name);
+                const existing_name = if (existing_loc.file.unwrap()) |existing_obj_id| blk: {
+                    const imp = objectImport(wasm, existing_obj_id, existing_loc.index);
+                    break :blk objectString(wasm, existing_obj_id, imp.module_name);
                 } else blk: {
                     const name_index = wasm.imports.get(existing_loc).?.module_name;
                     break :blk wasm.string_table.get(name_index);
                 };
 
-                const imp = obj_file.import(sym_index);
-                const module_name = obj_file.string(imp.module_name);
+                const imp = objectImport(wasm, object_id, sym_index);
+                const module_name = objectString(wasm, object_id, imp.module_name);
                 if (!mem.eql(u8, existing_name, module_name)) {
                     var err = try diags.addErrorWithNotes(2);
                     try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
@@ -848,7 +941,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
                         module_name,
                     });
                     try err.addNote("first definition in '{s}'", .{existing_file_path});
-                    try err.addNote("next definition in '{s}'", .{obj_file.path()});
+                    try err.addNote("next definition in '{s}'", .{obj_path});
                 }
             }
 
@@ -864,7 +957,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
                 var err = try diags.addErrorWithNotes(2);
                 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
                 try err.addNote("first definition in '{s}'", .{existing_file_path});
-                try err.addNote("next definition in '{s}'", .{obj_file.path()});
+                try err.addNote("next definition in '{s}'", .{obj_path});
             }
         }
 
@@ -876,7 +969,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
                 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
                 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
                 try err.addNote("first definition in '{s}'", .{existing_file_path});
-                try err.addNote("next definition in '{s}'", .{obj_file.path()});
+                try err.addNote("next definition in '{s}'", .{obj_path});
             }
         }
 
@@ -891,7 +984,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
         // simply overwrite with the new symbol
         log.debug("Overwriting symbol '{s}'", .{sym_name});
         log.debug("  old definition in '{s}'", .{existing_file_path});
-        log.debug("  new definition in '{s}'", .{obj_file.path()});
+        log.debug("  new definition in '{s}'", .{obj_path});
         try wasm.discarded.putNoClobber(gpa, existing_loc, location);
         maybe_existing.value_ptr.* = location;
         try wasm.globals.put(gpa, sym_name_index, location);
@@ -924,16 +1017,14 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
             // Symbol is found in unparsed object file within current archive.
             // Parse object and and resolve symbols again before we check remaining
             // undefined symbols.
-            var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
+            const object = archive.parseObject(wasm, offset.items[0]) catch |e| {
                 var err_note = try diags.addErrorWithNotes(1);
                 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
                 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
                 return error.FlushFailure;
             };
-            object.index = @enumFromInt(wasm.files.len);
-            try wasm.files.append(gpa, .{ .object = object });
-            try wasm.objects.append(gpa, object.index);
-            try wasm.resolveSymbolsInObject(object.index);
+            try wasm.objects.append(gpa, object);
+            try wasm.resolveSymbolsInObject(@enumFromInt(wasm.objects.items.len - 1));
 
             // continue loop for any remaining undefined symbols that still exist
             // after resolving last object file
@@ -964,13 +1055,13 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
         return;
     }
     const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function);
-    sym_loc.getSymbol(wasm).mark();
+    wasm.symbolLocSymbol(sym_loc).mark();
 
     const flag_address: u32 = if (shared_memory) address: {
         // when we have passive initialization segments and shared memory
         // `setupMemory` will create this symbol and set its virtual address.
         const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?;
-        break :address loc.getSymbol(wasm).virtual_address;
+        break :address wasm.symbolLocSymbol(loc).virtual_address;
     } else 0;
 
     var function_body = std.ArrayList(u8).init(gpa);
@@ -1028,7 +1119,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
                 try writeI32Const(writer, segment.offset);
                 try writer.writeByte(std.wasm.opcode(.global_set));
                 const loc = wasm.findGlobalSymbol("__tls_base").?;
-                try leb.writeUleb128(writer, loc.getSymbol(wasm).index);
+                try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
             }
 
             try writeI32Const(writer, 0);
@@ -1128,7 +1219,7 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
     }
 
     const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs", .function);
-    loc.getSymbol(wasm).mark();
+    wasm.symbolLocSymbol(loc).mark();
     var function_body = std.ArrayList(u8).init(gpa);
     defer function_body.deinit();
     const writer = function_body.writer();
@@ -1136,12 +1227,12 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
     // locals (we have none)
     try writer.writeByte(0);
     for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
-        const sym: *Symbol = got_loc.getSymbol(wasm);
+        const sym: *Symbol = wasm.symbolLocSymbol(got_loc);
         if (!sym.isTLS()) continue; // only relocate TLS symbols
         if (sym.tag == .data and sym.isDefined()) {
             // get __tls_base
             try writer.writeByte(std.wasm.opcode(.global_get));
-            try leb.writeUleb128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index);
+            try leb.writeUleb128(writer, wasm.symbolLocSymbol(wasm.findGlobalSymbol("__tls_base").?).index);
 
             // add the virtual address of the symbol
             try writer.writeByte(std.wasm.opcode(.i32_const));
@@ -1165,7 +1256,7 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
 
 fn validateFeatures(
     wasm: *const Wasm,
-    to_emit: *[@typeInfo(types.Feature.Tag).@"enum".fields.len]bool,
+    to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool,
     emit_features_count: *u32,
 ) !void {
     const comp = wasm.base.comp;
@@ -1174,7 +1265,7 @@ fn validateFeatures(
     const shared_memory = comp.config.shared_memory;
     const cpu_features = target.cpu.features;
     const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
-    const known_features_count = @typeInfo(types.Feature.Tag).@"enum".fields.len;
+    const known_features_count = @typeInfo(Feature.Tag).@"enum".fields.len;
 
     var allowed = [_]bool{false} ** known_features_count;
     var used = [_]u17{0} ** known_features_count;
@@ -1199,10 +1290,9 @@ fn validateFeatures(
 
     // extract all the used, disallowed and required features from each
     // linked object file so we can test them.
-    for (wasm.objects.items) |file_index| {
-        const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
+    for (wasm.objects.items, 0..) |*object, file_index| {
         for (object.features) |feature| {
-            const value = @as(u16, @intFromEnum(file_index)) << 1 | @as(u1, 1);
+            const value = (@as(u16, @intCast(file_index)) << 1) | 1;
             switch (feature.prefix) {
                 .used => {
                     used[@intFromEnum(feature.tag)] = value;
@@ -1234,8 +1324,8 @@ fn validateFeatures(
             emit_features_count.* += @intFromBool(is_enabled);
         } else if (is_enabled and !allowed[used_index]) {
             var err = try diags.addErrorWithNotes(1);
-            try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
-            try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
+            try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(Feature.Tag, @enumFromInt(used_index))});
+            try err.addNote("defined in '{s}'", .{wasm.objects.items[used_set >> 1].path});
             valid_feature_set = false;
         }
     }
@@ -1245,17 +1335,17 @@ fn validateFeatures(
     }
 
     if (shared_memory) {
-        const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
+        const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)];
         if (@as(u1, @truncate(disallowed_feature)) != 0) {
             var err = try diags.addErrorWithNotes(0);
             try err.addMsg(
                 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
-                .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
+                .{wasm.objects.items[disallowed_feature >> 1].path},
             );
             valid_feature_set = false;
         }
 
-        for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
+        for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {
             if (!allowed[@intFromEnum(feature)]) {
                 var err = try diags.addErrorWithNotes(0);
                 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
@@ -1264,7 +1354,7 @@ fn validateFeatures(
     }
 
     if (has_tls) {
-        for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
+        for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {
             if (!allowed[@intFromEnum(feature)]) {
                 var err = try diags.addErrorWithNotes(0);
                 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
@@ -1272,9 +1362,8 @@ fn validateFeatures(
         }
     }
     // For each linked object, validate the required and disallowed features
-    for (wasm.objects.items) |file_index| {
+    for (wasm.objects.items) |*object| {
         var object_used_features = [_]bool{false} ** known_features_count;
-        const object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
         for (object.features) |feature| {
             if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
             // from here a feature is always used
@@ -1282,7 +1371,7 @@ fn validateFeatures(
             if (@as(u1, @truncate(disallowed_feature)) != 0) {
                 var err = try diags.addErrorWithNotes(2);
                 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
-                try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
+                try err.addNote("disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].path});
                 try err.addNote("used in '{s}'", .{object.path});
                 valid_feature_set = false;
             }
@@ -1295,8 +1384,8 @@ fn validateFeatures(
             const is_required = @as(u1, @truncate(required_feature)) != 0;
             if (is_required and !object_used_features[feature_index]) {
                 var err = try diags.addErrorWithNotes(2);
-                try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
-                try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
+                try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))});
+                try err.addNote("required by '{s}'", .{wasm.objects.items[required_feature >> 1].path});
                 try err.addNote("missing in '{s}'", .{object.path});
                 valid_feature_set = false;
             }
@@ -1341,7 +1430,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
                 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);
                 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
                 _ = wasm.resolved_symbols.swapRemove(kv.value);
-                const symbol = loc.getSymbol(wasm);
+                const symbol = wasm.symbolLocSymbol(loc);
                 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
                 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
                 try wasm.wasm_globals.append(gpa, .{
@@ -1368,14 +1457,15 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
 
     var found_undefined_symbols = false;
     for (wasm.undefs.values()) |undef| {
-        const symbol = undef.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(undef);
         if (symbol.tag == .data) {
             found_undefined_symbols = true;
-            const file_name = if (wasm.file(undef.file)) |obj_file|
-                obj_file.path()
-            else
-                wasm.name;
-            const symbol_name = undef.getName(wasm);
+            const file_name = switch (undef.file) {
+                .zig_object => wasm.zig_object.?.path,
+                .none => wasm.name,
+                _ => wasm.objects.items[@intFromEnum(undef.file)].path,
+            };
+            const symbol_name = wasm.symbolLocName(undef);
             var err = try diags.addErrorWithNotes(1);
             try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
             try err.addNote("defined in '{s}'", .{file_name});
@@ -1396,11 +1486,11 @@ pub fn deinit(wasm: *Wasm) void {
     for (wasm.segment_info.values()) |segment_info| {
         gpa.free(segment_info.name);
     }
-    if (wasm.zigObjectPtr()) |zig_obj| {
+    if (wasm.zig_object) |zig_obj| {
         zig_obj.deinit(wasm);
     }
-    for (wasm.objects.items) |obj_index| {
-        wasm.file(obj_index).?.object.deinit(gpa);
+    for (wasm.objects.items) |*object| {
+        object.deinit(gpa);
     }
 
     for (wasm.archives.items) |*archive| {
@@ -1437,7 +1527,6 @@ pub fn deinit(wasm: *Wasm) void {
     wasm.exports.deinit(gpa);
 
     wasm.string_table.deinit(gpa);
-    wasm.files.deinit(gpa);
 }
 
 pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
@@ -1445,7 +1534,7 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
-    try wasm.zigObjectPtr().?.updateFunc(wasm, pt, func_index, air, liveness);
+    try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness);
 }
 
 // Generate code for the "Nav", storing it in memory to be later written to
@@ -1455,51 +1544,84 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !voi
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
-    try wasm.zigObjectPtr().?.updateNav(wasm, pt, nav);
+    try wasm.zig_object.?.updateNav(wasm, pt, nav);
 }
 
 pub fn updateNavLineNumber(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
     if (wasm.llvm_object) |_| return;
-    try wasm.zigObjectPtr().?.updateNavLineNumber(pt, nav);
+    try wasm.zig_object.?.updateNavLineNumber(pt, nav);
 }
 
 /// From a given symbol location, returns its `wasm.GlobalType`.
 /// Asserts the Symbol represents a global.
 fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
-    const symbol = loc.getSymbol(wasm);
+    const symbol = wasm.symbolLocSymbol(loc);
     assert(symbol.tag == .global);
     const is_undefined = symbol.isUndefined();
-    if (wasm.file(loc.file)) |obj_file| {
-        if (is_undefined) {
-            return obj_file.import(loc.index).kind.global;
-        }
-        return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type;
-    }
-    if (is_undefined) {
-        return wasm.imports.get(loc).?.kind.global;
+    switch (loc.file) {
+        .zig_object => {
+            const zo = wasm.zig_object.?;
+            return if (is_undefined)
+                zo.imports.get(loc.index).?.kind.global
+            else
+                zo.globals.items[symbol.index - zo.imported_globals_count].global_type;
+        },
+        .none => {
+            return if (is_undefined)
+                wasm.imports.get(loc).?.kind.global
+            else
+                wasm.wasm_globals.items[symbol.index].global_type;
+        },
+        _ => {
+            const obj = &wasm.objects.items[@intFromEnum(loc.file)];
+            return if (is_undefined)
+                obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.global
+            else
+                obj.globals[symbol.index - obj.imported_globals_count].global_type;
+        },
     }
-    return wasm.wasm_globals.items[symbol.index].global_type;
 }
 
 /// From a given symbol location, returns its `wasm.Type`.
 /// Asserts the Symbol represents a function.
 fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
-    const symbol = loc.getSymbol(wasm);
+    const symbol = wasm.symbolLocSymbol(loc);
     assert(symbol.tag == .function);
     const is_undefined = symbol.isUndefined();
-    if (wasm.file(loc.file)) |obj_file| {
-        if (is_undefined) {
-            const ty_index = obj_file.import(loc.index).kind.function;
-            return obj_file.funcTypes()[ty_index];
-        }
-        const type_index = obj_file.function(loc.index).type_index;
-        return obj_file.funcTypes()[type_index];
-    }
-    if (is_undefined) {
-        const ty_index = wasm.imports.get(loc).?.kind.function;
-        return wasm.func_types.items[ty_index];
+    switch (loc.file) {
+        .zig_object => {
+            const zo = wasm.zig_object.?;
+            if (is_undefined) {
+                const type_index = zo.imports.get(loc.index).?.kind.function;
+                return zo.func_types.items[type_index];
+            }
+            const sym = zo.symbols.items[@intFromEnum(loc.index)];
+            const type_index = zo.functions.items[sym.index].type_index;
+            return zo.func_types.items[type_index];
+        },
+        .none => {
+            if (is_undefined) {
+                const type_index = wasm.imports.get(loc).?.kind.function;
+                return wasm.func_types.items[type_index];
+            }
+            return wasm.func_types.items[
+                wasm.functions.get(.{
+                    .file = .none,
+                    .index = symbol.index,
+                }).?.func.type_index
+            ];
+        },
+        _ => {
+            const obj = &wasm.objects.items[@intFromEnum(loc.file)];
+            if (is_undefined) {
+                const type_index = obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.function;
+                return obj.func_types[type_index];
+            }
+            const sym = obj.symtable[@intFromEnum(loc.index)];
+            const type_index = obj.functions[sym.index - obj.imported_functions_count].type_index;
+            return obj.func_types[type_index];
+        },
     }
-    return wasm.func_types.items[wasm.functions.get(.{ .file = loc.file, .index = symbol.index }).?.func.type_index];
 }
 
 /// Returns the symbol index from a symbol of which its flag is set global,
@@ -1508,7 +1630,7 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
 /// and then returns the index to it.
 pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
     _ = lib_name;
-    return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name);
+    return wasm.zig_object.?.getGlobalSymbol(wasm.base.comp.gpa, name);
 }
 
 /// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type.
@@ -1519,7 +1641,7 @@ pub fn getNavVAddr(
     nav: InternPool.Nav.Index,
     reloc_info: link.File.RelocInfo,
 ) !u64 {
-    return wasm.zigObjectPtr().?.getNavVAddr(wasm, pt, nav, reloc_info);
+    return wasm.zig_object.?.getNavVAddr(wasm, pt, nav, reloc_info);
 }
 
 pub fn lowerUav(
@@ -1529,11 +1651,11 @@ pub fn lowerUav(
     explicit_alignment: Alignment,
     src_loc: Zcu.LazySrcLoc,
 ) !codegen.GenResult {
-    return wasm.zigObjectPtr().?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc);
+    return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc);
 }
 
 pub fn getUavVAddr(wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
-    return wasm.zigObjectPtr().?.getUavVAddr(wasm, uav, reloc_info);
+    return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info);
 }
 
 pub fn deleteExport(
@@ -1542,7 +1664,7 @@ pub fn deleteExport(
     name: InternPool.NullTerminatedString,
 ) void {
     if (wasm.llvm_object) |_| return;
-    return wasm.zigObjectPtr().?.deleteExport(wasm, exported, name);
+    return wasm.zig_object.?.deleteExport(wasm, exported, name);
 }
 
 pub fn updateExports(
@@ -1555,12 +1677,12 @@ pub fn updateExports(
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
-    return wasm.zigObjectPtr().?.updateExports(wasm, pt, exported, export_indices);
+    return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices);
 }
 
 pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
     if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
-    return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index);
+    return wasm.zig_object.?.freeDecl(wasm, decl_index);
 }
 
 /// Assigns indexes to all indirect functions.
@@ -1570,7 +1692,7 @@ fn mapFunctionTable(wasm: *Wasm) void {
     var it = wasm.function_table.iterator();
     var index: u32 = 1;
     while (it.next()) |entry| {
-        const symbol = entry.key_ptr.*.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);
         if (symbol.isAlive()) {
             entry.value_ptr.* = index;
             index += 1;
@@ -1586,7 +1708,7 @@ fn mapFunctionTable(wasm: *Wasm) void {
     } else if (index > 1) {
         log.debug("Appending indirect function table", .{});
         const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
-        const symbol = sym_loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(sym_loc);
         const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
         table.limits = .{ .min = index, .max = index, .flags = 0x1 };
     }
@@ -1625,10 +1747,11 @@ fn allocateAtoms(wasm: *Wasm) !void {
             // Ensure we get the original symbol, so we verify the correct symbol on whether
             // it is dead or not and ensure an atom is removed when dead.
             // This is required as we may have parsed aliases into atoms.
-            const sym = if (wasm.file(symbol_loc.file)) |obj_file|
-                obj_file.symbol(symbol_loc.index).*
-            else
-                wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)];
+            const sym = switch (symbol_loc.file) {
+                .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)],
+                .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)],
+                _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)],
+            };
 
             // Dead symbols must be unlinked from the linked-list to prevent them
             // from being emit into the binary.
@@ -1647,7 +1770,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
             offset = @intCast(atom.alignment.forward(offset));
             atom.offset = offset;
             log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
-                symbol_loc.getName(wasm),
+                wasm.symbolLocName(symbol_loc),
                 offset,
                 offset + atom.size,
                 atom.size,
@@ -1663,7 +1786,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
 /// For each data symbol, sets the virtual address.
 fn allocateVirtualAddresses(wasm: *Wasm) void {
     for (wasm.resolved_symbols.keys()) |loc| {
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         if (symbol.tag != .data or symbol.isDead()) {
             // Only data symbols have virtual addresses.
             // Dead symbols do not get allocated, so we don't need to set their virtual address either.
@@ -1676,10 +1799,11 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {
 
         const atom = wasm.getAtom(atom_index);
         const merge_segment = wasm.base.comp.config.output_mode != .Obj;
-        const segment_info = if (atom.file != .null)
-            wasm.file(atom.file).?.segmentInfo()
-        else
-            wasm.segment_info.values();
+        const segment_info = switch (atom.file) {
+            .zig_object => wasm.zig_object.?.segment_info.items,
+            .none => wasm.segment_info.values(),
+            _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info,
+        };
         const segment_name = segment_info[symbol.index].outputName(merge_segment);
         const segment_index = wasm.data_segments.get(segment_name).?;
         const segment = wasm.segments.items[segment_index];
@@ -1737,13 +1861,12 @@ fn setupInitFunctions(wasm: *Wasm) !void {
     const gpa = wasm.base.comp.gpa;
     const diags = &wasm.base.comp.link_diags;
     // There's no constructors for Zig so we can simply search through linked object files only.
-    for (wasm.objects.items) |file_index| {
-        const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
+    for (wasm.objects.items, 0..) |*object, object_index| {
         try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
         for (object.init_funcs) |init_func| {
             const symbol = object.symtable[init_func.symbol_index];
             const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {
-                const imp: types.Import = object.findImport(symbol);
+                const imp: Import = object.findImport(symbol);
                 break :ty object.func_types[imp.kind.function];
             } else ty: {
                 const func_index = symbol.index - object.imported_functions_count;
@@ -1757,10 +1880,13 @@ fn setupInitFunctions(wasm: *Wasm) !void {
             log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
             wasm.init_funcs.appendAssumeCapacity(.{
                 .index = @enumFromInt(init_func.symbol_index),
-                .file = file_index,
+                .file = @enumFromInt(object_index),
                 .priority = init_func.priority,
             });
-            try wasm.mark(.{ .index = @enumFromInt(init_func.symbol_index), .file = file_index });
+            try wasm.mark(.{
+                .index = @enumFromInt(init_func.symbol_index),
+                .file = @enumFromInt(object_index),
+            });
         }
     }
 
@@ -1834,7 +1960,7 @@ fn createSyntheticFunction(
 ) !void {
     const gpa = wasm.base.comp.gpa;
     const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol?
-    const symbol = loc.getSymbol(wasm);
+    const symbol = wasm.symbolLocSymbol(loc);
     if (symbol.isDead()) {
         return;
     }
@@ -1843,13 +1969,13 @@ fn createSyntheticFunction(
     const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
     try wasm.functions.putNoClobber(
         gpa,
-        .{ .file = .null, .index = func_index },
+        .{ .file = .none, .index = func_index },
         .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
     );
     symbol.index = func_index;
 
     // create the atom that will be output into the final binary
-    const atom_index = try wasm.createAtom(loc.index, .null);
+    const atom_index = try wasm.createAtom(loc.index, .none);
     const atom = wasm.getAtomPtr(atom_index);
     atom.size = @intCast(function_body.items.len);
     atom.code = function_body.moveToUnmanaged();
@@ -1866,13 +1992,13 @@ pub fn createFunction(
     function_body: *std.ArrayList(u8),
     relocations: *std.ArrayList(Relocation),
 ) !Symbol.Index {
-    return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
+    return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
 }
 
 /// If required, sets the function index in the `start` section.
 fn setupStartSection(wasm: *Wasm) !void {
     if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| {
-        wasm.entry = loc.getSymbol(wasm).index;
+        wasm.entry = wasm.symbolLocSymbol(loc).index;
     }
 }
 
@@ -1884,7 +2010,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
     if (!shared_memory) return;
 
     // ensure function is marked as we must emit it
-    wasm.findGlobalSymbol("__wasm_init_tls").?.getSymbol(wasm).mark();
+    wasm.symbolLocSymbol(wasm.findGlobalSymbol("__wasm_init_tls").?).mark();
 
     var function_body = std.ArrayList(u8).init(gpa);
     defer function_body.deinit();
@@ -1905,7 +2031,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
 
         const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;
         try writer.writeByte(std.wasm.opcode(.global_set));
-        try leb.writeUleb128(writer, tls_base_loc.getSymbol(wasm).index);
+        try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index);
 
         // load stack values for the bulk-memory operation
         {
@@ -1933,8 +2059,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
     // generated by the linker.
     if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
         try writer.writeByte(std.wasm.opcode(.call));
-        try leb.writeUleb128(writer, loc.getSymbol(wasm).index);
-        loc.getSymbol(wasm).mark();
+        try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
+        wasm.symbolLocSymbol(loc).mark();
     }
 
     try writer.writeByte(std.wasm.opcode(.end));
@@ -1950,27 +2076,27 @@ fn setupImports(wasm: *Wasm) !void {
     const gpa = wasm.base.comp.gpa;
     log.debug("Merging imports", .{});
     for (wasm.resolved_symbols.keys()) |symbol_loc| {
-        const obj_file = wasm.file(symbol_loc.file) orelse {
+        const object_id = symbol_loc.file.unwrap() orelse {
             // Synthetic symbols will already exist in the `import` section
             continue;
         };
 
-        const symbol = symbol_loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(symbol_loc);
         if (symbol.isDead() or
             !symbol.requiresImport() or
-            std.mem.eql(u8, symbol_loc.getName(wasm), "__indirect_function_table"))
+            std.mem.eql(u8, wasm.symbolLocName(symbol_loc), "__indirect_function_table"))
         {
             continue;
         }
 
-        log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
-        const import = obj_file.import(symbol_loc.index);
+        log.debug("Symbol '{s}' will be imported from the host", .{wasm.symbolLocName(symbol_loc)});
+        const import = objectImport(wasm, object_id, symbol_loc.index);
 
         // We copy the import to a new import to ensure the names contain references
         // to the internal string table, rather than of the object file.
-        const new_imp: types.Import = .{
-            .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)),
-            .name = try wasm.string_table.put(gpa, obj_file.string(import.name)),
+        const new_imp: Import = .{
+            .module_name = try wasm.string_table.put(gpa, objectString(wasm, object_id, import.module_name)),
+            .name = try wasm.string_table.put(gpa, objectString(wasm, object_id, import.name)),
             .kind = import.kind,
         };
         // TODO: De-duplicate imports when they contain the same names and type
@@ -1983,8 +2109,8 @@ fn setupImports(wasm: *Wasm) !void {
     var table_index: u32 = 0;
     var it = wasm.imports.iterator();
     while (it.next()) |entry| {
-        const symbol = entry.key_ptr.*.getSymbol(wasm);
-        const import: types.Import = entry.value_ptr.*;
+        const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);
+        const import: Import = entry.value_ptr.*;
         switch (import.kind) {
             .function => {
                 symbol.index = function_index;
@@ -2021,12 +2147,12 @@ fn mergeSections(wasm: *Wasm) !void {
     defer removed_duplicates.deinit();
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const obj_file = wasm.file(sym_loc.file) orelse {
+        const object_id = sym_loc.file.unwrap() orelse {
             // Synthetic symbols already live in the corresponding sections.
             continue;
         };
 
-        const symbol = obj_file.symbol(sym_loc.index);
+        const symbol = objectSymbol(wasm, object_id, sym_loc.index);
         if (symbol.isDead() or symbol.isUndefined()) {
             // Skip undefined symbols as they go in the `import` section
             continue;
@@ -2044,7 +2170,7 @@ fn mergeSections(wasm: *Wasm) !void {
                     // we only emit a single function, instead of duplicates.
                     // we favor keeping the global over a local.
                     const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
-                    const original_sym = original_loc.getSymbol(wasm);
+                    const original_sym = wasm.symbolLocSymbol(original_loc);
                     if (original_sym.isLocal() and symbol.isGlobal()) {
                         original_sym.unmark();
                         try wasm.discarded.put(gpa, original_loc, sym_loc);
@@ -2056,20 +2182,23 @@ fn mergeSections(wasm: *Wasm) !void {
                         continue;
                     }
                 }
-                gop.value_ptr.* = .{ .func = obj_file.function(sym_loc.index), .sym_index = sym_loc.index };
+                gop.value_ptr.* = .{
+                    .func = objectFunction(wasm, object_id, sym_loc.index),
+                    .sym_index = sym_loc.index,
+                };
                 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
             },
             .global => {
-                const index = symbol.index - obj_file.importedFunctions();
-                const original_global = obj_file.globals()[index];
+                const index = symbol.index - objectImportedFunctions(wasm, object_id);
+                const original_global = objectGlobals(wasm, object_id)[index];
                 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
                 try wasm.wasm_globals.append(gpa, original_global);
             },
             .table => {
-                const index = symbol.index - obj_file.importedFunctions();
+                const index = symbol.index - objectImportedFunctions(wasm, object_id);
                 // assert it's a regular relocatable object file as `ZigObject` will never
                 // contain a table.
-                const original_table = obj_file.object.tables[index];
+                const original_table = wasm.objectById(object_id).?.tables[index];
                 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
                 try wasm.tables.append(gpa, original_table);
             },
@@ -2081,7 +2210,7 @@ fn mergeSections(wasm: *Wasm) !void {
     // For any removed duplicates, remove them from the resolved symbols list
     for (removed_duplicates.items) |sym_loc| {
         assert(wasm.resolved_symbols.swapRemove(sym_loc));
-        gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)});
+        gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)});
     }
 
     log.debug("Merged ({d}) functions", .{wasm.functions.count()});
@@ -2102,26 +2231,26 @@ fn mergeTypes(wasm: *Wasm) !void {
     defer dirty.deinit();
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const obj_file = wasm.file(sym_loc.file) orelse {
+        const object_id = sym_loc.file.unwrap() orelse {
             // zig code-generated symbols are already present in final type section
             continue;
         };
 
-        const symbol = obj_file.symbol(sym_loc.index);
+        const symbol = objectSymbol(wasm, object_id, sym_loc.index);
         if (symbol.tag != .function or symbol.isDead()) {
             // Only functions have types. Only retrieve the type of referenced functions.
             continue;
         }
 
         if (symbol.isUndefined()) {
-            log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
-            const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;
-            const original_type = obj_file.funcTypes()[import.kind.function];
+            log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)});
+            const import: *Import = wasm.imports.getPtr(sym_loc) orelse continue;
+            const original_type = objectFuncTypes(wasm, object_id)[import.kind.function];
             import.kind.function = try wasm.putOrGetFuncType(original_type);
         } else if (!dirty.contains(symbol.index)) {
-            log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});
+            log.debug("Adding type from function '{s}'", .{wasm.symbolLocName(sym_loc)});
             const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
-            func.type_index = try wasm.putOrGetFuncType(obj_file.funcTypes()[func.type_index]);
+            func.type_index = try wasm.putOrGetFuncType(objectFuncTypes(wasm, object_id)[func.type_index]);
             dirty.putAssumeCapacityNoClobber(symbol.index, {});
         }
     }
@@ -2142,7 +2271,7 @@ fn checkExportNames(wasm: *Wasm) !void {
                 continue;
             };
 
-            const symbol = loc.getSymbol(wasm);
+            const symbol = wasm.symbolLocSymbol(loc);
             symbol.setFlag(.WASM_SYM_EXPORTED);
         }
 
@@ -2159,15 +2288,15 @@ fn setupExports(wasm: *Wasm) !void {
     log.debug("Building exports from symbols", .{});
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const symbol = sym_loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(sym_loc);
         if (!symbol.isExported(comp.config.rdynamic)) continue;
 
-        const sym_name = sym_loc.getName(wasm);
-        const export_name = if (sym_loc.file == .null)
+        const sym_name = wasm.symbolLocName(sym_loc);
+        const export_name = if (sym_loc.file == .none)
             symbol.name
         else
             try wasm.string_table.put(gpa, sym_name);
-        const exp: types.Export = if (symbol.tag == .data) exp: {
+        const exp: Export = if (symbol.tag == .data) exp: {
             const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
             try wasm.wasm_globals.append(gpa, .{
                 .global_type = .{ .valtype = .i32, .mutable = false },
@@ -2206,7 +2335,7 @@ fn setupStart(wasm: *Wasm) !void {
         return error.FlushFailure;
     };
 
-    const symbol = symbol_loc.getSymbol(wasm);
+    const symbol = wasm.symbolLocSymbol(symbol_loc);
     if (symbol.tag != .function) {
         var err = try diags.addErrorWithNotes(0);
         try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
@@ -2240,7 +2369,7 @@ fn setupMemory(wasm: *Wasm) !void {
     const is_obj = comp.config.output_mode == .Obj;
 
     const stack_ptr = if (wasm.findGlobalSymbol("__stack_pointer")) |loc| index: {
-        const sym = loc.getSymbol(wasm);
+        const sym = wasm.symbolLocSymbol(loc);
         break :index sym.index - wasm.imported_globals_count;
     } else null;
 
@@ -2262,15 +2391,15 @@ fn setupMemory(wasm: *Wasm) !void {
         // set TLS-related symbols
         if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
             if (wasm.findGlobalSymbol("__tls_size")) |loc| {
-                const sym = loc.getSymbol(wasm);
+                const sym = wasm.symbolLocSymbol(loc);
                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
             }
             if (wasm.findGlobalSymbol("__tls_align")) |loc| {
-                const sym = loc.getSymbol(wasm);
+                const sym = wasm.symbolLocSymbol(loc);
                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?);
             }
             if (wasm.findGlobalSymbol("__tls_base")) |loc| {
-                const sym = loc.getSymbol(wasm);
+                const sym = wasm.symbolLocSymbol(loc);
                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory)
                     @as(i32, 0)
                 else
@@ -2288,7 +2417,7 @@ fn setupMemory(wasm: *Wasm) !void {
         // align to pointer size
         memory_ptr = mem.alignForward(u64, memory_ptr, 4);
         const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
-        const sym = loc.getSymbol(wasm);
+        const sym = wasm.symbolLocSymbol(loc);
         sym.mark();
         sym.virtual_address = @as(u32, @intCast(memory_ptr));
         memory_ptr += 4;
@@ -2305,7 +2434,7 @@ fn setupMemory(wasm: *Wasm) !void {
     // One of the linked object files has a reference to the __heap_base symbol.
     // We must set its virtual address so it can be used in relocations.
     if (wasm.findGlobalSymbol("__heap_base")) |loc| {
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr));
     }
 
@@ -2335,7 +2464,7 @@ fn setupMemory(wasm: *Wasm) !void {
     log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
 
     if (wasm.findGlobalSymbol("__heap_end")) |loc| {
-        const symbol = loc.getSymbol(wasm);
+        const symbol = wasm.symbolLocSymbol(loc);
         symbol.virtual_address = @as(u32, @intCast(memory_ptr));
     }
 
@@ -2364,18 +2493,17 @@ fn setupMemory(wasm: *Wasm) !void {
 /// From a given object's index and the index of the segment, returns the corresponding
 /// index of the segment within the final data section. When the segment does not yet
 /// exist, a new one will be initialized and appended. The new index will be returned in that case.
-pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 {
+pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !u32 {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     const diags = &wasm.base.comp.link_diags;
-    const obj_file = wasm.file(file_index).?;
-    const symbol = obj_file.symbols()[@intFromEnum(symbol_index)];
+    const symbol = objectSymbols(wasm, object_id)[@intFromEnum(symbol_index)];
     const index: u32 = @intCast(wasm.segments.items.len);
     const shared_memory = comp.config.shared_memory;
 
     switch (symbol.tag) {
         .data => {
-            const segment_info = obj_file.segmentInfo()[symbol.index];
+            const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index];
             const merge_segment = comp.config.output_mode != .Obj;
             const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
             if (!result.found_existing) {
@@ -2404,7 +2532,7 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
             break :blk index;
         },
         .section => {
-            const section_name = obj_file.symbolName(symbol_index);
+            const section_name = objectSymbolName(wasm, object_id, symbol_index);
             if (mem.eql(u8, section_name, ".debug_info")) {
                 return wasm.debug_info_index orelse blk: {
                     wasm.debug_info_index = index;
@@ -2456,7 +2584,7 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
             } else {
                 var err = try diags.addErrorWithNotes(1);
                 try err.addMsg("found unknown section '{s}'", .{section_name});
-                try err.addNote("defined in '{s}'", .{obj_file.path()});
+                try err.addNote("defined in '{s}'", .{objectPath(wasm, object_id)});
                 return error.UnexpectedValue;
             }
         },
@@ -2523,7 +2651,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
     const link_libcpp = comp.config.link_libcpp;
     const wasi_exec_model = comp.config.wasi_exec_model;
 
-    if (wasm.zigObjectPtr()) |zig_object| {
+    if (wasm.zig_object) |zig_object| {
         try zig_object.flushModule(wasm, tid);
     }
 
@@ -2578,17 +2706,17 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
         log.warn("Unexpected file format at path: '{s}'", .{path});
     }
 
-    if (wasm.zig_object_index != .null) {
-        try wasm.resolveSymbolsInObject(wasm.zig_object_index);
+    if (wasm.zig_object != null) {
+        try wasm.resolveSymbolsInObject(.zig_object);
     }
     if (diags.hasErrors()) return error.FlushFailure;
-    for (wasm.objects.items) |object_index| {
-        try wasm.resolveSymbolsInObject(object_index);
+    for (0..wasm.objects.items.len) |object_index| {
+        try wasm.resolveSymbolsInObject(@enumFromInt(object_index));
     }
     if (diags.hasErrors()) return error.FlushFailure;
 
     var emit_features_count: u32 = 0;
-    var enabled_features: [@typeInfo(types.Feature.Tag).@"enum".fields.len]bool = undefined;
+    var enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool = undefined;
     try wasm.validateFeatures(&enabled_features, &emit_features_count);
     try wasm.resolveSymbolsInArchives();
     if (diags.hasErrors()) return error.FlushFailure;
@@ -2622,7 +2750,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
 /// Writes the WebAssembly in-memory module to the file
 fn writeToFile(
     wasm: *Wasm,
-    enabled_features: [@typeInfo(types.Feature.Tag).@"enum".fields.len]bool,
+    enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool,
     feature_count: u32,
     arena: Allocator,
 ) !void {
@@ -2688,14 +2816,14 @@ fn writeToFile(
 
         var it = wasm.imports.iterator();
         while (it.next()) |entry| {
-            assert(entry.key_ptr.*.getSymbol(wasm).isUndefined());
+            assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined());
             const import = entry.value_ptr.*;
             try wasm.emitImport(binary_writer, import);
         }
 
         if (import_memory) {
             const mem_name = if (is_obj) "__linear_memory" else "memory";
-            const mem_imp: types.Import = .{
+            const mem_imp: Import = .{
                 .module_name = try wasm.string_table.put(gpa, wasm.host_name),
                 .name = try wasm.string_table.put(gpa, mem_name),
                 .kind = .{ .memory = wasm.memories.limits },
@@ -2829,7 +2957,7 @@ fn writeToFile(
         const header_offset = try reserveVecSectionHeader(&binary_bytes);
 
         const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
-        const table_sym = table_loc.getSymbol(wasm);
+        const table_sym = wasm.symbolLocSymbol(table_loc);
 
         const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
         try leb.writeUleb128(binary_writer, flags);
@@ -2843,7 +2971,7 @@ fn writeToFile(
         try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
         var symbol_it = wasm.function_table.keyIterator();
         while (symbol_it.next()) |symbol_loc_ptr| {
-            const sym = symbol_loc_ptr.getSymbol(wasm);
+            const sym = wasm.symbolLocSymbol(symbol_loc_ptr.*);
             std.debug.assert(sym.isAlive());
             std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
             try leb.writeUleb128(binary_writer, sym.index);
@@ -3183,7 +3311,7 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con
     try leb.writeUleb128(writer, features_count);
     for (enabled_features, 0..) |enabled, feature_index| {
         if (enabled) {
-            const feature: types.Feature = .{ .prefix = .used, .tag = @as(types.Feature.Tag, @enumFromInt(feature_index)) };
+            const feature: Feature = .{ .prefix = .used, .tag = @as(Feature.Tag, @enumFromInt(feature_index)) };
             try leb.writeUleb128(writer, @intFromEnum(feature.prefix));
             var buf: [100]u8 = undefined;
             const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag});
@@ -3219,11 +3347,11 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
     var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count());
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const symbol = sym_loc.getSymbol(wasm).*;
+        const symbol = wasm.symbolLocSymbol(sym_loc).*;
         if (symbol.isDead()) {
             continue;
         }
-        const name = sym_loc.getName(wasm);
+        const name = wasm.symbolLocName(sym_loc);
         switch (symbol.tag) {
             .function => {
                 const gop = funcs.getOrPutAssumeCapacity(symbol.index);
@@ -3320,7 +3448,7 @@ fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
     try writer.writeByte(std.wasm.opcode(.end));
 }
 
-fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void {
+fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void {
     const module_name = wasm.string_table.get(import.module_name);
     try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));
     try writer.writeAll(module_name);
@@ -3800,7 +3928,7 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
     // meta data version, which is currently '2'
     try leb.writeUleb128(writer, @as(u32, 2));
 
-    // For each subsection type (found in types.Subsection) we can emit a section.
+    // For each subsection type (found in Subsection) we can emit a section.
     // Currently, we only support emitting segment info and the symbol table.
     try wasm.emitSymbolTable(binary_bytes, symbol_table);
     try wasm.emitSegmentInfo(binary_bytes);
@@ -3812,12 +3940,12 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
 fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
     const writer = binary_bytes.writer();
 
-    try leb.writeUleb128(writer, @intFromEnum(types.SubsectionType.WASM_SYMBOL_TABLE));
+    try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE));
     const table_offset = binary_bytes.items.len;
 
     var symbol_count: u32 = 0;
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const symbol = sym_loc.getSymbol(wasm).*;
+        const symbol = wasm.symbolLocSymbol(sym_loc).*;
         if (symbol.tag == .dead) continue; // Do not emit dead symbols
         try symbol_table.putNoClobber(sym_loc, symbol_count);
         symbol_count += 1;
@@ -3825,7 +3953,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
         try leb.writeUleb128(writer, @intFromEnum(symbol.tag));
         try leb.writeUleb128(writer, symbol.flags);
 
-        const sym_name = sym_loc.getName(wasm);
+        const sym_name = wasm.symbolLocName(sym_loc);
         switch (symbol.tag) {
             .data => {
                 try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
@@ -3860,7 +3988,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
 
 fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
     const writer = binary_bytes.writer();
-    try leb.writeUleb128(writer, @intFromEnum(types.SubsectionType.WASM_SEGMENT_INFO));
+    try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO));
     const segment_offset = binary_bytes.items.len;
 
     try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));
@@ -4030,22 +4158,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
 /// Asserts declaration has an associated `Atom`.
 /// Returns the index into the list of types.
 pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
-    return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, nav, func_type);
+    return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type);
 }
 
 /// Returns the symbol index of the error name table.
 ///
 /// When the symbol does not yet exist, it will create a new one instead.
-pub fn getErrorTableSymbol(wasm_file: *Wasm, pt: Zcu.PerThread) !u32 {
-    const sym_index = try wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file, pt);
+pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {
+    const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt);
     return @intFromEnum(sym_index);
 }
 
 /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
 /// When the index was not found, a new `Atom` will be created, and its index will be returned.
 /// The newly created Atom is empty with default fields as specified by `Atom.empty`.
-pub fn getOrCreateAtomForNav(wasm_file: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index {
-    return wasm_file.zigObjectPtr().?.getOrCreateAtomForNav(wasm_file, pt, nav);
+pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index {
+    return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav);
 }
 
 /// Verifies all resolved symbols and checks whether itself needs to be marked alive,
@@ -4058,7 +4186,7 @@ fn markReferences(wasm: *Wasm) !void {
     const comp = wasm.base.comp;
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
-        const sym = sym_loc.getSymbol(wasm);
+        const sym = wasm.symbolLocSymbol(sym_loc);
         if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
             try wasm.mark(sym_loc);
             continue;
@@ -4067,8 +4195,8 @@ fn markReferences(wasm: *Wasm) !void {
         // Debug sections may require to be parsed and marked when it contains
         // relocations to alive symbols.
         if (sym.tag == .section and comp.config.debug_format != .strip) {
-            const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently
-            _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index);
+            const object_id = sym_loc.file.unwrap() orelse continue; // Incremental debug info is done independently
+            _ = try wasm.parseSymbolIntoAtom(object_id, sym_loc.index);
             sym.mark();
         }
     }
@@ -4077,7 +4205,7 @@ fn markReferences(wasm: *Wasm) !void {
 /// Marks a symbol as 'alive' recursively so itself and any references it contains to
 /// other symbols will not be omit from the binary.
 fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
-    const symbol = loc.getSymbol(wasm);
+    const symbol = wasm.symbolLocSymbol(loc);
     if (symbol.isAlive()) {
         // Symbol is already marked alive, including its references.
         // This means we can skip it so we don't end up marking the same symbols
@@ -4085,22 +4213,22 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
         return;
     }
     symbol.mark();
-    gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)});
+    gc_log.debug("Marked symbol '{s}'", .{wasm.symbolLocName(loc)});
     if (symbol.isUndefined()) {
         // undefined symbols do not have an associated `Atom` and therefore also
         // do not contain relocations.
         return;
     }
 
-    const atom_index = if (wasm.file(loc.file)) |obj_file|
-        try obj_file.parseSymbolIntoAtom(wasm, loc.index)
+    const atom_index = if (loc.file.unwrap()) |object_id|
+        try wasm.parseSymbolIntoAtom(object_id, loc.index)
     else
         wasm.symbol_atom.get(loc) orelse return;
 
     const atom = wasm.getAtom(atom_index);
     for (atom.relocs.items) |reloc| {
         const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file };
-        try wasm.mark(target_loc.finalLoc(wasm));
+        try wasm.mark(wasm.symbolLocFinalLoc(target_loc));
     }
 }
 
@@ -4110,3 +4238,566 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
         .command => "_start",
     };
 }
+
+pub const Atom = struct {
+    /// Represents the index of the file this atom was generated from.
+    /// This is `none` when the atom was generated by a synthetic linker symbol.
+    file: OptionalObjectId,
+    /// symbol index of the symbol representing this atom
+    sym_index: Symbol.Index,
+    /// Size of the atom, used to calculate section sizes in the final binary
+    size: u32 = 0,
+    /// List of relocations belonging to this atom
+    relocs: std.ArrayListUnmanaged(Relocation) = .empty,
+    /// Contains the binary data of an atom, which can be non-relocated
+    code: std.ArrayListUnmanaged(u8) = .empty,
+    /// For code this is 1, for data this is set to the highest value of all segments
+    alignment: Wasm.Alignment = .@"1",
+    /// Offset into the section where the atom lives, this already accounts
+    /// for alignment.
+    offset: u32 = 0,
+    /// The original offset within the object file. This value is subtracted from
+    /// relocation offsets to determine where in the `data` to rewrite the value
+    original_offset: u32 = 0,
+    /// Previous atom in relation to this atom.
+    /// is null when this atom is the first in its order
+    prev: Atom.Index = .null,
+    /// Contains atoms local to a decl, all managed by this `Atom`.
+    /// When the parent atom is being freed, it will also do so for all local atoms.
+    locals: std.ArrayListUnmanaged(Atom.Index) = .empty,
+
+    /// Represents the index of an Atom where `null` is considered
+    /// an invalid atom.
+    pub const Index = enum(u32) {
+        null = std.math.maxInt(u32),
+        _,
+    };
+
+    /// Frees all resources owned by this `Atom`.
+    pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
+        atom.relocs.deinit(gpa);
+        atom.code.deinit(gpa);
+        atom.locals.deinit(gpa);
+        atom.* = undefined;
+    }
+
+    /// Sets the length of relocations and code to '0',
+    /// effectively resetting them and allowing them to be re-populated.
+    pub fn clear(atom: *Atom) void {
+        atom.relocs.clearRetainingCapacity();
+        atom.code.clearRetainingCapacity();
+    }
+
+    pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+        _ = fmt;
+        _ = options;
+        try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
+            @intFromEnum(atom.sym_index),
+            atom.alignment,
+            atom.size,
+            atom.offset,
+        });
+    }
+
+    /// Returns the location of the symbol that represents this `Atom`
+    pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
+        return .{
+            .file = atom.file,
+            .index = atom.sym_index,
+        };
+    }
+
+    /// Resolves the relocations within the atom, writing the new value
+    /// at the calculated offset.
+    pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void {
+        if (atom.relocs.items.len == 0) return;
+        const symbol_name = wasm.symbolLocName(atom.symbolLoc());
+        log.debug("Resolving relocs in atom '{s}' count({d})", .{
+            symbol_name,
+            atom.relocs.items.len,
+        });
+
+        for (atom.relocs.items) |reloc| {
+            const value = atom.relocationValue(reloc, wasm);
+            log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
+                wasm.symbolLocName(.{
+                    .file = atom.file,
+                    .index = @enumFromInt(reloc.index),
+                }),
+                symbol_name,
+                reloc.offset,
+                value,
+            });
+
+            switch (reloc.relocation_type) {
+                .R_WASM_TABLE_INDEX_I32,
+                .R_WASM_FUNCTION_OFFSET_I32,
+                .R_WASM_GLOBAL_INDEX_I32,
+                .R_WASM_MEMORY_ADDR_I32,
+                .R_WASM_SECTION_OFFSET_I32,
+                => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
+                .R_WASM_TABLE_INDEX_I64,
+                .R_WASM_MEMORY_ADDR_I64,
+                => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
+                .R_WASM_GLOBAL_INDEX_LEB,
+                .R_WASM_EVENT_INDEX_LEB,
+                .R_WASM_FUNCTION_INDEX_LEB,
+                .R_WASM_MEMORY_ADDR_LEB,
+                .R_WASM_MEMORY_ADDR_SLEB,
+                .R_WASM_TABLE_INDEX_SLEB,
+                .R_WASM_TABLE_NUMBER_LEB,
+                .R_WASM_TYPE_INDEX_LEB,
+                .R_WASM_MEMORY_ADDR_TLS_SLEB,
+                => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
+                .R_WASM_MEMORY_ADDR_LEB64,
+                .R_WASM_MEMORY_ADDR_SLEB64,
+                .R_WASM_TABLE_INDEX_SLEB64,
+                .R_WASM_MEMORY_ADDR_TLS_SLEB64,
+                => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
+            }
+        }
+    }
+
+    /// From a given `relocation` will return the new value to be written.
+    /// 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(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 {
+        const target_loc = wasm.symbolLocFinalLoc(.{
+            .file = atom.file,
+            .index = @enumFromInt(relocation.index),
+        });
+        const symbol = wasm.symbolLocSymbol(target_loc);
+        if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
+            symbol.tag != .section and
+            symbol.isDead())
+        {
+            const val = atom.thombstone(wasm) orelse relocation.addend;
+            return @bitCast(val);
+        }
+        switch (relocation.relocation_type) {
+            .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
+            .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
+            .R_WASM_TABLE_INDEX_I32,
+            .R_WASM_TABLE_INDEX_I64,
+            .R_WASM_TABLE_INDEX_SLEB,
+            .R_WASM_TABLE_INDEX_SLEB64,
+            => return wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
+            .R_WASM_TYPE_INDEX_LEB => {
+                const object_id = atom.file.unwrap() orelse return relocation.index;
+                const original_type = objectFuncTypes(wasm, object_id)[relocation.index];
+                return wasm.getTypeIndex(original_type).?;
+            },
+            .R_WASM_GLOBAL_INDEX_I32,
+            .R_WASM_GLOBAL_INDEX_LEB,
+            => return symbol.index,
+            .R_WASM_MEMORY_ADDR_I32,
+            .R_WASM_MEMORY_ADDR_I64,
+            .R_WASM_MEMORY_ADDR_LEB,
+            .R_WASM_MEMORY_ADDR_LEB64,
+            .R_WASM_MEMORY_ADDR_SLEB,
+            .R_WASM_MEMORY_ADDR_SLEB64,
+            => {
+                std.debug.assert(symbol.tag == .data);
+                if (symbol.isUndefined()) {
+                    return 0;
+                }
+                const va: i33 = @intCast(symbol.virtual_address);
+                return @intCast(va + relocation.addend);
+            },
+            .R_WASM_EVENT_INDEX_LEB => return symbol.index,
+            .R_WASM_SECTION_OFFSET_I32 => {
+                const target_atom_index = wasm.symbol_atom.get(target_loc).?;
+                const target_atom = wasm.getAtom(target_atom_index);
+                const rel_value: i33 = @intCast(target_atom.offset);
+                return @intCast(rel_value + relocation.addend);
+            },
+            .R_WASM_FUNCTION_OFFSET_I32 => {
+                if (symbol.isUndefined()) {
+                    const val = atom.thombstone(wasm) orelse relocation.addend;
+                    return @bitCast(val);
+                }
+                const target_atom_index = wasm.symbol_atom.get(target_loc).?;
+                const target_atom = wasm.getAtom(target_atom_index);
+                const rel_value: i33 = @intCast(target_atom.offset);
+                return @intCast(rel_value + relocation.addend);
+            },
+            .R_WASM_MEMORY_ADDR_TLS_SLEB,
+            .R_WASM_MEMORY_ADDR_TLS_SLEB64,
+            => {
+                const va: i33 = @intCast(symbol.virtual_address);
+                return @intCast(va + relocation.addend);
+            },
+        }
+    }
+
+    // For a given `Atom` returns whether it has a thombstone value or not.
+    /// This defines whether we want a specific value when a section is dead.
+    fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
+        const atom_name = wasm.symbolLocName(atom.symbolLoc());
+        if (std.mem.eql(u8, atom_name, ".debug_ranges") or std.mem.eql(u8, atom_name, ".debug_loc")) {
+            return -2;
+        } else if (std.mem.startsWith(u8, atom_name, ".debug_")) {
+            return -1;
+        }
+        return null;
+    }
+};
+
+pub const Relocation = struct {
+    /// Represents the type of the `Relocation`
+    relocation_type: RelocationType,
+    /// Offset of the value to rewrite relative to the relevant section's contents.
+    /// When `offset` is zero, its position is immediately after the id and size of the section.
+    offset: u32,
+    /// The index of the symbol used.
+    /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type.
+    index: u32,
+    /// Addend to add to the address.
+    /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`.
+    addend: i32 = 0,
+
+    /// All possible relocation types currently existing.
+    /// This enum is exhaustive as the spec is WIP and new types
+    /// can be added which means that a generated binary will be invalid,
+    /// so instead we will show an error in such cases.
+    pub const RelocationType = enum(u8) {
+        R_WASM_FUNCTION_INDEX_LEB = 0,
+        R_WASM_TABLE_INDEX_SLEB = 1,
+        R_WASM_TABLE_INDEX_I32 = 2,
+        R_WASM_MEMORY_ADDR_LEB = 3,
+        R_WASM_MEMORY_ADDR_SLEB = 4,
+        R_WASM_MEMORY_ADDR_I32 = 5,
+        R_WASM_TYPE_INDEX_LEB = 6,
+        R_WASM_GLOBAL_INDEX_LEB = 7,
+        R_WASM_FUNCTION_OFFSET_I32 = 8,
+        R_WASM_SECTION_OFFSET_I32 = 9,
+        R_WASM_EVENT_INDEX_LEB = 10,
+        R_WASM_GLOBAL_INDEX_I32 = 13,
+        R_WASM_MEMORY_ADDR_LEB64 = 14,
+        R_WASM_MEMORY_ADDR_SLEB64 = 15,
+        R_WASM_MEMORY_ADDR_I64 = 16,
+        R_WASM_TABLE_INDEX_SLEB64 = 18,
+        R_WASM_TABLE_INDEX_I64 = 19,
+        R_WASM_TABLE_NUMBER_LEB = 20,
+        R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
+        R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
+
+        /// Returns true for relocation types where the `addend` field is present.
+        pub fn addendIsPresent(self: RelocationType) bool {
+            return switch (self) {
+                .R_WASM_MEMORY_ADDR_LEB,
+                .R_WASM_MEMORY_ADDR_SLEB,
+                .R_WASM_MEMORY_ADDR_I32,
+                .R_WASM_MEMORY_ADDR_LEB64,
+                .R_WASM_MEMORY_ADDR_SLEB64,
+                .R_WASM_MEMORY_ADDR_I64,
+                .R_WASM_MEMORY_ADDR_TLS_SLEB,
+                .R_WASM_MEMORY_ADDR_TLS_SLEB64,
+                .R_WASM_FUNCTION_OFFSET_I32,
+                .R_WASM_SECTION_OFFSET_I32,
+                => true,
+                else => false,
+            };
+        }
+    };
+
+    /// Verifies the relocation type of a given `Relocation` and returns
+    /// true when the relocation references a function call or address to a function.
+    pub fn isFunction(self: Relocation) bool {
+        return switch (self.relocation_type) {
+            .R_WASM_FUNCTION_INDEX_LEB,
+            .R_WASM_TABLE_INDEX_SLEB,
+            => true,
+            else => false,
+        };
+    }
+
+    pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+        _ = fmt;
+        _ = options;
+        try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{
+            @tagName(self.relocation_type),
+            self.offset,
+            self.index,
+        });
+    }
+};
+
+/// Unlike the `Import` object defined by the wasm spec, and existing
+/// in the std.wasm namespace, this construct saves the 'module name' and 'name'
+/// of the import using offsets into a string table, rather than the slices itself.
+/// This saves us (potentially) 24 bytes per import on 64bit machines.
+pub const Import = struct {
+    module_name: u32,
+    name: u32,
+    kind: std.wasm.Import.Kind,
+};
+
+/// Unlike the `Export` object defined by the wasm spec, and existing
+/// in the std.wasm namespace, this construct saves the 'name'
+/// of the export using offsets into a string table, rather than the slice itself.
+/// This saves us (potentially) 12 bytes per export on 64bit machines.
+pub const Export = struct {
+    name: u32,
+    index: u32,
+    kind: std.wasm.ExternalKind,
+};
+
+pub const SubsectionType = enum(u8) {
+    WASM_SEGMENT_INFO = 5,
+    WASM_INIT_FUNCS = 6,
+    WASM_COMDAT_INFO = 7,
+    WASM_SYMBOL_TABLE = 8,
+};
+
+pub const Alignment = @import("../InternPool.zig").Alignment;
+
+pub const NamedSegment = struct {
+    /// Segment's name, encoded as UTF-8 bytes.
+    name: []const u8,
+    /// The required alignment of the segment, encoded as a power of 2
+    alignment: Alignment,
+    /// Bitfield containing flags for a segment
+    flags: u32,
+
+    pub fn isTLS(segment: NamedSegment) bool {
+        return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0;
+    }
+
+    /// Returns the name as how it will be output into the final object
+    /// file or binary. When `merge_segments` is true, this will return the
+    /// short name. i.e. ".rodata". When false, it returns the entire name instead.
+    pub fn outputName(segment: NamedSegment, merge_segments: bool) []const u8 {
+        if (segment.isTLS()) {
+            return ".tdata";
+        } else if (!merge_segments) {
+            return segment.name;
+        } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
+            return ".rodata";
+        } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
+            return ".text";
+        } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
+            return ".data";
+        } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
+            return ".bss";
+        }
+        return segment.name;
+    }
+
+    pub const Flags = enum(u32) {
+        WASM_SEG_FLAG_STRINGS = 0x1,
+        WASM_SEG_FLAG_TLS = 0x2,
+    };
+};
+
+pub const InitFunc = struct {
+    /// Priority of the init function
+    priority: u32,
+    /// The symbol index of init function (not the function index).
+    symbol_index: u32,
+};
+
+pub const Comdat = struct {
+    name: []const u8,
+    /// Must be zero, no flags are currently defined by the tool-convention.
+    flags: u32,
+    symbols: []const ComdatSym,
+};
+
+pub const ComdatSym = struct {
+    kind: @This().Type,
+    /// Index of the data segment/function/global/event/table within a WASM module.
+    /// The object must not be an import.
+    index: u32,
+
+    pub const Type = enum(u8) {
+        WASM_COMDAT_DATA = 0,
+        WASM_COMDAT_FUNCTION = 1,
+        WASM_COMDAT_GLOBAL = 2,
+        WASM_COMDAT_EVENT = 3,
+        WASM_COMDAT_TABLE = 4,
+        WASM_COMDAT_SECTION = 5,
+    };
+};
+
+pub const Feature = struct {
+    /// Provides information about the usage of the feature.
+    /// - '0x2b' (+): Object uses this feature, and the link fails if feature is not in the allowed set.
+    /// - '0x2d' (-): Object does not use this feature, and the link fails if this feature is in the allowed set.
+    /// - '0x3d' (=): Object uses this feature, and the link fails if this feature is not in the allowed set,
+    /// or if any object does not use this feature.
+    prefix: Prefix,
+    /// Type of the feature, must be unique in the sequence of features.
+    tag: Tag,
+
+    /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
+    pub const Tag = enum {
+        atomics,
+        bulk_memory,
+        exception_handling,
+        extended_const,
+        half_precision,
+        multimemory,
+        multivalue,
+        mutable_globals,
+        nontrapping_fptoint,
+        reference_types,
+        relaxed_simd,
+        sign_ext,
+        simd128,
+        tail_call,
+        shared_mem,
+
+        /// From a given cpu feature, returns its linker feature
+        pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
+            return @as(Tag, @enumFromInt(@intFromEnum(feature)));
+        }
+
+        pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
+            _ = fmt;
+            _ = opt;
+            try writer.writeAll(switch (tag) {
+                .atomics => "atomics",
+                .bulk_memory => "bulk-memory",
+                .exception_handling => "exception-handling",
+                .extended_const => "extended-const",
+                .half_precision => "half-precision",
+                .multimemory => "multimemory",
+                .multivalue => "multivalue",
+                .mutable_globals => "mutable-globals",
+                .nontrapping_fptoint => "nontrapping-fptoint",
+                .reference_types => "reference-types",
+                .relaxed_simd => "relaxed-simd",
+                .sign_ext => "sign-ext",
+                .simd128 => "simd128",
+                .tail_call => "tail-call",
+                .shared_mem => "shared-mem",
+            });
+        }
+    };
+
+    pub const Prefix = enum(u8) {
+        used = '+',
+        disallowed = '-',
+        required = '=',
+    };
+
+    pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
+        _ = opt;
+        _ = fmt;
+        try writer.print("{c} {}", .{ feature.prefix, feature.tag });
+    }
+};
+
+pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{
+    .{ "atomics", .atomics },
+    .{ "bulk-memory", .bulk_memory },
+    .{ "exception-handling", .exception_handling },
+    .{ "extended-const", .extended_const },
+    .{ "half-precision", .half_precision },
+    .{ "multimemory", .multimemory },
+    .{ "multivalue", .multivalue },
+    .{ "mutable-globals", .mutable_globals },
+    .{ "nontrapping-fptoint", .nontrapping_fptoint },
+    .{ "reference-types", .reference_types },
+    .{ "relaxed-simd", .relaxed_simd },
+    .{ "sign-ext", .sign_ext },
+    .{ "simd128", .simd128 },
+    .{ "tail-call", .tail_call },
+    .{ "shared-mem", .shared_mem },
+});
+
+/// Parses an object file into atoms, for code and data sections
+fn parseSymbolIntoAtom(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Atom.Index {
+    const object = wasm.objectById(object_id) orelse
+        return wasm.zig_object.?.parseSymbolIntoAtom(wasm, symbol_index);
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const symbol = &object.symtable[@intFromEnum(symbol_index)];
+    const relocatable_data: Object.RelocatableData = switch (symbol.tag) {
+        .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
+        .data => object.relocatable_data.get(.data).?[symbol.index],
+        .section => blk: {
+            const data = object.relocatable_data.get(.custom).?;
+            for (data) |dat| {
+                if (dat.section_index == symbol.index) {
+                    break :blk dat;
+                }
+            }
+            unreachable;
+        },
+        else => unreachable,
+    };
+    const final_index = try wasm.getMatchingSegment(object_id, symbol_index);
+    const atom_index = try wasm.createAtom(symbol_index, object_id.toOptional());
+    try wasm.appendAtomAtIndex(final_index, atom_index);
+
+    const atom = wasm.getAtomPtr(atom_index);
+    atom.size = relocatable_data.size;
+    atom.alignment = relocatable_data.getAlignment(object);
+    atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
+    atom.original_offset = relocatable_data.offset;
+
+    const segment: *Wasm.Segment = &wasm.segments.items[final_index];
+    if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
+        segment.alignment = segment.alignment.max(atom.alignment);
+    }
+
+    if (object.relocations.get(relocatable_data.section_index)) |relocations| {
+        const start = searchRelocStart(relocations, relocatable_data.offset);
+        const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
+        atom.relocs = std.ArrayListUnmanaged(Wasm.Relocation).fromOwnedSlice(relocations[start..][0..len]);
+        for (atom.relocs.items) |reloc| {
+            switch (reloc.relocation_type) {
+                .R_WASM_TABLE_INDEX_I32,
+                .R_WASM_TABLE_INDEX_I64,
+                .R_WASM_TABLE_INDEX_SLEB,
+                .R_WASM_TABLE_INDEX_SLEB64,
+                => {
+                    try wasm.function_table.put(gpa, .{
+                        .file = object_id.toOptional(),
+                        .index = @enumFromInt(reloc.index),
+                    }, 0);
+                },
+                .R_WASM_GLOBAL_INDEX_I32,
+                .R_WASM_GLOBAL_INDEX_LEB,
+                => {
+                    const sym = object.symtable[reloc.index];
+                    if (sym.tag != .global) {
+                        try wasm.got_symbols.append(gpa, .{
+                            .file = object_id.toOptional(),
+                            .index = @enumFromInt(reloc.index),
+                        });
+                    }
+                },
+                else => {},
+            }
+        }
+    }
+
+    return atom_index;
+}
+
+fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize {
+    var min: usize = 0;
+    var max: usize = relocs.len;
+    while (min < max) {
+        const index = (min + max) / 2;
+        const curr = relocs[index];
+        if (curr.offset < address) {
+            min = index + 1;
+        } else {
+            max = index;
+        }
+    }
+    return min;
+}
+
+fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize {
+    for (relocs, 0..relocs.len) |reloc, index| {
+        if (reloc.offset > address) {
+            return index;
+        }
+    }
+    return relocs.len;
+}
CMakeLists.txt
@@ -648,12 +648,9 @@ set(ZIG_STAGE2_SOURCES
     src/link/StringTable.zig
     src/link/Wasm.zig
     src/link/Wasm/Archive.zig
-    src/link/Wasm/Atom.zig
     src/link/Wasm/Object.zig
     src/link/Wasm/Symbol.zig
     src/link/Wasm/ZigObject.zig
-    src/link/Wasm/file.zig
-    src/link/Wasm/types.zig
     src/link/aarch64.zig
     src/link/riscv.zig
     src/link/table_section.zig