Commit 0789e91eeb

Andrew Kelley <andrew@ziglang.org>
2023-12-13 00:25:28
linkers: update references to "options" field
1 parent 3b6cb25
src/codegen/llvm.zig
@@ -3083,9 +3083,7 @@ pub const Object = struct {
         if (comp.unwind_tables) {
             try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder);
         }
-        if (comp.bin_file.options.skip_linker_dependencies or
-            comp.bin_file.options.no_builtin)
-        {
+        if (comp.skip_linker_dependencies or comp.bin_file.options.no_builtin) {
             // The intent here is for compiler-rt and libc functions to not generate
             // infinite recursion. For example, if we are compiling the memcpy function,
             // and llvm detects that the body is equivalent to memcpy, it may replace the
src/Compilation/Config.zig
@@ -335,7 +335,7 @@ pub fn resolve(options: Options) !Config {
         break :b .Static;
     };
 
-    const import_memory = options.import_memory orelse false;
+    const import_memory = options.import_memory orelse (options.output_mode == .Obj);
     const export_memory = b: {
         if (link_mode == .Dynamic) {
             if (options.export_memory == true) return error.ExportMemoryAndDynamicIncompatible;
src/link/Coff/lld.zig
@@ -48,7 +48,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
     const is_lib = self.base.comp.config.output_mode == .Lib;
     const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib;
     const is_exe_or_dyn_lib = is_dyn_lib or self.base.comp.config.output_mode == .Exe;
-    const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
+    const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
     const target = self.base.comp.root_mod.resolved_target.result;
     const optimize_mode = self.base.comp.root_mod.optimize_mode;
 
@@ -56,17 +56,17 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
     const id_symlink_basename = "lld.id";
 
     var man: Cache.Manifest = undefined;
-    defer if (!self.base.options.disable_lld_caching) man.deinit();
+    defer if (!self.base.disable_lld_caching) man.deinit();
 
     var digest: [Cache.hex_digest_len]u8 = undefined;
 
-    if (!self.base.options.disable_lld_caching) {
+    if (!self.base.disable_lld_caching) {
         man = comp.cache_parent.obtain();
         self.base.releaseLock();
 
         comptime assert(Compilation.link_hash_implementation_version == 10);
 
-        for (self.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             _ = try man.addFile(obj.path, null);
             man.hash.add(obj.must_link);
         }
@@ -79,12 +79,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             }
         }
         try man.addOptionalFile(module_obj_path);
-        man.hash.addOptionalBytes(self.base.options.entry);
+        man.hash.addOptionalBytes(comp.config.entry);
         man.hash.add(self.base.stack_size);
         man.hash.addOptional(self.image_base);
-        man.hash.addListOfBytes(self.base.options.lib_dirs);
-        man.hash.add(self.base.options.skip_linker_dependencies);
-        if (self.base.options.link_libc) {
+        man.hash.addListOfBytes(self.lib_dirs);
+        man.hash.add(comp.skip_linker_dependencies);
+        if (comp.config.link_libc) {
             man.hash.add(self.base.comp.libc_installation != null);
             if (self.base.comp.libc_installation) |libc_installation| {
                 man.hash.addBytes(libc_installation.crt_dir.?);
@@ -95,18 +95,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             }
         }
         try link.hashAddSystemLibs(&man, self.base.comp.system_libs);
-        man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
-        man.hash.addOptional(self.base.options.subsystem);
-        man.hash.add(self.base.options.is_test);
-        man.hash.add(self.base.options.tsaware);
-        man.hash.add(self.base.options.nxcompat);
-        man.hash.add(self.base.options.dynamicbase);
+        man.hash.addListOfBytes(self.base.force_undefined_symbols.keys());
+        man.hash.addOptional(self.subsystem);
+        man.hash.add(comp.config.is_test);
+        man.hash.add(self.tsaware);
+        man.hash.add(self.nxcompat);
+        man.hash.add(self.dynamicbase);
         man.hash.addOptional(self.base.allow_shlib_undefined);
         // strip does not need to go into the linker hash because it is part of the hash namespace
-        man.hash.addOptional(self.base.options.major_subsystem_version);
-        man.hash.addOptional(self.base.options.minor_subsystem_version);
-        man.hash.addOptional(self.base.options.version);
-        try man.addOptionalFile(self.base.options.module_definition_file);
+        man.hash.addOptional(self.major_subsystem_version);
+        man.hash.addOptional(self.minor_subsystem_version);
+        man.hash.addOptional(comp.version);
+        try man.addOptionalFile(self.module_definition_file);
 
         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
         _ = try man.hit();
@@ -141,8 +141,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
         // here. TODO: think carefully about how we can avoid this redundant operation when doing
         // build-obj. See also the corresponding TODO in linkAsArchive.
         const the_object_path = blk: {
-            if (self.base.options.objects.len != 0)
-                break :blk self.base.options.objects[0].path;
+            if (comp.objects.len != 0)
+                break :blk comp.objects[0].path;
 
             if (comp.c_object_table.count() != 0)
                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
@@ -171,21 +171,21 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
 
         try argv.append("-ERRORLIMIT:0");
         try argv.append("-NOLOGO");
-        if (!self.base.options.strip) {
+        if (self.base.debug_format != .strip) {
             try argv.append("-DEBUG");
 
             const out_ext = std.fs.path.extension(full_out_path);
-            const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
+            const out_pdb = self.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
                 full_out_path[0 .. full_out_path.len - out_ext.len],
             });
 
             try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
             try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb}));
         }
-        if (self.base.options.version) |version| {
+        if (comp.version) |version| {
             try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
         }
-        if (self.base.options.lto) {
+        if (comp.config.lto) {
             switch (optimize_mode) {
                 .Debug => {},
                 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
@@ -209,7 +209,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             }
         }
 
-        for (self.base.options.force_undefined_symbols.keys()) |symbol| {
+        for (self.base.force_undefined_symbols.keys()) |symbol| {
             try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
         }
 
@@ -217,17 +217,17 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             try argv.append("-DLL");
         }
 
-        if (self.base.options.entry) |entry| {
+        if (comp.config.entry) |entry| {
             try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
         }
 
-        if (self.base.options.tsaware) {
+        if (self.tsaware) {
             try argv.append("-tsaware");
         }
-        if (self.base.options.nxcompat) {
+        if (self.nxcompat) {
             try argv.append("-nxcompat");
         }
-        if (!self.base.options.dynamicbase) {
+        if (!self.dynamicbase) {
             try argv.append("-dynamicbase:NO");
         }
         if (self.base.allow_shlib_undefined) {
@@ -236,12 +236,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
 
         try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
 
-        if (self.base.options.implib_emit) |emit| {
+        if (self.implib_emit) |emit| {
             const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
             try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
         }
 
-        if (self.base.options.link_libc) {
+        if (comp.config.link_libc) {
             if (self.base.comp.libc_installation) |libc_installation| {
                 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
 
@@ -252,12 +252,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             }
         }
 
-        for (self.base.options.lib_dirs) |lib_dir| {
+        for (self.lib_dirs) |lib_dir| {
             try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
         }
 
-        try argv.ensureUnusedCapacity(self.base.options.objects.len);
-        for (self.base.options.objects) |obj| {
+        try argv.ensureUnusedCapacity(comp.objects.len);
+        for (comp.objects) |obj| {
             if (obj.must_link) {
                 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
             } else {
@@ -279,18 +279,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
             try argv.append(p);
         }
 
-        if (self.base.options.module_definition_file) |def| {
+        if (self.module_definition_file) |def| {
             try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
         }
 
         const resolved_subsystem: ?std.Target.SubSystem = blk: {
-            if (self.base.options.subsystem) |explicit| break :blk explicit;
+            if (self.subsystem) |explicit| break :blk explicit;
             switch (target.os.tag) {
                 .windows => {
                     if (self.base.comp.module) |module| {
                         if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
                             break :blk null;
-                        if (module.stage1_flags.have_c_main or self.base.options.is_test or
+                        if (module.stage1_flags.have_c_main or comp.config.is_test or
                             module.stage1_flags.have_winmain_crt_startup or
                             module.stage1_flags.have_wwinmain_crt_startup)
                         {
@@ -310,8 +310,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
         const mode: Mode = mode: {
             if (resolved_subsystem) |subsystem| {
                 const subsystem_suffix = ss: {
-                    if (self.base.options.major_subsystem_version) |major| {
-                        if (self.base.options.minor_subsystem_version) |minor| {
+                    if (self.major_subsystem_version) |major| {
+                        if (self.minor_subsystem_version) |minor| {
                             break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
                         } else {
                             break :ss try allocPrint(arena, ",{d}", .{major});
@@ -447,7 +447,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
                     }
                 } else {
                     try argv.append("-NODEFAULTLIB");
-                    if (!is_lib and self.base.options.entry == null) {
+                    if (!is_lib and comp.config.entry == null) {
                         if (self.base.comp.module) |module| {
                             if (module.stage1_flags.have_winmain_crt_startup) {
                                 try argv.append("-ENTRY:WinMainCRTStartup");
@@ -463,18 +463,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
         }
 
         // libc++ dep
-        if (self.base.options.link_libcpp) {
+        if (comp.config.link_libcpp) {
             try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
             try argv.append(comp.libcxx_static_lib.?.full_object_path);
         }
 
         // libunwind dep
-        if (self.base.options.link_libunwind) {
+        if (comp.config.link_libunwind) {
             try argv.append(comp.libunwind_static_lib.?.full_object_path);
         }
 
-        if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
-            if (!self.base.options.link_libc) {
+        if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
+            if (!comp.config.link_libc) {
                 if (comp.libc_static_lib) |lib| {
                     try argv.append(lib.full_object_path);
                 }
@@ -492,13 +492,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
                 argv.appendAssumeCapacity(crt_file.full_object_path);
                 continue;
             }
-            if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| {
+            if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {
                 argv.appendAssumeCapacity(full_path);
                 continue;
             }
             if (target.abi.isGnu()) {
                 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
-                if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| {
+                if (try findLib(arena, fallback_name, self.lib_dirs)) |full_path| {
                     argv.appendAssumeCapacity(full_path);
                     continue;
                 }
@@ -582,7 +582,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
         }
     }
 
-    if (!self.base.options.disable_lld_caching) {
+    if (!self.base.disable_lld_caching) {
         // Update the file with the digest. If it fails we can continue; it only
         // means that the next invocation will have an unnecessary cache miss.
         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
src/link/Elf/Atom.zig
@@ -543,7 +543,7 @@ fn scanReloc(
             try self.reportPicError(symbol, rel, elf_file),
 
         .copyrel => {
-            if (elf_file.base.options.z_nocopyreloc) {
+            if (elf_file.z_nocopyreloc) {
                 if (symbol.isAbs(elf_file))
                     try self.reportNoPicError(symbol, rel, elf_file)
                 else
@@ -553,9 +553,9 @@ fn scanReloc(
         },
 
         .dyn_copyrel => {
-            if (is_writeable or elf_file.base.options.z_nocopyreloc) {
+            if (is_writeable or elf_file.z_nocopyreloc) {
                 if (!is_writeable) {
-                    if (elf_file.base.options.z_notext) {
+                    if (elf_file.z_notext) {
                         elf_file.has_text_reloc = true;
                     } else {
                         try self.reportTextRelocError(symbol, rel, elf_file);
@@ -587,7 +587,7 @@ fn scanReloc(
 
         .dynrel, .baserel, .ifunc => {
             if (!is_writeable) {
-                if (elf_file.base.options.z_notext) {
+                if (elf_file.z_notext) {
                     elf_file.has_text_reloc = true;
                 } else {
                     try self.reportTextRelocError(symbol, rel, elf_file);
@@ -657,11 +657,12 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
 }
 
 fn outputType(elf_file: *Elf) u2 {
+    const comp = elf_file.base.comp;
     assert(!elf_file.isRelocatable());
     return switch (elf_file.base.comp.config.output_mode) {
         .Obj => unreachable,
         .Lib => 0,
-        .Exe => if (elf_file.base.options.pie) 1 else 2,
+        .Exe => if (comp.config.pie) 1 else 2,
     };
 }
 
@@ -979,7 +980,7 @@ fn resolveDynAbsReloc(
         => try writer.writeInt(i32, @as(i32, @truncate(S + A)), .little),
 
         .dyn_copyrel => {
-            if (is_writeable or elf_file.base.options.z_nocopyreloc) {
+            if (is_writeable or elf_file.z_nocopyreloc) {
                 elf_file.addRelaDynAssumeCapacity(.{
                     .offset = P,
                     .sym = target.extra(elf_file).?.dynamic,
src/link/Elf/Object.zig
@@ -299,7 +299,7 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
         if (mem.startsWith(u8, name, ".note")) break :blk true;
         if (mem.startsWith(u8, name, ".comment")) break :blk true;
         if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
-        if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
+        if (elf_file.base.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
             mem.startsWith(u8, name, ".debug")) break :blk true;
         break :blk false;
     };
src/link/Elf/synthetic_sections.zig
@@ -32,7 +32,7 @@ pub const DynamicSection = struct {
     fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 {
         _ = dt;
         var flags: u64 = 0;
-        if (elf_file.base.options.z_now) {
+        if (elf_file.z_now) {
             flags |= elf.DF_BIND_NOW;
         }
         for (elf_file.got.entries.items) |entry| switch (entry.tag) {
@@ -49,15 +49,16 @@ pub const DynamicSection = struct {
     }
 
     fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 {
+        const comp = elf_file.base.comp;
         _ = dt;
         var flags_1: u64 = 0;
-        if (elf_file.base.options.z_now) {
+        if (elf_file.z_now) {
             flags_1 |= elf.DF_1_NOW;
         }
-        if (elf_file.isExe() and elf_file.base.options.pie) {
+        if (elf_file.isExe() and comp.config.pie) {
             flags_1 |= elf.DF_1_PIE;
         }
-        // if (elf_file.base.options.z_nodlopen) {
+        // if (elf_file.z_nodlopen) {
         //     flags_1 |= elf.DF_1_NOOPEN;
         // }
         return if (flags_1 > 0) flags_1 else null;
@@ -246,12 +247,13 @@ pub const ZigGotSection = struct {
     }
 
     pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
+        const comp = elf_file.base.comp;
         const index = try zig_got.allocateEntry(elf_file.base.allocator);
         const entry = &zig_got.entries.items[index];
         entry.* = sym_index;
         const symbol = elf_file.symbol(sym_index);
         symbol.flags.has_zig_got = true;
-        if (elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) {
+        if (elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) {
             zig_got.flags.needs_rela = true;
         }
         if (symbol.extra(elf_file)) |extra| {
@@ -478,6 +480,7 @@ pub const GotSection = struct {
     }
 
     pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
+        const comp = elf_file.base.comp;
         const index = try got.allocateEntry(elf_file.base.allocator);
         const entry = &got.entries.items[index];
         entry.tag = .got;
@@ -485,7 +488,7 @@ pub const GotSection = struct {
         const symbol = elf_file.symbol(sym_index);
         symbol.flags.has_got = true;
         if (symbol.flags.import or symbol.isIFunc(elf_file) or
-            ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and !symbol.isAbs(elf_file)))
+            ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file)))
         {
             got.flags.needs_rela = true;
         }
@@ -561,6 +564,7 @@ pub const GotSection = struct {
     }
 
     pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void {
+        const comp = elf_file.base.comp;
         const is_dyn_lib = elf_file.isDynLib();
         const apply_relocs = true; // TODO add user option for this
 
@@ -576,7 +580,7 @@ pub const GotSection = struct {
                         if (symbol.?.flags.import) break :blk 0;
                         if (symbol.?.isIFunc(elf_file))
                             break :blk if (apply_relocs) value else 0;
-                        if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and
+                        if ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and
                             !symbol.?.isAbs(elf_file))
                         {
                             break :blk if (apply_relocs) value else 0;
@@ -623,6 +627,7 @@ pub const GotSection = struct {
     }
 
     pub fn addRela(got: GotSection, elf_file: *Elf) !void {
+        const comp = elf_file.base.comp;
         const is_dyn_lib = elf_file.isDynLib();
         try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file));
 
@@ -652,7 +657,7 @@ pub const GotSection = struct {
                         });
                         continue;
                     }
-                    if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and
+                    if ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and
                         !symbol.?.isAbs(elf_file))
                     {
                         elf_file.addRelaDynAssumeCapacity(.{
@@ -725,6 +730,7 @@ pub const GotSection = struct {
     }
 
     pub fn numRela(got: GotSection, elf_file: *Elf) usize {
+        const comp = elf_file.base.comp;
         const is_dyn_lib = elf_file.isDynLib();
         var num: usize = 0;
         for (got.entries.items) |entry| {
@@ -734,7 +740,7 @@ pub const GotSection = struct {
             };
             switch (entry.tag) {
                 .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or
-                    ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and
+                    ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and
                     !symbol.?.isAbs(elf_file)))
                 {
                     num += 1;
src/link/Elf/ZigObject.zig
@@ -97,7 +97,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
     symbol_ptr.esym_index = esym_index;
 
     if (elf_file.base.debug_format != .strip) {
-        self.dwarf = Dwarf.init(gpa, &elf_file.base, .dwarf32);
+        self.dwarf = Dwarf.init(&elf_file.base, .dwarf32);
     }
 }
 
src/link/MachO/dead_strip.zig
@@ -60,7 +60,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
     }
 
     // Add all symbols force-defined by the user.
-    for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
+    for (macho_file.base.force_undefined_symbols.keys()) |sym_name| {
         const global_index = macho_file.resolver.get(sym_name).?;
         const global = macho_file.globals.items[global_index];
         const sym = macho_file.getSymbol(global);
src/link/MachO/DebugSymbols.zig
@@ -198,10 +198,10 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64
 }
 
 pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
+    const comp = macho_file.base.comp;
     // TODO This linker code currently assumes there is only 1 compilation unit
     // and it corresponds to the Zig source code.
-    const options = macho_file.base.options;
-    const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
+    const zcu = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented;
 
     for (self.relocs.items) |*reloc| {
         const sym = switch (reloc.type) {
@@ -245,7 +245,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
         const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?];
         const low_pc = text_section.addr;
         const high_pc = text_section.addr + text_section.size;
-        try self.dwarf.writeDbgInfoHeader(module, low_pc, high_pc);
+        try self.dwarf.writeDbgInfoHeader(zcu, low_pc, high_pc);
         self.debug_info_header_dirty = false;
     }
 
@@ -572,6 +572,5 @@ const trace = @import("../../tracy.zig").trace;
 const Allocator = mem.Allocator;
 const Dwarf = @import("../Dwarf.zig");
 const MachO = @import("../MachO.zig");
-const Module = @import("../../Module.zig");
 const StringTable = @import("../StringTable.zig");
 const Type = @import("../../type.zig").Type;
src/link/MachO/load_commands.zig
@@ -1,6 +1,3 @@
-/// Default implicit entrypoint symbol name.
-pub const default_entry_point: []const u8 = "_main";
-
 /// Default path to dyld.
 pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
 
@@ -17,7 +14,9 @@ const CalcLCsSizeCtx = struct {
     wants_function_starts: bool = true,
 };
 
-fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 {
+fn calcLCsSize(m: *MachO, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 {
+    const comp = m.base.comp;
+    const gpa = comp.gpa;
     var has_text_segment: bool = false;
     var sizeofcmds: u64 = 0;
     for (ctx.segments) |seg| {
@@ -46,15 +45,15 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
         false,
     );
     // LC_MAIN
-    if (options.output_mode == .Exe) {
+    if (comp.config.output_mode == .Exe) {
         sizeofcmds += @sizeOf(macho.entry_point_command);
     }
     // LC_ID_DYLIB
-    if (options.output_mode == .Lib and options.link_mode == .Dynamic) {
+    if (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic) {
         sizeofcmds += blk: {
-            const emit = options.emit.?;
-            const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
-            defer if (options.install_name == null) gpa.free(install_name);
+            const emit = m.base.emit;
+            const install_name = m.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
+            defer if (m.install_name == null) gpa.free(install_name);
             break :blk calcInstallNameLen(
                 @sizeOf(macho.dylib_command),
                 install_name,
@@ -64,7 +63,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
     }
     // LC_RPATH
     {
-        var it = RpathIterator.init(gpa, options.rpath_list);
+        var it = RpathIterator.init(gpa, m.rpath_list);
         defer it.deinit();
         while (try it.next()) |rpath| {
             sizeofcmds += calcInstallNameLen(
@@ -78,7 +77,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
     sizeofcmds += @sizeOf(macho.source_version_command);
     // LC_BUILD_VERSION or LC_VERSION_MIN_ or nothing
     {
-        const platform = Platform.fromTarget(options.target);
+        const target = comp.root_mod.resolved_target.result;
+        const platform = Platform.fromTarget(target);
         if (platform.isBuildVersionCompatible()) {
             // LC_BUILD_VERSION
             sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
@@ -100,19 +100,19 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
         );
     }
     // LC_CODE_SIGNATURE
-    if (MachO.requiresCodeSignature(options)) {
+    if (m.requiresCodeSignature()) {
         sizeofcmds += @sizeOf(macho.linkedit_data_command);
     }
 
-    return @as(u32, @intCast(sizeofcmds));
+    return @intCast(sizeofcmds);
 }
 
-pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 {
-    var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0);
+pub fn calcMinHeaderPad(m: *MachO, ctx: CalcLCsSizeCtx) !u64 {
+    var padding: u32 = (try calcLCsSize(m, ctx, false)) + m.headerpad_size;
     log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
 
-    if (options.headerpad_max_install_names) {
-        const min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true);
+    if (m.headerpad_max_install_names) {
+        const min_headerpad_size: u32 = try calcLCsSize(m, ctx, true);
         log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
             min_headerpad_size + @sizeOf(macho.mach_header_64),
         });
src/link/MachO/zld.zig
@@ -499,7 +499,7 @@ pub fn linkWithZld(
         }
 
         // Write code signature padding if required
-        var codesig: ?CodeSignature = if (MachO.requiresCodeSignature(&macho_file.base.options)) blk: {
+        var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: {
             // Preallocate space for the code signature.
             // We need to do this at this stage so that we have the load commands with proper values
             // written out to the file.
@@ -547,12 +547,12 @@ pub fn linkWithZld(
                 });
             },
             .Lib => if (link_mode == .Dynamic) {
-                try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer);
+                try load_commands.writeDylibIdLC(macho_file, lc_writer);
             },
             else => {},
         }
 
-        try load_commands.writeRpathLCs(gpa, &macho_file.base.options, lc_writer);
+        try load_commands.writeRpathLCs(macho_file, lc_writer);
         try lc_writer.writeStruct(macho.source_version_command{
             .version = 0,
         });
@@ -1072,11 +1072,10 @@ fn calcSectionSizes(macho_file: *MachO) !void {
 }
 
 fn allocateSegments(macho_file: *MachO) !void {
-    const gpa = macho_file.base.allocator;
     for (macho_file.segments.items, 0..) |*segment, segment_index| {
         const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
         const base_size = if (is_text_segment)
-            try load_commands.calcMinHeaderPad(gpa, &macho_file.base.options, .{
+            try load_commands.calcMinHeaderPad(macho_file, .{
                 .segments = macho_file.segments.items,
                 .dylibs = macho_file.dylibs.items,
                 .referenced_dylibs = macho_file.referenced_dylibs.keys(),
src/link/Dwarf.zig
@@ -1022,16 +1022,18 @@ const min_nop_size = 2;
 /// actual_capacity + (actual_capacity / ideal_factor)
 const ideal_factor = 3;
 
-pub fn init(allocator: Allocator, bin_file: *File, format: Format) Dwarf {
-    const target = &bin_file.options.target;
+pub fn init(lf: *File, format: Format) Dwarf {
+    const comp = lf.comp;
+    const gpa = comp.gpa;
+    const target = comp.root_mod.resolved_target.result;
     const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
         0...32 => .p32,
         33...64 => .p64,
         else => unreachable,
     };
     return .{
-        .allocator = allocator,
-        .bin_file = bin_file,
+        .allocator = gpa,
+        .bin_file = lf,
         .format = format,
         .ptr_width = ptr_width,
         .dbg_line_header = switch (target.cpu.arch) {
src/link/Elf.zig
@@ -1,5 +1,6 @@
 base: link.File,
 image_base: u64,
+rdynamic: bool,
 
 ptr_width: PtrWidth,
 
@@ -358,19 +359,21 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
             .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
             .function_sections = options.function_sections,
             .data_sections = options.data_sections,
-
-            .image_base = b: {
-                if (is_dyn_lib) break :b 0;
-                if (output_mode == .Exe and comp.config.pie) return 0;
-                return options.image_base orelse switch (ptr_width) {
-                    .p32 => 0x1000,
-                    .p64 => 0x1000000,
-                };
-            },
         },
         .ptr_width = ptr_width,
         .page_size = page_size,
         .default_sym_version = default_sym_version,
+
+        .image_base = b: {
+            if (is_dyn_lib) break :b 0;
+            if (output_mode == .Exe and comp.config.pie) break :b 0;
+            break :b options.image_base orelse switch (ptr_width) {
+                .p32 => 0x1000,
+                .p64 => 0x1000000,
+            };
+        },
+
+        .rdynamic = options.rdynamic,
     };
     if (use_llvm and comp.config.have_zcu) {
         self.llvm_object = try LlvmObject.create(arena, options);
@@ -1006,7 +1009,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
             break :blk path;
         }
     } else null;
-    const gc_sections = self.base.gc_sections;
 
     // --verbose-link
     if (self.base.comp.verbose_link) try self.dumpArgv(comp);
@@ -1047,14 +1049,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     var rpath_table = std.StringArrayHashMap(void).init(gpa);
     defer rpath_table.deinit();
 
-    for (self.base.options.rpath_list) |rpath| {
+    for (self.base.rpath_list) |rpath| {
         _ = try rpath_table.put(rpath, {});
     }
 
-    if (self.base.options.each_lib_rpath) {
+    if (self.each_lib_rpath) {
         var test_path = std.ArrayList(u8).init(gpa);
         defer test_path.deinit();
-        for (self.base.options.lib_dirs) |lib_dir_path| {
+        for (self.lib_dirs) |lib_dir_path| {
             for (self.base.comp.system_libs.keys()) |link_lib| {
                 if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic)))
                     continue;
@@ -1076,9 +1078,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     }
 
     // libc
-    if (!self.base.options.skip_linker_dependencies and
-        !self.base.options.link_libc)
-    {
+    if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
         if (comp.libc_static_lib) |lib| {
             try positionals.append(.{ .path = lib.full_object_path });
         }
@@ -1263,10 +1263,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         self.entry_index = if (entry) |name| self.globalByName(name) else null;
     }
 
-    if (gc_sections) {
+    if (self.base.gc_sections) {
         try gc.gcAtoms(self);
 
-        if (self.base.options.print_gc_sections) {
+        if (self.base.print_gc_sections) {
             try gc.dumpPrunedAtoms(self);
         }
     }
@@ -1347,13 +1347,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
 }
 
 pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
-    const gpa = self.base.comp.gpa;
+    const gpa = comp.gpa;
 
     var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
     defer positionals.deinit();
 
-    try positionals.ensureUnusedCapacity(self.base.options.objects.len);
-    positionals.appendSliceAssumeCapacity(self.base.options.objects);
+    try positionals.ensureUnusedCapacity(comp.objects.len);
+    positionals.appendSliceAssumeCapacity(comp.objects);
 
     // This is a set of object files emitted by clang in a single `build-exe` invocation.
     // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
@@ -1495,8 +1495,8 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)
 
     var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
     defer positionals.deinit();
-    try positionals.ensureUnusedCapacity(self.base.options.objects.len);
-    positionals.appendSliceAssumeCapacity(self.base.options.objects);
+    try positionals.ensureUnusedCapacity(comp.objects.len);
+    positionals.appendSliceAssumeCapacity(comp.objects);
 
     // This is a set of object files emitted by clang in a single `build-exe` invocation.
     // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
@@ -1606,7 +1606,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
     try argv.append(full_out_path);
 
     if (self.base.isRelocatable()) {
-        for (self.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             try argv.append(obj.path);
         }
 
@@ -1626,28 +1626,28 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
         }
 
         if (self.base.isDynLib()) {
-            if (self.base.options.soname) |name| {
+            if (self.soname) |name| {
                 try argv.append("-soname");
                 try argv.append(name);
             }
         }
 
-        if (self.base.options.entry) |entry| {
+        if (comp.config.entry) |entry| {
             try argv.append("--entry");
             try argv.append(entry);
         }
 
-        for (self.base.options.rpath_list) |rpath| {
+        for (self.base.rpath_list) |rpath| {
             try argv.append("-rpath");
             try argv.append(rpath);
         }
 
-        if (self.base.options.each_lib_rpath) {
-            for (self.base.options.lib_dirs) |lib_dir_path| {
+        if (self.each_lib_rpath) {
+            for (self.lib_dirs) |lib_dir_path| {
                 try argv.append("-rpath");
                 try argv.append(lib_dir_path);
             }
-            for (self.base.options.objects) |obj| {
+            for (comp.objects) |obj| {
                 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
                     const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
                     if (obj.loption) continue;
@@ -1669,29 +1669,29 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
             try argv.append("--gc-sections");
         }
 
-        if (self.base.options.print_gc_sections) {
+        if (self.base.print_gc_sections) {
             try argv.append("--print-gc-sections");
         }
 
-        if (self.base.options.eh_frame_hdr) {
+        if (self.eh_frame_hdr) {
             try argv.append("--eh-frame-hdr");
         }
 
-        if (self.base.options.rdynamic) {
+        if (self.rdynamic) {
             try argv.append("--export-dynamic");
         }
 
-        if (self.base.options.z_notext) {
+        if (self.z_notext) {
             try argv.append("-z");
             try argv.append("notext");
         }
 
-        if (self.base.options.z_nocopyreloc) {
+        if (self.z_nocopyreloc) {
             try argv.append("-z");
             try argv.append("nocopyreloc");
         }
 
-        if (self.base.options.z_now) {
+        if (self.z_now) {
             try argv.append("-z");
             try argv.append("now");
         }
@@ -1702,11 +1702,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
             try argv.append("-shared");
         }
 
-        if (self.base.options.pie and self.base.isExe()) {
+        if (comp.config.pie and self.base.isExe()) {
             try argv.append("-pie");
         }
 
-        if (self.base.options.strip) {
+        if (self.base.debug_format == .strip) {
             try argv.append("-s");
         }
 
@@ -1715,12 +1715,12 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
         if (csu.crti) |v| try argv.append(v);
         if (csu.crtbegin) |v| try argv.append(v);
 
-        for (self.base.options.lib_dirs) |lib_dir| {
+        for (self.lib_dirs) |lib_dir| {
             try argv.append("-L");
             try argv.append(lib_dir);
         }
 
-        if (self.base.options.link_libc) {
+        if (comp.config.link_libc) {
             if (self.base.comp.libc_installation) |libc_installation| {
                 try argv.append("-L");
                 try argv.append(libc_installation.crt_dir.?);
@@ -1728,7 +1728,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
         }
 
         var whole_archive = false;
-        for (self.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             if (obj.must_link and !whole_archive) {
                 try argv.append("-whole-archive");
                 whole_archive = true;
@@ -1756,15 +1756,12 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
             try argv.append(p);
         }
 
-        // TSAN
-        if (self.base.options.tsan) {
+        if (comp.config.any_sanitize_thread) {
             try argv.append(comp.tsan_static_lib.?.full_object_path);
         }
 
         // libc
-        if (!self.base.options.skip_linker_dependencies and
-            !self.base.options.link_libc)
-        {
+        if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
             if (comp.libc_static_lib) |lib| {
                 try argv.append(lib.full_object_path);
             }
@@ -1799,18 +1796,18 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
         }
 
         // libc++ dep
-        if (self.base.options.link_libcpp) {
+        if (comp.config.link_libcpp) {
             try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
             try argv.append(comp.libcxx_static_lib.?.full_object_path);
         }
 
         // libunwind dep
-        if (self.base.options.link_libunwind) {
+        if (comp.config.link_libunwind) {
             try argv.append(comp.libunwind_static_lib.?.full_object_path);
         }
 
         // libc dep
-        if (self.base.options.link_libc) {
+        if (comp.config.link_libc) {
             if (self.base.comp.libc_installation != null) {
                 const needs_grouping = link_mode == .Static;
                 if (needs_grouping) try argv.append("--start-group");
@@ -1963,8 +1960,6 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
     defer script.deinit(gpa);
     try script.parse(data, self);
 
-    const lib_dirs = self.base.options.lib_dirs;
-
     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
     defer arena_allocator.deinit();
     const arena = arena_allocator.allocator();
@@ -1981,7 +1976,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
 
                 // TODO I think technically we should re-use the mechanism used by the frontend here.
                 // Maybe we should hoist search-strategy all the way here?
-                for (lib_dirs) |lib_dir| {
+                for (self.lib_dirs) |lib_dir| {
                     if (!self.isStatic()) {
                         if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Dynamic))
                             break :success;
@@ -1998,7 +1993,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
                 } else |_| {}
 
                 try checked_paths.append(try gpa.dupe(u8, scr_obj.path));
-                for (lib_dirs) |lib_dir| {
+                for (self.lib_dirs) |lib_dir| {
                     if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, scr_obj.path, null))
                         break :success;
                 }
@@ -2338,7 +2333,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
     const link_mode = self.base.comp.config.link_mode;
     const is_dyn_lib = link_mode == .Dynamic and is_lib;
     const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
-    const have_dynamic_linker = self.base.options.link_libc and
+    const have_dynamic_linker = comp.config.link_libc and
         link_mode == .Dynamic and is_exe_or_dyn_lib;
     const target = self.base.comp.root_mod.resolved_target.result;
     const compiler_rt_path: ?[]const u8 = blk: {
@@ -2358,11 +2353,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
     const id_symlink_basename = "lld.id";
 
     var man: Cache.Manifest = undefined;
-    defer if (!self.base.options.disable_lld_caching) man.deinit();
+    defer if (!self.base.disable_lld_caching) man.deinit();
 
     var digest: [Cache.hex_digest_len]u8 = undefined;
 
-    if (!self.base.options.disable_lld_caching) {
+    if (!self.base.disable_lld_caching) {
         man = comp.cache_parent.obtain();
 
         // We are about to obtain this lock, so here we give other processes a chance first.
@@ -2370,9 +2365,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
 
         comptime assert(Compilation.link_hash_implementation_version == 10);
 
-        try man.addOptionalFile(self.base.options.linker_script);
-        try man.addOptionalFile(self.base.options.version_script);
-        for (self.base.options.objects) |obj| {
+        try man.addOptionalFile(self.linker_script);
+        try man.addOptionalFile(self.version_script);
+        for (comp.objects) |obj| {
             _ = try man.addFile(obj.path, null);
             man.hash.add(obj.must_link);
             man.hash.add(obj.loption);
@@ -2385,34 +2380,34 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
 
         // We can skip hashing libc and libc++ components that we are in charge of building from Zig
         // installation sources because they are always a product of the compiler version + target information.
-        man.hash.addOptionalBytes(self.base.options.entry);
+        man.hash.addOptionalBytes(comp.config.entry);
         man.hash.addOptional(self.image_base);
         man.hash.add(self.base.gc_sections);
-        man.hash.addOptional(self.base.options.sort_section);
-        man.hash.add(self.base.options.eh_frame_hdr);
-        man.hash.add(self.base.options.emit_relocs);
-        man.hash.add(self.base.options.rdynamic);
-        man.hash.addListOfBytes(self.base.options.lib_dirs);
-        man.hash.addListOfBytes(self.base.options.rpath_list);
-        man.hash.add(self.base.options.each_lib_rpath);
+        man.hash.addOptional(self.sort_section);
+        man.hash.add(self.eh_frame_hdr);
+        man.hash.add(self.emit_relocs);
+        man.hash.add(self.rdynamic);
+        man.hash.addListOfBytes(self.lib_dirs);
+        man.hash.addListOfBytes(self.base.rpath_list);
+        man.hash.add(self.each_lib_rpath);
         if (output_mode == .Exe) {
             man.hash.add(self.base.stack_size);
             man.hash.add(self.base.build_id);
         }
-        man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys());
-        man.hash.add(self.base.options.skip_linker_dependencies);
-        man.hash.add(self.base.options.z_nodelete);
-        man.hash.add(self.base.options.z_notext);
-        man.hash.add(self.base.options.z_defs);
-        man.hash.add(self.base.options.z_origin);
-        man.hash.add(self.base.options.z_nocopyreloc);
-        man.hash.add(self.base.options.z_now);
-        man.hash.add(self.base.options.z_relro);
-        man.hash.add(self.base.options.z_common_page_size orelse 0);
-        man.hash.add(self.base.options.z_max_page_size orelse 0);
-        man.hash.add(self.base.options.hash_style);
+        man.hash.addListOfBytes(self.symbol_wrap_set.keys());
+        man.hash.add(comp.skip_linker_dependencies);
+        man.hash.add(self.z_nodelete);
+        man.hash.add(self.z_notext);
+        man.hash.add(self.z_defs);
+        man.hash.add(self.z_origin);
+        man.hash.add(self.z_nocopyreloc);
+        man.hash.add(self.z_now);
+        man.hash.add(self.z_relro);
+        man.hash.add(self.z_common_page_size orelse 0);
+        man.hash.add(self.z_max_page_size orelse 0);
+        man.hash.add(self.hash_style);
         // strip does not need to go into the linker hash because it is part of the hash namespace
-        if (self.base.options.link_libc) {
+        if (comp.config.link_libc) {
             man.hash.add(self.base.comp.libc_installation != null);
             if (self.base.comp.libc_installation) |libc_installation| {
                 man.hash.addBytes(libc_installation.crt_dir.?);
@@ -2421,16 +2416,16 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
                 man.hash.addOptionalBytes(target.dynamic_linker.get());
             }
         }
-        man.hash.addOptionalBytes(self.base.options.soname);
-        man.hash.addOptional(self.base.options.version);
+        man.hash.addOptionalBytes(self.soname);
+        man.hash.addOptional(comp.version);
         try link.hashAddSystemLibs(&man, self.base.comp.system_libs);
-        man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
+        man.hash.addListOfBytes(self.base.force_undefined_symbols.keys());
         man.hash.add(self.base.allow_shlib_undefined);
-        man.hash.add(self.base.options.bind_global_refs_locally);
-        man.hash.add(self.base.options.compress_debug_sections);
-        man.hash.add(self.base.options.tsan);
-        man.hash.addOptionalBytes(self.base.options.sysroot);
-        man.hash.add(self.base.options.linker_optimization);
+        man.hash.add(self.bind_global_refs_locally);
+        man.hash.add(self.compress_debug_sections);
+        man.hash.add(comp.config.any_sanitize_thread);
+        man.hash.addOptionalBytes(self.sysroot);
+        man.hash.add(self.optimization);
 
         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
         _ = try man.hit();
@@ -2466,14 +2461,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
     // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
     // before even generating the relocatable.
     if (output_mode == .Obj and
-        (self.base.options.lto or target.isBpfFreestanding()))
+        (comp.config.lto or target.isBpfFreestanding()))
     {
         // In this case we must do a simple file copy
         // here. TODO: think carefully about how we can avoid this redundant operation when doing
         // build-obj. See also the corresponding TODO in linkAsArchive.
         const the_object_path = blk: {
-            if (self.base.options.objects.len != 0)
-                break :blk self.base.options.objects[0].path;
+            if (comp.objects.len != 0)
+                break :blk comp.objects[0].path;
 
             if (comp.c_object_table.count() != 0)
                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
@@ -2505,32 +2500,32 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
 
         try argv.append("--error-limit=0");
 
-        if (self.base.options.sysroot) |sysroot| {
+        if (self.sysroot) |sysroot| {
             try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
         }
 
-        if (self.base.options.lto) {
-            switch (self.base.options.optimize_mode) {
+        if (comp.config.lto) {
+            switch (comp.root_mod.optimize_mode) {
                 .Debug => {},
                 .ReleaseSmall => try argv.append("--lto-O2"),
                 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
             }
         }
         try argv.append(try std.fmt.allocPrint(arena, "-O{d}", .{
-            self.base.options.linker_optimization,
+            self.optimization,
         }));
 
-        if (self.base.options.entry) |entry| {
+        if (comp.config.entry) |entry| {
             try argv.append("--entry");
             try argv.append(entry);
         }
 
-        for (self.base.options.force_undefined_symbols.keys()) |sym| {
+        for (self.base.force_undefined_symbols.keys()) |sym| {
             try argv.append("-u");
             try argv.append(sym);
         }
 
-        switch (self.base.options.hash_style) {
+        switch (self.hash_style) {
             .gnu => try argv.append("--hash-style=gnu"),
             .sysv => try argv.append("--hash-style=sysv"),
             .both => {}, // this is the default
@@ -2559,12 +2554,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
 
         try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
 
-        if (self.base.options.linker_script) |linker_script| {
+        if (self.linker_script) |linker_script| {
             try argv.append("-T");
             try argv.append(linker_script);
         }
 
-        if (self.base.options.sort_section) |how| {
+        if (self.sort_section) |how| {
             const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
             try argv.append(arg);
         }
@@ -2573,67 +2568,67 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             try argv.append("--gc-sections");
         }
 
-        if (self.base.options.print_gc_sections) {
+        if (self.base.print_gc_sections) {
             try argv.append("--print-gc-sections");
         }
 
-        if (self.base.options.print_icf_sections) {
+        if (self.print_icf_sections) {
             try argv.append("--print-icf-sections");
         }
 
-        if (self.base.options.print_map) {
+        if (self.print_map) {
             try argv.append("--print-map");
         }
 
-        if (self.base.options.eh_frame_hdr) {
+        if (self.eh_frame_hdr) {
             try argv.append("--eh-frame-hdr");
         }
 
-        if (self.base.options.emit_relocs) {
+        if (self.emit_relocs) {
             try argv.append("--emit-relocs");
         }
 
-        if (self.base.options.rdynamic) {
+        if (self.rdynamic) {
             try argv.append("--export-dynamic");
         }
 
-        if (self.base.options.strip) {
+        if (self.base.debug_format == .strip) {
             try argv.append("-s");
         }
 
-        if (self.base.options.z_nodelete) {
+        if (self.z_nodelete) {
             try argv.append("-z");
             try argv.append("nodelete");
         }
-        if (self.base.options.z_notext) {
+        if (self.z_notext) {
             try argv.append("-z");
             try argv.append("notext");
         }
-        if (self.base.options.z_defs) {
+        if (self.z_defs) {
             try argv.append("-z");
             try argv.append("defs");
         }
-        if (self.base.options.z_origin) {
+        if (self.z_origin) {
             try argv.append("-z");
             try argv.append("origin");
         }
-        if (self.base.options.z_nocopyreloc) {
+        if (self.z_nocopyreloc) {
             try argv.append("-z");
             try argv.append("nocopyreloc");
         }
-        if (self.base.options.z_now) {
+        if (self.z_now) {
             // LLD defaults to -zlazy
             try argv.append("-znow");
         }
-        if (!self.base.options.z_relro) {
+        if (!self.z_relro) {
             // LLD defaults to -zrelro
             try argv.append("-znorelro");
         }
-        if (self.base.options.z_common_page_size) |size| {
+        if (self.z_common_page_size) |size| {
             try argv.append("-z");
             try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
         }
-        if (self.base.options.z_max_page_size) |size| {
+        if (self.z_max_page_size) |size| {
             try argv.append("-z");
             try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
         }
@@ -2658,7 +2653,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             try argv.append("-shared");
         }
 
-        if (self.base.options.pie and output_mode == .Exe) {
+        if (comp.config.pie and output_mode == .Exe) {
             try argv.append("-pie");
         }
 
@@ -2683,20 +2678,20 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
         // rpaths
         var rpath_table = std.StringHashMap(void).init(gpa);
         defer rpath_table.deinit();
-        for (self.base.options.rpath_list) |rpath| {
+        for (self.base.rpath_list) |rpath| {
             if ((try rpath_table.fetchPut(rpath, {})) == null) {
                 try argv.append("-rpath");
                 try argv.append(rpath);
             }
         }
 
-        for (self.base.options.symbol_wrap_set.keys()) |symbol_name| {
+        for (self.symbol_wrap_set.keys()) |symbol_name| {
             try argv.appendSlice(&.{ "-wrap", symbol_name });
         }
 
-        if (self.base.options.each_lib_rpath) {
+        if (self.each_lib_rpath) {
             var test_path = std.ArrayList(u8).init(arena);
-            for (self.base.options.lib_dirs) |lib_dir_path| {
+            for (self.lib_dirs) |lib_dir_path| {
                 for (self.base.comp.system_libs.keys()) |link_lib| {
                     if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic)))
                         continue;
@@ -2706,7 +2701,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
                     }
                 }
             }
-            for (self.base.options.objects) |obj| {
+            for (comp.objects) |obj| {
                 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
                     const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
                     if (obj.loption) continue;
@@ -2719,12 +2714,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             }
         }
 
-        for (self.base.options.lib_dirs) |lib_dir| {
+        for (self.lib_dirs) |lib_dir| {
             try argv.append("-L");
             try argv.append(lib_dir);
         }
 
-        if (self.base.options.link_libc) {
+        if (comp.config.link_libc) {
             if (self.base.comp.libc_installation) |libc_installation| {
                 try argv.append("-L");
                 try argv.append(libc_installation.crt_dir.?);
@@ -2739,11 +2734,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
         }
 
         if (is_dyn_lib) {
-            if (self.base.options.soname) |soname| {
+            if (self.soname) |soname| {
                 try argv.append("-soname");
                 try argv.append(soname);
             }
-            if (self.base.options.version_script) |version_script| {
+            if (self.version_script) |version_script| {
                 try argv.append("-version-script");
                 try argv.append(version_script);
             }
@@ -2751,7 +2746,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
 
         // Positional arguments to the linker such as object files.
         var whole_archive = false;
-        for (self.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             if (obj.must_link and !whole_archive) {
                 try argv.append("-whole-archive");
                 whole_archive = true;
@@ -2779,15 +2774,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             try argv.append(p);
         }
 
-        // TSAN
-        if (self.base.options.tsan) {
+        if (comp.config.any_sanitize_thread) {
             try argv.append(comp.tsan_static_lib.?.full_object_path);
         }
 
         // libc
         if (is_exe_or_dyn_lib and
-            !self.base.options.skip_linker_dependencies and
-            !self.base.options.link_libc)
+            !comp.skip_linker_dependencies and
+            !comp.config.link_libc)
         {
             if (comp.libc_static_lib) |lib| {
                 try argv.append(lib.full_object_path);
@@ -2832,19 +2826,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             }
 
             // libc++ dep
-            if (self.base.options.link_libcpp) {
+            if (comp.config.link_libcpp) {
                 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
                 try argv.append(comp.libcxx_static_lib.?.full_object_path);
             }
 
             // libunwind dep
-            if (self.base.options.link_libunwind) {
+            if (comp.config.link_libunwind) {
                 try argv.append(comp.libunwind_static_lib.?.full_object_path);
             }
 
             // libc dep
             self.error_flags.missing_libc = false;
-            if (self.base.options.link_libc) {
+            if (comp.config.link_libc) {
                 if (self.base.comp.libc_installation != null) {
                     const needs_grouping = link_mode == .Static;
                     if (needs_grouping) try argv.append("--start-group");
@@ -2884,13 +2878,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             try argv.append("--allow-shlib-undefined");
         }
 
-        switch (self.base.options.compress_debug_sections) {
+        switch (self.compress_debug_sections) {
             .none => {},
             .zlib => try argv.append("--compress-debug-sections=zlib"),
             .zstd => try argv.append("--compress-debug-sections=zstd"),
         }
 
-        if (self.base.options.bind_global_refs_locally) {
+        if (self.bind_global_refs_locally) {
             try argv.append("-Bsymbolic");
         }
 
@@ -2964,7 +2958,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
         }
     }
 
-    if (!self.base.options.disable_lld_caching) {
+    if (!self.base.disable_lld_caching) {
         // Update the file with the digest. If it fails we can continue; it only
         // means that the next invocation will have an unnecessary cache miss.
         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
@@ -3101,7 +3095,8 @@ fn writeElfHeader(self: *Elf) !void {
     };
     index += 1;
 
-    const target = self.base.comp.root_mod.resolved_target.result;
+    const comp = self.base.comp;
+    const target = comp.root_mod.resolved_target.result;
     const endian = target.cpu.arch.endian();
     hdr_buf[index] = switch (endian) {
         .little => elf.ELFDATA2LSB,
@@ -3120,10 +3115,10 @@ fn writeElfHeader(self: *Elf) !void {
 
     assert(index == 16);
 
-    const output_mode = self.base.comp.config.output_mode;
-    const link_mode = self.base.comp.config.link_mode;
+    const output_mode = comp.config.output_mode;
+    const link_mode = comp.config.link_mode;
     const elf_type: elf.ET = switch (output_mode) {
-        .Exe => if (self.base.options.pie) .DYN else .EXEC,
+        .Exe => if (comp.config.pie) .DYN else .EXEC,
         .Obj => .REL,
         .Lib => switch (link_mode) {
             .Static => @as(elf.ET, .REL),
@@ -3283,7 +3278,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
     self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self);
     self.end_index = try linker_defined.addGlobal("_end", self);
 
-    if (self.base.options.eh_frame_hdr) {
+    if (self.eh_frame_hdr) {
         self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self);
     }
 
@@ -3314,7 +3309,8 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
 }
 
 fn allocateLinkerDefinedSymbols(self: *Elf) void {
-    const link_mode = self.base.comp.config.link_mode;
+    const comp = self.base.comp;
+    const link_mode = comp.config.link_mode;
 
     // _DYNAMIC
     if (self.dynamic_section_index) |shndx| {
@@ -3398,7 +3394,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
 
     // __rela_iplt_start, __rela_iplt_end
     if (self.rela_dyn_section_index) |shndx| blk: {
-        if (link_mode != .Static or self.base.options.pie) break :blk;
+        if (link_mode != .Static or comp.config.pie) break :blk;
         const shdr = &self.shdrs.items[shndx];
         const end_addr = shdr.sh_addr + shdr.sh_size;
         const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
@@ -3445,7 +3441,8 @@ fn initOutputSections(self: *Elf) !void {
 }
 
 fn initSyntheticSections(self: *Elf) !void {
-    const target = self.base.comp.root_mod.resolved_target.result;
+    const comp = self.base.comp;
+    const target = comp.root_mod.resolved_target.result;
     const ptr_size = self.ptrWidthBytes();
 
     const needs_eh_frame = for (self.objects.items) |index| {
@@ -3460,7 +3457,7 @@ fn initSyntheticSections(self: *Elf) !void {
             .offset = std.math.maxInt(u64),
         });
 
-        if (self.base.options.eh_frame_hdr) {
+        if (self.eh_frame_hdr) {
             self.eh_frame_hdr_section_index = try self.addSection(.{
                 .name = ".eh_frame_hdr",
                 .type = elf.SHT_PROGBITS,
@@ -3554,7 +3551,7 @@ fn initSyntheticSections(self: *Elf) !void {
         // In this case, if we do generate .interp section and segment, we will get
         // a segfault in the dynamic linker trying to load a binary that is static
         // and doesn't contain .dynamic section.
-        if (self.isStatic() and !self.base.options.pie) break :blk false;
+        if (self.isStatic() and !comp.config.pie) break :blk false;
         break :blk target.dynamic_linker.get() != null;
     };
     if (needs_interp) {
@@ -3567,7 +3564,7 @@ fn initSyntheticSections(self: *Elf) !void {
         });
     }
 
-    if (self.base.isDynLib() or self.shared_objects.items.len > 0 or self.base.options.pie) {
+    if (self.base.isDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) {
         self.dynstrtab_section_index = try self.addSection(.{
             .name = ".dynstr",
             .flags = elf.SHF_ALLOC,
@@ -3859,7 +3856,7 @@ fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void {
     }
 
     if (self.base.isDynLib()) {
-        if (self.base.options.soname) |soname| {
+        if (self.soname) |soname| {
             try self.dynamic.setSoname(soname, self);
         }
     }
src/link/MachO.zig
@@ -252,7 +252,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
 
         self.d_sym = .{
             .allocator = gpa,
-            .dwarf = link.File.Dwarf.init(gpa, &self.base, .dwarf32),
+            .dwarf = link.File.Dwarf.init(&self.base, .dwarf32),
             .file = d_sym_file,
         };
     }
@@ -573,7 +573,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
 
     try self.writeLinkeditSegmentData();
 
-    var codesig: ?CodeSignature = if (requiresCodeSignature(&self.base.options)) blk: {
+    var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: {
         // Preallocate space for the code signature.
         // We need to do this at this stage so that we have the load commands with proper values
         // written out to the file.
@@ -619,12 +619,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
             });
         },
         .Lib => if (self.base.comp.config.link_mode == .Dynamic) {
-            try load_commands.writeDylibIdLC(gpa, &self.base.options, lc_writer);
+            try load_commands.writeDylibIdLC(self, lc_writer);
         },
         else => {},
     }
 
-    try load_commands.writeRpathLCs(gpa, &self.base.options, lc_writer);
+    try load_commands.writeRpathLCs(self, lc_writer);
     try lc_writer.writeStruct(macho.source_version_command{
         .version = 0,
     });
@@ -713,7 +713,7 @@ pub fn resolveLibSystem(
     success: {
         if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {
             .sdk => {
-                const dir = try fs.path.join(arena, &[_][]const u8{ self.base.options.sysroot.?, "usr", "lib" });
+                const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" });
                 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success;
             },
             .vendored => {
@@ -1156,7 +1156,8 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
     // 2) afterwards, we parse dependents of the included dylibs
     // TODO this should not be performed if the user specifies `-flat_namespace` flag.
     // See ld64 manpages.
-    const gpa = self.base.comp.gpa;
+    const comp = self.base.comp;
+    const gpa = comp.gpa;
 
     while (dependent_libs.readItem()) |dep_id| {
         defer dep_id.id.deinit(gpa);
@@ -1176,7 +1177,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
         var checked_paths = std.ArrayList([]const u8).init(arena);
 
         success: {
-            if (self.base.options.sysroot) |root| {
+            if (comp.sysroot) |root| {
                 const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname });
                 if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success;
             }
@@ -1713,12 +1714,12 @@ pub fn resolveSymbols(self: *MachO) !void {
     // we search for it in libraries should there be no object files specified
     // on the linker line.
     if (output_mode == .Exe) {
-        const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
+        const entry_name = comp.config.entry.?;
         _ = try self.addUndefined(entry_name, .{});
     }
 
     // Force resolution of any symbols requested by the user.
-    for (self.base.options.force_undefined_symbols.keys()) |sym_name| {
+    for (self.base.force_undefined_symbols.keys()) |sym_name| {
         _ = try self.addUndefined(sym_name, .{});
     }
 
@@ -4367,7 +4368,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
 
     // We generate stabs last in order to ensure that the strtab always has debug info
     // strings trailing
-    if (!self.base.options.strip) {
+    if (self.base.debug_format != .strip) {
         for (self.objects.items) |object| {
             assert(self.d_sym == null); // TODO
             try self.generateSymbolStabs(object, &locals);
@@ -5171,7 +5172,8 @@ pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
 /// Returns symbol location corresponding to the set entrypoint if any.
 /// Asserts output mode is executable.
 pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {
-    const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
+    const comp = self.base.comp;
+    const entry_name = comp.config.entry orelse return null;
     const global = self.getGlobal(entry_name) orelse return null;
     return global;
 }
@@ -5189,11 +5191,13 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
     };
 }
 
-pub fn requiresCodeSignature(options: *const link.Options) bool {
-    if (options.entitlements) |_| return true;
-    const cpu_arch = options.target.cpu.arch;
-    const os_tag = options.target.os.tag;
-    const abi = options.target.abi;
+pub fn requiresCodeSignature(m: *MachO) bool {
+    if (m.entitlements) |_| return true;
+    const comp = m.base.comp;
+    const target = comp.root_mod.resolved_target.result;
+    const cpu_arch = target.cpu.arch;
+    const os_tag = target.os.tag;
+    const abi = target.abi;
     if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true;
     return false;
 }
src/link/NvPtx.zig
@@ -57,6 +57,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
+            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
             .function_sections = options.function_sections,
             .data_sections = options.data_sections,
         },
@@ -66,7 +67,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
     return nvptx;
 }
 
-pub fn open(arena: Allocator, options: link.FileOpenOptions) !*NvPtx {
+pub fn open(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
     const target = options.comp.root_mod.resolved_target.result;
     assert(target.ofmt == .nvptx);
     return createEmpty(arena, options);
src/link/Plan9.zig
@@ -295,26 +295,36 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
 }
 
 pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
-    _ = arena;
-    const target = options.comp.root_mod.resolved_target.result;
-    const gpa = options.comp.gpa;
+    const comp = options.comp;
+    const target = comp.root_mod.resolved_target.result;
+    const gpa = comp.gpa;
+    const optimize_mode = comp.root_mod.optimize_mode;
+    const output_mode = comp.config.output_mode;
 
-    const sixtyfour_bit: bool = switch (options.target.ptrBitWidth()) {
+    const sixtyfour_bit: bool = switch (target.ptrBitWidth()) {
         0...32 => false,
         33...64 => true,
         else => return error.UnsupportedP9Architecture,
     };
 
-    const arena_allocator = std.heap.ArenaAllocator.init(gpa);
-
-    const self = try gpa.create(Plan9);
+    const self = try arena.create(Plan9);
     self.* = .{
-        .path_arena = arena_allocator,
+        .path_arena = std.heap.ArenaAllocator.init(gpa),
         .base = .{
             .tag = .plan9,
-            .options = options,
-            .allocator = gpa,
+            .comp = comp,
+            .emit = options.emit,
+            .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
+            .stack_size = options.stack_size orelse 16777216,
+            .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
             .file = null,
+            .disable_lld_caching = options.disable_lld_caching,
+            .build_id = options.build_id,
+            .rpath_list = options.rpath_list,
+            .force_undefined_symbols = options.force_undefined_symbols,
+            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
+            .function_sections = options.function_sections,
+            .data_sections = options.data_sections,
         },
         .sixtyfour_bit = sixtyfour_bit,
         .bases = undefined,
@@ -602,7 +612,7 @@ pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) li
     const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
     assert(!use_lld);
 
-    switch (self.base.options.effectiveOutputMode()) {
+    switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) {
         .Exe => {},
         // plan9 object files are totally different
         .Obj => return error.TODOImplementPlan9Objs,
src/link/SpirV.zig
@@ -67,6 +67,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
             .force_undefined_symbols = options.force_undefined_symbols,
             .function_sections = options.function_sections,
             .data_sections = options.data_sections,
+            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
         },
         .object = codegen.Object.init(gpa),
     };
@@ -102,7 +103,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
     errdefer spirv.base.destroy();
 
     // TODO: read the file and keep valid parts instead of truncating
-    const file = try options.emit.?.directory.handle.createFile(options.emit.sub_path, .{
+    const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{
         .truncate = true,
         .read = true,
     });
src/link/Wasm.zig
@@ -37,6 +37,13 @@ pub const Relocation = types.Relocation;
 pub const base_tag: link.File.Tag = .wasm;
 
 base: link.File,
+import_symbols: bool,
+export_symbol_names: []const []const u8,
+rdynamic: bool,
+global_base: ?u64,
+initial_memory: ?u64,
+max_memory: ?u64,
+wasi_emulated_libs: []const wasi_libc.CRTFile,
 /// Output name of the file
 name: []const u8,
 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
@@ -368,13 +375,15 @@ pub const StringTable = struct {
 
 pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     if (build_options.only_c) unreachable;
-    const gpa = options.comp.gpa;
-    const target = options.comp.root_mod.resolved_target.result;
+    const comp = options.comp;
+    const gpa = comp.gpa;
+    const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .wasm);
 
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
-    const output_mode = options.comp.config.output_mode;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
+    const output_mode = comp.config.output_mode;
+    const shared_memory = comp.config.shared_memory;
 
     const wasm = try createEmpty(arena, options);
     errdefer wasm.base.destroy();
@@ -395,7 +404,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     };
 
     // TODO: read the file and keep valid parts instead of truncating
-    const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+    const file = try options.emit.directory.handle.createFile(sub_path, .{
         .truncate = true,
         .read = true,
         .mode = if (fs.has_executable_bit)
@@ -480,7 +489,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     }
 
     // shared-memory symbols for TLS support
-    if (wasm.base.options.shared_memory) {
+    if (shared_memory) {
         {
             const loc = try wasm.createSyntheticSymbol("__tls_base", .global);
             const symbol = loc.getSymbol(wasm);
@@ -522,14 +531,15 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
 }
 
 pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
-    const use_llvm = options.comp.config.use_llvm;
-    const output_mode = options.comp.config.output_mode;
+    const comp = options.comp;
+    const use_llvm = comp.config.use_llvm;
+    const output_mode = comp.config.output_mode;
 
     const wasm = try arena.create(Wasm);
     wasm.* = .{
         .base = .{
             .tag = .wasm,
-            .comp = options.comp,
+            .comp = comp,
             .emit = options.emit,
             .gc_sections = options.gc_sections orelse (output_mode != .Obj),
             .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB
@@ -546,6 +556,13 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
         .name = undefined,
         .import_table = options.import_table,
         .export_table = options.export_table,
+        .import_symbols = options.import_symbols,
+        .export_symbol_names = options.export_symbol_names,
+        .rdynamic = options.rdynamic,
+        .global_base = options.global_base,
+        .initial_memory = options.initial_memory,
+        .max_memory = options.max_memory,
+        .wasi_emulated_libs = options.wasi_emulated_libs,
     };
 
     if (use_llvm) {
@@ -909,7 +926,10 @@ fn writeI32Const(writer: anytype, val: u32) !void {
 }
 
 fn setupInitMemoryFunction(wasm: *Wasm) !void {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const shared_memory = comp.config.shared_memory;
+    const import_memory = comp.config.import_memory;
 
     // Passive segments are used to avoid memory being reinitialized on each
     // thread's instantiation. These passive segments are initialized and
@@ -920,7 +940,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
         return;
     }
 
-    const flag_address: u32 = if (wasm.base.options.shared_memory) address: {
+    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").?;
@@ -934,7 +954,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
     // we have 0 locals
     try leb.writeULEB128(writer, @as(u32, 0));
 
-    if (wasm.base.options.shared_memory) {
+    if (shared_memory) {
         // destination blocks
         // based on values we jump to corresponding label
         try writer.writeByte(std.wasm.opcode(.block)); // $drop
@@ -968,14 +988,14 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
     var segment_index: u32 = 0;
     while (it.next()) |entry| : (segment_index += 1) {
         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
-        if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) {
+        if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
             // For passive BSS segments we can simple issue a memory.fill(0).
             // For non-BSS segments we do a memory.init.  Both these
             // instructions take as their first argument the destination
             // address.
             try writeI32Const(writer, segment.offset);
 
-            if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
+            if (shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
                 // When we initialize the TLS segment we also set the `__tls_base`
                 // global.  This allows the runtime to use this static copy of the
                 // TLS data for the first/main thread.
@@ -1000,7 +1020,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
         }
     }
 
-    if (wasm.base.options.shared_memory) {
+    if (shared_memory) {
         // we set the init memory flag to value '2'
         try writeI32Const(writer, flag_address);
         try writeI32Const(writer, 2);
@@ -1043,12 +1063,12 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
     while (it.next()) |entry| : (segment_index += 1) {
         const name = entry.key_ptr.*;
         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
-        if (segment.needsPassiveInitialization(wasm.base.options.import_memory, name) and
+        if (segment.needsPassiveInitialization(import_memory, name) and
             !std.mem.eql(u8, name, ".bss"))
         {
             // The TLS region should not be dropped since its is needed
             // during the initialization of each thread (__wasm_init_tls).
-            if (wasm.base.options.shared_memory and std.mem.eql(u8, name, ".tdata")) {
+            if (shared_memory and std.mem.eql(u8, name, ".tdata")) {
                 continue;
             }
 
@@ -1071,11 +1091,13 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
 /// Constructs a synthetic function that performs runtime relocations for
 /// TLS symbols. This function is called by `__wasm_init_tls`.
 fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const shared_memory = comp.config.shared_memory;
 
     // When we have TLS GOT entries and shared memory is enabled,
     // we must perform runtime relocations or else we don't create the function.
-    if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) {
+    if (!shared_memory or !wasm.requiresTLSReloc()) {
         return;
     }
 
@@ -1119,7 +1141,9 @@ fn validateFeatures(
     to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
     emit_features_count: *u32,
 ) !void {
-    const target = wasm.base.comp.root_mod.resolved_target.result;
+    const comp = wasm.base.comp;
+    const target = comp.root_mod.resolved_target.result;
+    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;
@@ -1190,7 +1214,7 @@ fn validateFeatures(
         return error.InvalidFeatureSet;
     }
 
-    if (wasm.base.options.shared_memory) {
+    if (shared_memory) {
         const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
         if (@as(u1, @truncate(disallowed_feature)) != 0) {
             log.err(
@@ -1255,7 +1279,9 @@ fn validateFeatures(
 /// if one or multiple undefined references exist. When none exist, the symbol will
 /// not be created, ensuring we don't unneccesarily emit unreferenced symbols.
 fn resolveLazySymbols(wasm: *Wasm) !void {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const shared_memory = comp.config.shared_memory;
 
     if (wasm.string_table.getOffset("__heap_base")) |name_offset| {
         if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
@@ -1273,7 +1299,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
         }
     }
 
-    if (!wasm.base.options.shared_memory) {
+    if (!shared_memory) {
         if (wasm.string_table.getOffset("__tls_base")) |name_offset| {
             if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
                 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);
@@ -1306,8 +1332,9 @@ pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc {
 }
 
 fn checkUndefinedSymbols(wasm: *const Wasm) !void {
-    if (wasm.base.comp.config.output_mode == .Obj) return;
-    if (wasm.base.options.import_symbols) return;
+    const comp = wasm.base.comp;
+    if (comp.config.output_mode == .Obj) return;
+    if (wasm.import_symbols) return;
 
     var found_undefined_symbols = false;
     for (wasm.undefs.values()) |undef| {
@@ -2182,7 +2209,10 @@ const Kind = union(enum) {
 
 /// Parses an Atom and inserts its metadata into the corresponding sections.
 fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const shared_memory = comp.config.shared_memory;
+    const import_memory = comp.config.import_memory;
     const atom = wasm.getAtomPtr(atom_index);
     const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
     const do_garbage_collect = wasm.base.gc_sections;
@@ -2233,7 +2263,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
             // we set the entire region of it to zeroes.
             // We do not have to do this when exporting the memory (the default) because the runtime
             // will do it for us, and we do not emit the bss segment at all.
-            if ((wasm.base.comp.config.output_mode == .Obj or wasm.base.options.import_memory) and kind.data == .uninitialized) {
+            if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) {
                 @memset(atom.code.items, 0);
             }
 
@@ -2250,7 +2280,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
             } else {
                 const index: u32 = @intCast(wasm.segments.items.len);
                 var flags: u32 = 0;
-                if (wasm.base.options.shared_memory) {
+                if (shared_memory) {
                     flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
                 }
                 try wasm.segments.append(gpa, .{
@@ -2679,9 +2709,11 @@ fn setupStartSection(wasm: *Wasm) !void {
 }
 
 fn initializeTLSFunction(wasm: *Wasm) !void {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const shared_memory = comp.config.shared_memory;
 
-    if (!wasm.base.options.shared_memory) return;
+    if (!shared_memory) return;
 
     var function_body = std.ArrayList(u8).init(gpa);
     defer function_body.deinit();
@@ -2940,7 +2972,7 @@ fn setupExports(wasm: *Wasm) !void {
     if (wasm.base.comp.config.output_mode == .Obj) return;
     log.debug("Building exports from symbols", .{});
 
-    const force_exp_names = wasm.base.options.export_symbol_names;
+    const force_exp_names = wasm.export_symbol_names;
     if (force_exp_names.len > 0) {
         var failed_exports = false;
 
@@ -2962,7 +2994,7 @@ fn setupExports(wasm: *Wasm) !void {
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
         const symbol = sym_loc.getSymbol(wasm);
-        if (!symbol.isExported(wasm.base.options.rdynamic)) continue;
+        if (!symbol.isExported(wasm.rdynamic)) continue;
 
         const sym_name = sym_loc.getName(wasm);
         const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: {
@@ -2997,8 +3029,9 @@ fn setupExports(wasm: *Wasm) !void {
 }
 
 fn setupStart(wasm: *Wasm) !void {
+    const comp = wasm.base.comp;
     // do not export entry point if user set none or no default was set.
-    const entry_name = wasm.base.options.entry orelse return;
+    const entry_name = comp.config.entry orelse return;
 
     const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
         log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
@@ -3012,13 +3045,15 @@ fn setupStart(wasm: *Wasm) !void {
     }
 
     // Ensure the symbol is exported so host environment can access it
-    if (wasm.base.comp.config.output_mode != .Obj) {
+    if (comp.config.output_mode != .Obj) {
         symbol.setFlag(.WASM_SYM_EXPORTED);
     }
 }
 
 /// Sets up the memory section of the wasm module, as well as the stack.
 fn setupMemory(wasm: *Wasm) !void {
+    const comp = wasm.base.comp;
+    const shared_memory = comp.config.shared_memory;
     log.debug("Setting up memory layout", .{});
     const page_size = std.wasm.page_size; // 64kb
     const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
@@ -3027,12 +3062,12 @@ fn setupMemory(wasm: *Wasm) !void {
     // Always place the stack at the start by default
     // unless the user specified the global-base flag
     var place_stack_first = true;
-    var memory_ptr: u64 = if (wasm.base.options.global_base) |base| blk: {
+    var memory_ptr: u64 = if (wasm.global_base) |base| blk: {
         place_stack_first = false;
         break :blk base;
     } else 0;
 
-    const is_obj = wasm.base.comp.config.output_mode == .Obj;
+    const is_obj = comp.config.output_mode == .Obj;
 
     if (place_stack_first and !is_obj) {
         memory_ptr = stack_alignment.forward(memory_ptr);
@@ -3059,7 +3094,7 @@ fn setupMemory(wasm: *Wasm) !void {
             }
             if (wasm.findGlobalSymbol("__tls_base")) |loc| {
                 const sym = loc.getSymbol(wasm);
-                wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory)
+                wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory)
                     @as(i32, 0)
                 else
                     @as(i32, @intCast(memory_ptr));
@@ -3072,7 +3107,7 @@ fn setupMemory(wasm: *Wasm) !void {
     }
 
     // create the memory init flag which is used by the init memory function
-    if (wasm.base.options.shared_memory and wasm.hasPassiveInitializationSegments()) {
+    if (shared_memory and wasm.hasPassiveInitializationSegments()) {
         // align to pointer size
         memory_ptr = mem.alignForward(u64, memory_ptr, 4);
         const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
@@ -3098,7 +3133,7 @@ fn setupMemory(wasm: *Wasm) !void {
     // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1
     const max_memory_allowed: u64 = (1 << 32) - 1;
 
-    if (wasm.base.options.initial_memory) |initial_memory| {
+    if (wasm.initial_memory) |initial_memory| {
         if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
             log.err("Initial memory must be {d}-byte aligned", .{page_size});
             return error.MissAlignment;
@@ -3124,7 +3159,7 @@ fn setupMemory(wasm: *Wasm) !void {
         symbol.virtual_address = @as(u32, @intCast(memory_ptr));
     }
 
-    if (wasm.base.options.max_memory) |max_memory| {
+    if (wasm.max_memory) |max_memory| {
         if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
             log.err("Maximum memory must be {d}-byte aligned", .{page_size});
             return error.MissAlignment;
@@ -3139,7 +3174,7 @@ fn setupMemory(wasm: *Wasm) !void {
         }
         wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
         wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
-        if (wasm.base.options.shared_memory) {
+        if (shared_memory) {
             wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);
         }
         log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
@@ -3150,20 +3185,22 @@ fn setupMemory(wasm: *Wasm) !void {
 /// 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, object_index: u16, symbol_index: u32) !u32 {
-    const gpa = wasm.base.comp.gpa;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
     const object: Object = wasm.objects.items[object_index];
     const symbol = object.symtable[symbol_index];
     const index: u32 = @intCast(wasm.segments.items.len);
+    const shared_memory = comp.config.shared_memory;
 
     switch (symbol.tag) {
         .data => {
             const segment_info = object.segment_info[symbol.index];
-            const merge_segment = wasm.base.comp.config.output_mode != .Obj;
+            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) {
                 result.value_ptr.* = index;
                 var flags: u32 = 0;
-                if (wasm.base.options.shared_memory) {
+                if (shared_memory) {
                     flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
                 }
                 try wasm.segments.append(gpa, .{
@@ -3445,6 +3482,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
     defer tracy.end();
 
     const gpa = wasm.base.comp.gpa;
+    const shared_memory = comp.config.shared_memory;
+    const import_memory = comp.config.import_memory;
 
     // Used for all temporary memory allocated during flushin
     var arena_instance = std.heap.ArenaAllocator.init(gpa);
@@ -3509,8 +3548,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
         man.hash.addOptionalBytes(wasm.base.comp.config.entry);
         man.hash.add(wasm.base.stack_size);
         man.hash.add(wasm.base.build_id);
-        man.hash.add(wasm.base.comp.config.import_memory);
-        man.hash.add(wasm.base.comp.config.shared_memory);
+        man.hash.add(import_memory);
+        man.hash.add(shared_memory);
         man.hash.add(wasm.import_table);
         man.hash.add(wasm.export_table);
         man.hash.addOptional(wasm.initial_memory);
@@ -3726,8 +3765,12 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
                 } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
                     // for safe build modes, we store the atom in the data segment,
                     // whereas for unsafe build modes we store it in bss.
-                    const is_initialized = wasm.base.options.optimize_mode == .Debug or
-                        wasm.base.options.optimize_mode == .ReleaseSafe;
+                    const decl_namespace = mod.namespacePtr(decl.src_namespace);
+                    const optimize_mode = decl_namespace.file_scope.mod.optimize_mode;
+                    const is_initialized = switch (optimize_mode) {
+                        .Debug, .ReleaseSafe => true,
+                        .ReleaseFast, .ReleaseSmall => false,
+                    };
                     try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized });
                 } else {
                     // when the decl is all zeroes, we store the atom in the bss segment,
@@ -3788,9 +3831,13 @@ fn writeToFile(
     feature_count: u32,
     arena: Allocator,
 ) !void {
-    const gpa = wasm.base.comp.gpa;
-    const use_llvm = wasm.base.comp.config.use_llvm;
-    const use_lld = build_options.have_llvm and wasm.base.comp.config.use_lld;
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    const use_llvm = comp.config.use_llvm;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const shared_memory = comp.config.shared_memory;
+    const import_memory = comp.config.import_memory;
+    const export_memory = comp.config.export_memory;
 
     // Size of each section header
     const header_size = 5 + 1;
@@ -3800,7 +3847,7 @@ fn writeToFile(
     var code_section_index: ?u32 = null;
     // Index of the data section. Used to tell relocation table where the section lives.
     var data_section_index: ?u32 = null;
-    const is_obj = wasm.base.comp.config.output_mode == .Obj or (!use_llvm and use_lld);
+    const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld);
 
     var binary_bytes = std.ArrayList(u8).init(gpa);
     defer binary_bytes.deinit();
@@ -3840,8 +3887,6 @@ fn writeToFile(
     }
 
     // Import section
-    const import_memory = wasm.base.options.import_memory or is_obj;
-    const export_memory = wasm.base.options.export_memory;
     if (wasm.imports.count() != 0 or import_memory) {
         const header_offset = try reserveVecSectionHeader(&binary_bytes);
 
@@ -4018,7 +4063,7 @@ fn writeToFile(
 
     // When the shared-memory option is enabled, we *must* emit the 'data count' section.
     const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
-    if (data_segments_count != 0 and wasm.base.options.shared_memory) {
+    if (data_segments_count != 0 and shared_memory) {
         const header_offset = try reserveVecSectionHeader(&binary_bytes);
         try writeVecSectionHeader(
             binary_bytes.items,
@@ -4160,11 +4205,11 @@ fn writeToFile(
         if (data_section_index) |data_index| {
             try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
         }
-    } else if (!wasm.base.options.strip) {
+    } else if (wasm.base.debug_format != .strip) {
         try wasm.emitNameSection(&binary_bytes, arena);
     }
 
-    if (!wasm.base.options.strip) {
+    if (wasm.base.debug_format != .strip) {
         // The build id must be computed on the main sections only,
         // so we have to do it now, before the debug sections.
         switch (wasm.base.build_id) {
@@ -4193,7 +4238,7 @@ fn writeToFile(
         }
 
         // if (wasm.dwarf) |*dwarf| {
-        //     const mod = wasm.base.comp.module.?;
+        //     const mod = comp.module.?;
         //     try dwarf.writeDbgAbbrev();
         //     // for debug info and ranges, the address is always 0,
         //     // as locations are always offsets relative to 'code' section.
@@ -4380,6 +4425,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con
 }
 
 fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
+    const comp = wasm.base.comp;
+    const import_memory = comp.config.import_memory;
     const Name = struct {
         index: u32,
         name: []const u8,
@@ -4418,7 +4465,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
     for (wasm.data_segments.keys()) |key| {
         // bss section is not emitted when this condition holds true, so we also
         // do not output a name for it.
-        if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;
+        if (!import_memory and std.mem.eql(u8, key, ".bss")) continue;
         segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
         data_segment_index += 1;
     }
@@ -4528,6 +4575,11 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
     const tracy = trace(@src());
     defer tracy.end();
 
+    const shared_memory = comp.config.shared_memory;
+    const export_memory = comp.config.export_memory;
+    const import_memory = comp.config.import_memory;
+    const target = comp.root_mod.resolved_target.result;
+
     const gpa = wasm.base.comp.gpa;
     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
     defer arena_allocator.deinit();
@@ -4560,8 +4612,6 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         break :blk null;
     };
 
-    const target = wasm.base.comp.root_mod.resolved_target.result;
-
     const id_symlink_basename = "lld.id";
 
     var man: Cache.Manifest = undefined;
@@ -4577,7 +4627,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
 
         comptime assert(Compilation.link_hash_implementation_version == 10);
 
-        for (wasm.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             _ = try man.addFile(obj.path, null);
             man.hash.add(obj.must_link);
         }
@@ -4589,17 +4639,17 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         man.hash.addOptionalBytes(wasm.base.comp.config.entry);
         man.hash.add(wasm.base.stack_size);
         man.hash.add(wasm.base.build_id);
-        man.hash.add(wasm.base.options.import_memory);
-        man.hash.add(wasm.base.options.export_memory);
+        man.hash.add(import_memory);
+        man.hash.add(export_memory);
         man.hash.add(wasm.import_table);
         man.hash.add(wasm.export_table);
-        man.hash.addOptional(wasm.base.options.initial_memory);
-        man.hash.addOptional(wasm.base.options.max_memory);
-        man.hash.add(wasm.base.options.shared_memory);
-        man.hash.addOptional(wasm.base.options.global_base);
-        man.hash.add(wasm.base.options.export_symbol_names.len);
+        man.hash.addOptional(wasm.initial_memory);
+        man.hash.addOptional(wasm.max_memory);
+        man.hash.add(shared_memory);
+        man.hash.addOptional(wasm.global_base);
+        man.hash.add(wasm.export_symbol_names.len);
         // strip does not need to go into the linker hash because it is part of the hash namespace
-        for (wasm.base.options.export_symbol_names) |symbol_name| {
+        for (wasm.export_symbol_names) |symbol_name| {
             man.hash.addBytes(symbol_name);
         }
 
@@ -4637,8 +4687,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         // here. TODO: think carefully about how we can avoid this redundant operation when doing
         // build-obj. See also the corresponding TODO in linkAsArchive.
         const the_object_path = blk: {
-            if (wasm.base.options.objects.len != 0)
-                break :blk wasm.base.options.objects[0].path;
+            if (comp.objects.len != 0)
+                break :blk comp.objects[0].path;
 
             if (comp.c_object_table.count() != 0)
                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
@@ -4666,19 +4716,19 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
         try argv.append("--error-limit=0");
 
-        if (wasm.base.options.lto) {
-            switch (wasm.base.options.optimize_mode) {
+        if (comp.config.lto) {
+            switch (comp.root_mod.optimize_mode) {
                 .Debug => {},
                 .ReleaseSmall => try argv.append("-O2"),
                 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
             }
         }
 
-        if (wasm.base.options.import_memory) {
+        if (import_memory) {
             try argv.append("--import-memory");
         }
 
-        if (wasm.base.options.export_memory) {
+        if (export_memory) {
             try argv.append("--export-memory");
         }
 
@@ -4698,25 +4748,25 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
             try argv.append("--no-gc-sections");
         }
 
-        if (wasm.base.options.strip) {
+        if (wasm.base.debug_format == .strip) {
             try argv.append("-s");
         }
 
-        if (wasm.base.options.initial_memory) |initial_memory| {
+        if (wasm.initial_memory) |initial_memory| {
             const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory});
             try argv.append(arg);
         }
 
-        if (wasm.base.options.max_memory) |max_memory| {
+        if (wasm.max_memory) |max_memory| {
             const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory});
             try argv.append(arg);
         }
 
-        if (wasm.base.options.shared_memory) {
+        if (shared_memory) {
             try argv.append("--shared-memory");
         }
 
-        if (wasm.base.options.global_base) |global_base| {
+        if (wasm.global_base) |global_base| {
             const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base});
             try argv.append(arg);
         } else {
@@ -4728,16 +4778,16 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         }
 
         // Users are allowed to specify which symbols they want to export to the wasm host.
-        for (wasm.base.options.export_symbol_names) |symbol_name| {
+        for (wasm.export_symbol_names) |symbol_name| {
             const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
             try argv.append(arg);
         }
 
-        if (wasm.base.options.rdynamic) {
+        if (wasm.rdynamic) {
             try argv.append("--export-dynamic");
         }
 
-        if (wasm.base.options.entry) |entry| {
+        if (comp.config.entry) |entry| {
             try argv.append("--entry");
             try argv.append(entry);
         } else {
@@ -4749,14 +4799,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
             try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}),
         });
 
-        if (wasm.base.options.import_symbols) {
+        if (wasm.import_symbols) {
             try argv.append("--allow-undefined");
         }
 
         if (wasm.base.comp.config.output_mode == .Lib and wasm.base.comp.config.link_mode == .Dynamic) {
             try argv.append("--shared");
         }
-        if (wasm.base.options.pie) {
+        if (comp.config.pie) {
             try argv.append("--pie");
         }
 
@@ -4782,15 +4832,15 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
                     ));
                 }
 
-                if (wasm.base.options.link_libc) {
+                if (comp.config.link_libc) {
                     try argv.append(try comp.get_libc_crt_file(
                         arena,
-                        wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model),
+                        wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
                     ));
                     try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
                 }
 
-                if (wasm.base.options.link_libcpp) {
+                if (comp.config.link_libcpp) {
                     try argv.append(comp.libcxx_static_lib.?.full_object_path);
                     try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
                 }
@@ -4799,7 +4849,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
 
         // Positional arguments to the linker such as object files.
         var whole_archive = false;
-        for (wasm.base.options.objects) |obj| {
+        for (comp.objects) |obj| {
             if (obj.must_link and !whole_archive) {
                 try argv.append("-whole-archive");
                 whole_archive = true;
@@ -4822,8 +4872,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         }
 
         if (wasm.base.comp.config.output_mode != .Obj and
-            !wasm.base.options.skip_linker_dependencies and
-            !wasm.base.options.link_libc)
+            !comp.skip_linker_dependencies and
+            !comp.config.link_libc)
         {
             try argv.append(comp.libc_static_lib.?.full_object_path);
         }
@@ -5168,10 +5218,13 @@ fn emitDataRelocations(
 }
 
 fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
+    const comp = wasm.base.comp;
+    const import_memory = comp.config.import_memory;
+
     var it = wasm.data_segments.iterator();
     while (it.next()) |entry| {
         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
-        if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) {
+        if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
             return true;
         }
     }
@@ -5227,14 +5280,14 @@ fn markReferences(wasm: *Wasm) !void {
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
         const sym = sym_loc.getSymbol(wasm);
-        if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
+        if (sym.isExported(wasm.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
             try wasm.mark(sym_loc);
             continue;
         }
 
         // Debug sections may require to be parsed and marked when it contains
         // relocations to alive symbols.
-        if (sym.tag == .section and !wasm.base.options.strip) {
+        if (sym.tag == .section and wasm.base.debug_format != .strip) {
             const file = sym_loc.file orelse continue; // Incremental debug info is done independently
             const object = &wasm.objects.items[file];
             const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);
src/Compilation.zig
@@ -76,6 +76,7 @@ framework_dirs: []const []const u8,
 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
 version: ?std.SemanticVersion,
 libc_installation: ?*const LibCInstallation,
+skip_linker_dependencies: bool,
 
 c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
 win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) =
@@ -940,7 +941,7 @@ pub const InitOptions = struct {
     /// * getpid
     /// * mman
     /// * signal
-    wasi_emulated_libs: []const wasi_libc.CRTFile = &[0]wasi_libc.CRTFile{},
+    wasi_emulated_libs: []const wasi_libc.CRTFile = &.{},
     /// This means that if the output mode is an executable it will be a
     /// Position Independent Executable. If the output mode is not an
     /// executable this field is ignored.
@@ -1238,7 +1239,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         const sysroot = options.sysroot orelse libc_dirs.sysroot;
 
         const include_compiler_rt = options.want_compiler_rt orelse
-            (!options.skip_linker_dependencies and is_exe_or_dyn_lib);
+            (!comp.skip_linker_dependencies and is_exe_or_dyn_lib);
 
         if (include_compiler_rt and output_mode == .Obj) {
             // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
@@ -1639,6 +1640,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .objects = options.link_objects,
             .framework_dirs = options.framework_dirs,
             .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
+            .skip_linker_dependencies = options.skip_linker_dependencies,
         };
 
         if (bin_file_emit) |emit| {
@@ -1695,7 +1697,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
                 .soname = options.soname,
                 .compatibility_version = options.compatibility_version,
                 .dll_export_fns = dll_export_fns,
-                .skip_linker_dependencies = options.skip_linker_dependencies,
                 .parent_compilation_link_libc = options.parent_compilation_link_libc,
                 .each_lib_rpath = each_lib_rpath,
                 .build_id = build_id,
@@ -1765,9 +1766,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         }
     }
 
-    const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null;
+    const have_bin_emit = comp.bin_file != null or comp.whole_bin_sub_path != null;
 
-    if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and target.ofmt != .c) {
+    if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {
         if (target.isDarwin()) {
             switch (target.abi) {
                 .none,
@@ -1808,27 +1809,34 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
                 .{ .musl_crt_file = .crt1_o },
                 .{ .musl_crt_file = .scrt1_o },
                 .{ .musl_crt_file = .rcrt1_o },
-                switch (comp.bin_file.options.link_mode) {
+                switch (comp.config.link_mode) {
                     .Static => .{ .musl_crt_file = .libc_a },
                     .Dynamic => .{ .musl_crt_file = .libc_so },
                 },
             });
         }
-        if (comp.wantBuildWasiLibcFromSource()) {
-            if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
 
-            const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs;
-            try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components
-            for (wasi_emulated_libs) |crt_file| {
-                comp.work_queue.writeItemAssumeCapacity(.{
-                    .wasi_libc_crt_file = crt_file,
-                });
+        if (comp.bin_file) |lf| {
+            if (lf.cast(link.File.Wasm)) |wasm| {
+                if (comp.wantBuildWasiLibcFromSource()) {
+                    if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
+
+                    // worst-case we need all components
+                    try comp.work_queue.ensureUnusedCapacity(wasm.wasi_emulated_libs.len + 2);
+
+                    for (wasm.wasi_emulated_libs) |crt_file| {
+                        comp.work_queue.writeItemAssumeCapacity(.{
+                            .wasi_libc_crt_file = crt_file,
+                        });
+                    }
+                    comp.work_queue.writeAssumeCapacity(&[_]Job{
+                        .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) },
+                        .{ .wasi_libc_crt_file = .libc_a },
+                    });
+                }
             }
-            comp.work_queue.writeAssumeCapacity(&[_]Job{
-                .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) },
-                .{ .wasi_libc_crt_file = .libc_a },
-            });
         }
+
         if (comp.wantBuildMinGWFromSource()) {
             if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
 
@@ -1863,27 +1871,29 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         if (comp.wantBuildLibUnwindFromSource()) {
             try comp.work_queue.writeItem(.{ .libunwind = {} });
         }
-        if (build_options.have_llvm and is_exe_or_dyn_lib and comp.bin_file.options.link_libcpp) {
+        if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
             try comp.work_queue.writeItem(.libcxx);
             try comp.work_queue.writeItem(.libcxxabi);
         }
-        if (build_options.have_llvm and comp.bin_file.options.tsan) {
+        if (build_options.have_llvm and comp.config.any_sanitize_thread) {
             try comp.work_queue.writeItem(.libtsan);
         }
 
-        if (comp.getTarget().isMinGW() and comp.config.any_non_single_threaded) {
-            // LLD might drop some symbols as unused during LTO and GCing, therefore,
-            // we force mark them for resolution here.
+        if (comp.bin_file) |lf| {
+            if (comp.getTarget().isMinGW() and comp.config.any_non_single_threaded) {
+                // LLD might drop some symbols as unused during LTO and GCing, therefore,
+                // we force mark them for resolution here.
 
-            const tls_index_sym = switch (comp.getTarget().cpu.arch) {
-                .x86 => "__tls_index",
-                else => "_tls_index",
-            };
+                const tls_index_sym = switch (comp.getTarget().cpu.arch) {
+                    .x86 => "__tls_index",
+                    else => "_tls_index",
+                };
 
-            try comp.bin_file.options.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
+                try lf.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
+            }
         }
 
-        if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
+        if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
             if (is_exe_or_dyn_lib) {
                 log.debug("queuing a job to build compiler_rt_lib", .{});
                 comp.job_queued_compiler_rt_lib = true;
@@ -1895,8 +1905,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             }
         }
 
-        if (!comp.bin_file.options.skip_linker_dependencies and is_exe_or_dyn_lib and
-            !comp.bin_file.options.link_libc and capable_of_building_zig_libc)
+        if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
+            !comp.config.link_libc and capable_of_building_zig_libc)
         {
             try comp.work_queue.writeItem(.{ .zig_libc = {} });
         }
@@ -2425,7 +2435,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
         man.hash.add(comp.test_evented_io);
         man.hash.addOptionalBytes(comp.test_filter);
         man.hash.addOptionalBytes(comp.test_name_prefix);
-        man.hash.add(comp.bin_file.options.skip_linker_dependencies);
+        man.hash.add(comp.skip_linker_dependencies);
         man.hash.add(comp.formatted_panics);
         man.hash.add(mod.emit_h != null);
         man.hash.add(mod.error_limit);
@@ -2477,7 +2487,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
     man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys());
     man.hash.add(comp.bin_file.options.each_lib_rpath);
     man.hash.add(comp.bin_file.build_id);
-    man.hash.add(comp.bin_file.options.skip_linker_dependencies);
+    man.hash.add(comp.skip_linker_dependencies);
     man.hash.add(comp.bin_file.options.z_nodelete);
     man.hash.add(comp.bin_file.options.z_notext);
     man.hash.add(comp.bin_file.options.z_defs);
@@ -2489,7 +2499,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
     man.hash.add(comp.bin_file.options.z_max_page_size orelse 0);
     man.hash.add(comp.bin_file.options.hash_style);
     man.hash.add(comp.bin_file.options.compress_debug_sections);
-    man.hash.add(comp.bin_file.options.include_compiler_rt);
+    man.hash.add(comp.include_compiler_rt);
     man.hash.add(comp.bin_file.options.sort_section);
     if (comp.config.link_libc) {
         man.hash.add(comp.bin_file.options.libc_installation != null);
@@ -3836,7 +3846,7 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {
     // Also nothing that applies only to compiling .zig code.
     man.hash.add(comp.sanitize_c);
     man.hash.addListOfBytes(comp.clang_argv);
-    man.hash.add(comp.bin_file.options.link_libcpp);
+    man.hash.add(comp.config.link_libcpp);
 
     // When libc_installation is null it means that Zig generated this dir list
     // based on the zig library directory alone. The zig lib directory file
@@ -4909,7 +4919,7 @@ pub fn addCCArgs(
         try argv.append("-fno-builtin");
     }
 
-    if (comp.bin_file.options.link_libcpp) {
+    if (comp.config.link_libcpp) {
         const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
             comp.zig_lib_directory.path.?, "libcxx", "include",
         });
@@ -4954,7 +4964,7 @@ pub fn addCCArgs(
         try argv.append(libunwind_include_path);
     }
 
-    if (comp.bin_file.options.link_libc and target.isGnuLibC()) {
+    if (comp.config.link_libc and target.isGnuLibC()) {
         const target_version = target.os.version_range.linux.glibc;
         const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{
             target_version.minor,
@@ -5944,7 +5954,7 @@ fn wantBuildLibCFromSource(comp: Compilation) bool {
         .Lib => comp.bin_file.options.link_mode == .Dynamic,
         .Exe => true,
     };
-    return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
+    return comp.config.link_libc and is_exe_or_dyn_lib and
         comp.bin_file.options.libc_installation == null and
         comp.bin_file.options.target.ofmt != .c;
 }
@@ -6164,6 +6174,7 @@ fn buildOutputFromZig(
         .have_zcu = true,
         .emit_bin = true,
         .root_optimize_mode = comp.compilerRtOptMode(),
+        .link_libc = comp.config.link_libc,
     });
 
     const root_mod = Package.Module.create(.{
@@ -6224,7 +6235,6 @@ fn buildOutputFromZig(
         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
         .clang_passthrough_mode = comp.clang_passthrough_mode,
         .skip_linker_dependencies = true,
-        .link_libc = comp.bin_file.options.link_libc,
         .want_structured_cfg = comp.bin_file.options.want_structured_cfg,
     });
     defer sub_compilation.destroy();
@@ -6305,7 +6315,6 @@ pub fn build_crt_file(
         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
         .clang_passthrough_mode = comp.clang_passthrough_mode,
         .skip_linker_dependencies = true,
-        .link_libc = comp.bin_file.options.link_libc,
         .want_structured_cfg = comp.bin_file.options.want_structured_cfg,
     });
     defer sub_compilation.destroy();
@@ -6327,7 +6336,7 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
     // This can happen when the user uses `build-exe foo.obj -lkernel32` and
     // then when we create a sub-Compilation for zig libc, it also tries to
     // build kernel32.lib.
-    if (comp.bin_file.options.skip_linker_dependencies) return;
+    if (comp.skip_linker_dependencies) return;
 
     // This happens when an `extern "foo"` function is referenced.
     // If we haven't seen this library yet and we're targeting Windows, we need
src/libtsan.zig
@@ -119,7 +119,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void {
         });
     }
 
-    const to_c_or_not_to_c_sources = if (comp.bin_file.options.link_libc)
+    const to_c_or_not_to_c_sources = if (comp.config.link_libc)
         &sanitizer_libcdep_sources
     else
         &sanitizer_nolibc_sources;
src/link.zig
@@ -133,7 +133,6 @@ pub const File = struct {
         export_symbol_names: []const []const u8,
         global_base: ?u64,
         dll_export_fns: bool,
-        skip_linker_dependencies: bool,
         parent_compilation_link_libc: bool,
         each_lib_rpath: bool,
         build_id: std.zig.BuildId,
@@ -1099,7 +1098,7 @@ pub const File = struct {
     }
 
     pub fn isStatic(self: File) bool {
-        return self.base.comp.config.link_mode == .Static;
+        return self.comp.config.link_mode == .Static;
     }
 
     pub fn isObject(self: File) bool {
src/Sema.zig
@@ -5742,7 +5742,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
             const msg = try sema.errMsg(&child_block, src, "C import failed", .{});
             errdefer msg.destroy(gpa);
 
-            if (!comp.bin_file.options.link_libc)
+            if (!comp.config.link_libc)
                 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{});
 
             const gop = try mod.cimport_errors.getOrPut(gpa, sema.owner_decl_index);
@@ -9058,7 +9058,7 @@ fn resolveGenericBody(
 
 /// Given a library name, examines if the library name should end up in
 /// `link.File.Options.system_libs` table (for example, libc is always
-/// specified via dedicated flag `link.File.Options.link_libc` instead),
+/// specified via dedicated flag `link_libc` instead),
 /// and puts it there if it doesn't exist.
 /// It also dupes the library name which can then be saved as part of the
 /// respective `Decl` (either `ExternFn` or `Var`).
@@ -9076,7 +9076,7 @@ fn handleExternLibName(
         const target = mod.getTarget();
         log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
         if (target.is_libc_lib_name(lib_name)) {
-            if (!comp.bin_file.options.link_libc) {
+            if (!comp.config.link_libc) {
                 return sema.fail(
                     block,
                     src_loc,
@@ -9087,18 +9087,21 @@ fn handleExternLibName(
             break :blk;
         }
         if (target.is_libcpp_lib_name(lib_name)) {
-            if (!comp.bin_file.options.link_libcpp) {
-                return sema.fail(
-                    block,
-                    src_loc,
-                    "dependency on libc++ must be explicitly specified in the build command",
-                    .{},
-                );
-            }
+            if (!comp.config.link_libcpp) return sema.fail(
+                block,
+                src_loc,
+                "dependency on libc++ must be explicitly specified in the build command",
+                .{},
+            );
             break :blk;
         }
         if (mem.eql(u8, lib_name, "unwind")) {
-            comp.bin_file.options.link_libunwind = true;
+            if (!comp.config.link_libunwind) return sema.fail(
+                block,
+                src_loc,
+                "dependency on libunwind must be explicitly specified in the build command",
+                .{},
+            );
             break :blk;
         }
         if (!target.isWasm() and !comp.bin_file.options.pic) {