authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-27 10:23:00+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-27 10:24:44+01:00
logbc616127e87a76ef3de02d5aee6c7d6ff6c9e34f
tree10718e3aa36da9e015f343449eda1c5390c1ee9b
parentfd0baa9834293be085fd15929437f83986fb2243
parent6418840f90d71a3c991bbb207ccdde4c9dc21d17
signature Commit is signed but in an unrecognized format.

Merge branch 'macho2'

Manual merge of https://codeberg.org/ziglang/zig/pulls/36942 to resolve conflict. Add a stub `MachO2` linker implementation; move Darwin input resolution to the frontend; clean up API between codegen and linker.

26 files changed, 2369 insertions(+), 1080 deletions(-)

lib/std/macho.zig+16-8
......@@ -1145,16 +1145,16 @@ pub const LC = enum(u32) {
11451145};
11461146
11471147/// the mach magic number
1148pub const MH_MAGIC = 0xfeedface;
1148pub const MH_MAGIC: u32 = 0xfeedface;
11491149
11501150/// NXSwapInt(MH_MAGIC)
1151pub const MH_CIGAM = 0xcefaedfe;
1151pub const MH_CIGAM: u32 = 0xcefaedfe;
11521152
11531153/// the 64-bit mach magic number
1154pub const MH_MAGIC_64 = 0xfeedfacf;
1154pub const MH_MAGIC_64: u32 = 0xfeedfacf;
11551155
11561156/// NXSwapInt(MH_MAGIC_64)
1157pub const MH_CIGAM_64 = 0xcffaedfe;
1157pub const MH_CIGAM_64: u32 = 0xcffaedfe;
11581158
11591159/// relocatable object file
11601160pub const MH_OBJECT = 0x1;
......@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;
12811281// Constants for the flags field of the fat_header
12821282
12831283/// the fat magic number
1284pub const FAT_MAGIC = 0xcafebabe;
1284pub const FAT_MAGIC: u32 = 0xcafebabe;
12851285
12861286/// NXSwapLong(FAT_MAGIC)
1287pub const FAT_CIGAM = 0xbebafeca;
1287pub const FAT_CIGAM: u32 = 0xbebafeca;
12881288
12891289/// the 64-bit fat magic number
1290pub const FAT_MAGIC_64 = 0xcafebabf;
1290pub const FAT_MAGIC_64: u32 = 0xcafebabf;
12911291
12921292/// NXSwapLong(FAT_MAGIC_64)
1293pub const FAT_CIGAM_64 = 0xbfbafeca;
1293pub const FAT_CIGAM_64: u32 = 0xbfbafeca;
12941294
12951295/// Segment flags
12961296/// The file contents for this segment is for the high part of the VM space, the low part
......@@ -2212,3 +2212,11 @@ pub const CompactUnwindEncoding = packed struct(u32) {
22122212 has_lsda: u1,
22132213 start: u1,
22142214};
2215
2216// Archive files start with the ARMAG identifying string. Then follows a
2217// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
2218// member indicates, for each member file.
2219/// String that begins an archive file.
2220pub const ARMAG = "!<arch>\n";
2221/// String in ar_fmag at the end of each header.
2222pub const ARFMAG = "`\n";
lib/std/zig/LibCDirs.zig+5-25
......@@ -11,15 +11,6 @@ libc_include_dir_list: []const []const u8,
1111libc_installation: ?*const LibCInstallation,
1212libc_framework_dir_list: []const []const u8,
1313sysroot: ?[]const u8,
14darwin_sdk_layout: ?DarwinSdkLayout,
15
16/// The filesystem layout of darwin SDK elements.
17pub const DarwinSdkLayout = enum {
18 /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
19 sdk,
20 /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
21 vendored,
22};
2314
2415pub fn detect(
2516 arena: Allocator,
......@@ -37,7 +28,6 @@ pub fn detect(
3728 .libc_installation = null,
3829 .libc_framework_dir_list = &.{},
3930 .sysroot = null,
40 .darwin_sdk_layout = null,
4131 };
4232 }
4333
......@@ -102,13 +92,11 @@ pub fn detect(
10292 .libc_installation = null,
10393 .libc_framework_dir_list = &.{},
10494 .sysroot = null,
105 .darwin_sdk_layout = null,
10695 };
10796}
10897
10998fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *const LibCInstallation) !LibCDirs {
11099 var list = try std.array_list.Managed([]const u8).initCapacity(arena, 5);
111 var framework_list = std.array_list.Managed([]const u8).init(arena);
112100
113101 list.appendAssumeCapacity(lci.include_dir.?);
114102
......@@ -149,21 +137,15 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con
149137 }
150138 }
151139
152 var sysroot: ?[]const u8 = null;
153
154 if (target.os.tag.isDarwin()) d: {
155 const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
156 const down2 = std.fs.path.dirname(down1) orelse break :d;
157 try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
158 sysroot = down2;
159 }
140 const frameworks: []const []const u8 = if (target.os.tag.isDarwin()) try arena.dupe([]const u8, &.{
141 try std.fs.path.join(arena, &.{ lci.darwin_sdk_dir.?, "System", "Library", "Frameworks" }),
142 }) else &.{};
160143
161144 return .{
162145 .libc_include_dir_list = list.items,
163146 .libc_installation = lci,
164 .libc_framework_dir_list = framework_list.items,
165 .sysroot = sysroot,
166 .darwin_sdk_layout = if (sysroot == null) null else .sdk,
147 .libc_framework_dir_list = frameworks,
148 .sysroot = if (target.os.tag.isDarwin()) lci.darwin_sdk_dir.? else null,
167149 };
168150}
169151
......@@ -178,7 +160,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st
178160 .libc_installation = null,
179161 .libc_framework_dir_list = &.{},
180162 .sysroot = null,
181 .darwin_sdk_layout = .vendored,
182163 };
183164 }
184165
......@@ -230,7 +211,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st
230211 .libc_installation = null,
231212 .libc_framework_dir_list = &.{},
232213 .sysroot = null,
233 .darwin_sdk_layout = .vendored,
234214 };
235215}
236216
lib/std/zig/LibCInstallation.zig+25-28
......@@ -23,6 +23,7 @@ crt_dir: ?[]const u8 = null,
2323msvc_lib_dir: ?[]const u8 = null,
2424kernel32_lib_dir: ?[]const u8 = null,
2525cc_dir: ?[]const u8 = null,
26darwin_sdk_dir: ?[]const u8 = null,
2627
2728pub const FindError = error{
2829 OutOfMemory,
......@@ -96,6 +97,10 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
9697 log.err("sys_include_dir may not be empty", .{});
9798 return error.ParseError;
9899 }
100 if (self.crt_dir == null) {
101 log.err("crt_dir may not be empty", .{});
102 return error.ParseError;
103 }
99104
100105 const os_tag = target.os.tag;
101106
......@@ -103,12 +108,6 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
103108 log.err("cc_dir may not be empty for {s}", .{@tagName(os_tag)});
104109 return error.ParseError;
105110 }
106
107 if (self.crt_dir == null and !target.os.tag.isDarwin()) {
108 log.err("crt_dir may not be empty for {s}", .{@tagName(os_tag)});
109 return error.ParseError;
110 }
111
112111 if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) {
113112 log.err("msvc_lib_dir may not be empty for {s}-{s}", .{
114113 @tagName(os_tag),
......@@ -123,19 +122,16 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
123122 });
124123 return error.ParseError;
125124 }
125 if (self.darwin_sdk_dir == null and os_tag.isDarwin()) {
126 log.err("darwin_sdk_dir may not be empty for {t}", .{os_tag});
127 return error.ParseError;
128 }
126129
127130 return self;
128131}
129132
130133pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
131134 @setEvalBranchQuota(4000);
132 const include_dir = self.include_dir orelse "";
133 const sys_include_dir = self.sys_include_dir orelse "";
134 const crt_dir = self.crt_dir orelse "";
135 const msvc_lib_dir = self.msvc_lib_dir orelse "";
136 const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
137 const cc_dir = self.cc_dir orelse "";
138
139135 try out.print(
140136 \\# The directory that contains `stdlib.h`.
141137 \\# On POSIX, can be found with: `cc -E -Wp,-v -xc /dev/null`
......@@ -153,7 +149,6 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
153149 \\
154150 \\# The directory that contains `crt1.o` or `crt2.o`.
155151 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
156 \\# Not needed when targeting macOS.
157152 \\crt_dir={s}
158153 \\
159154 \\# The directory that contains `vcruntime.lib`.
......@@ -164,13 +159,18 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
164159 \\# Only needed when targeting MSVC on Windows.
165160 \\kernel32_lib_dir={s}
166161 \\
162 \\# The directory that contains `SDKSettings.json`
163 \\# Only needed when targeting Darwin.
164 \\darwin_sdk_dir={s}
165 \\
167166 , .{
168 include_dir,
169 sys_include_dir,
170 crt_dir,
171 msvc_lib_dir,
172 kernel32_lib_dir,
173 cc_dir,
167 self.include_dir orelse "",
168 self.sys_include_dir orelse "",
169 self.cc_dir orelse "",
170 self.crt_dir orelse "",
171 self.msvc_lib_dir orelse "",
172 self.kernel32_lib_dir orelse "",
173 self.darwin_sdk_dir orelse "",
174174 });
175175}
176176
......@@ -189,16 +189,13 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib
189189 if (is_darwin and args.target.os.tag.isDarwin()) {
190190 if (!std.zig.system.darwin.isSdkInstalled(gpa, io))
191191 return error.DarwinSdkNotFound;
192 const sdk = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse
192 self.darwin_sdk_dir = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse
193193 return error.DarwinSdkNotFound;
194 defer gpa.free(sdk);
195194
196 self.include_dir = try fs.path.join(gpa, &.{
197 sdk, "usr/include",
198 });
199 self.sys_include_dir = try fs.path.join(gpa, &.{
200 sdk, "usr/include",
201 });
195 const sdk = self.darwin_sdk_dir.?;
196 self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" });
197 self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" });
198 self.crt_dir = try fs.path.join(gpa, &.{ sdk, "usr/lib" });
202199 return self;
203200 } else if (is_windows) {
204201 const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch, args.environ_map) catch |err| switch (err) {
src/Compilation.zig+21-17
......@@ -83,7 +83,7 @@ zigc_strat: RtStrat,
8383/// Resolved into known paths, any GNU ld scripts already resolved.
8484link_inputs: []const link.Input,
8585/// Needed only for passing -F args to clang.
86framework_dirs: []const []const u8,
86framework_dirs: []const Cache.Directory,
8787/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
8888/// with Zig. Static libraries are provided as `link.Input` values.
8989windows_libs: std.array_hash_map.String(void),
......@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {
13501350 version,
13511351};
13521352
1353pub const Framework = link.File.MachO.Framework;
13541353pub const SystemLib = link.SystemLib;
13551354
13561355pub const CacheMode = enum {
......@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {
14871486 /// The ELF implementation no longer uses this data, however the MachO and COFF
14881487 /// implementations still do.
14891488 lib_directories: []const Cache.Directory = &.{},
1489 framework_directories: []const Cache.Directory = &.{},
14901490 rpath_list: []const []const u8 = &[0][]const u8{},
14911491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,
14921492 c_source_files: []const CSourceFile = &.{},
......@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {
14941494 manifest_file: ?[]const u8 = null,
14951495 rc_includes: std.zig.RcIncludes = .any,
14961496 link_inputs: []const link.Input = &.{},
1497 framework_dirs: []const []const u8 = &[0][]const u8{},
1498 frameworks: []const Framework = &.{},
14991497 windows_lib_names: []const []const u8 = &.{},
15001498 /// This means that if the output mode is an executable it will be a
15011499 /// Position Independent Executable. If the output mode is not an
......@@ -2146,7 +2144,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
21462144 .ubsan_rt_strat = ubsan_rt_strat,
21472145 .zigc_strat = zigc_strat,
21482146 .link_inputs = options.link_inputs,
2149 .framework_dirs = options.framework_dirs,
2147 .framework_dirs = options.framework_directories,
21502148 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
21512149 .skip_linker_dependencies = options.skip_linker_dependencies,
21522150 .queued_jobs = .{},
......@@ -2208,10 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
22082206 .z_relro = options.linker_z_relro,
22092207 .z_common_page_size = options.linker_z_common_page_size,
22102208 .z_max_page_size = options.linker_z_max_page_size,
2211 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
2212 .frameworks = options.frameworks,
22132209 .lib_directories = options.lib_directories,
2214 .framework_dirs = options.framework_dirs,
22152210 .rpath_list = options.rpath_list,
22162211 .symbol_wrap_set = options.symbol_wrap_set,
22172212 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),
......@@ -2429,11 +2424,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
24292424 // If linking against host libc installation, instead queue up jobs
24302425 // for loading those files in the linker.
24312426 if (comp.config.link_libc and is_exe_or_dyn_lib) {
2432 // If the "is darwin" check is moved below the libc_installation check below,
2433 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
2434 if (target.isDarwinLibC()) {
2435 // TODO delete logic from MachO flush() and queue up tasks here instead.
2436 } else if (comp.libc_installation) |lci| {
2427 if (comp.libc_installation) |lci| {
24372428 const basenames = LibCInstallation.CrtBasenames.get(.{
24382429 .target = target,
24392430 .link_libc = comp.config.link_libc,
......@@ -2456,6 +2447,18 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
24562447 }
24572448 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
24582449 comp.oneshot_prelink_tasks.appendAssumeCapacity(.load_host_libc);
2450 } else if (target.isDarwinLibC()) {
2451 try comp.oneshot_prelink_tasks.appendSlice(gpa, &.{
2452 .{ .load_tbd = .{
2453 .root_dir = comp.dirs.zig_lib,
2454 .sub_path = "libc" ++ fs.path.sep_str ++ "darwin" ++ fs.path.sep_str ++ "libSystem.tbd",
2455 } },
2456 .{ .load_darwin_sdk_settings = .{
2457 .root_dir = comp.dirs.zig_lib,
2458 .sub_path = "libc" ++ fs.path.sep_str ++ "darwin" ++ fs.path.sep_str ++ "SDKSettings.json",
2459 } },
2460 });
2461 // TODO delete logic from MachO flush() and queue up tasks here instead.
24592462 } else if (target.isMuslLibC()) {
24602463 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
24612464
......@@ -3409,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
34093412 man.hash.add(comp.zigc_strat);
34103413 man.hash.add(comp.rc_includes);
34113414 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
3412 man.hash.addListOfBytes(comp.framework_dirs);
3415 man.hash.add(comp.framework_dirs.len);
3416 for (comp.framework_dirs) |framework_dir| {
3417 man.hash.addBytes(framework_dir.path orelse ".");
3418 }
34133419 man.hash.addListOfBytes(comp.windows_libs.keys());
34143420
34153421 man.hash.addListOfBytes(comp.global_cc_argv);
......@@ -3475,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
34753481 man.hash.add(opts.growable_table);
34763482
34773483 // Mach-O specific stuff
3478 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
34793484 try man.addInputPathOptional(opts.entitlements, .{});
34803485 man.hash.addOptional(opts.pagezero_size);
34813486 man.hash.addOptional(opts.headerpad_size);
......@@ -3485,7 +3490,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
34853490 man.hash.add(opts.discard_local_symbols);
34863491 man.hash.addOptional(opts.compatibility_version);
34873492 man.hash.addOptionalBytes(opts.install_name);
3488 man.hash.addOptional(opts.darwin_sdk_layout);
34893493
34903494 // COFF specific stuff
34913495 man.hash.addOptional(opts.subsystem);
......@@ -6455,7 +6459,7 @@ fn addCommonCCArgs(
64556459
64566460 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
64576461 for (comp.framework_dirs) |framework_dir| {
6458 try argv.appendSlice(&.{ "-F", framework_dir });
6462 try argv.appendSlice(&.{ "-F", framework_dir.path orelse "." });
64596463 }
64606464 }
64616465 }
src/codegen.zig+12-74
......@@ -754,23 +754,15 @@ fn lowerUavRef(
754754 }
755755
756756 const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu);
757 _ = try lf.lowerUav(pt, uav_val, uav_align);
758757
759 const vaddr = lf.getUavVAddr(uav_val, .{
758 try lf.relocSymAddr(.{
760759 .parent = reloc_parent,
761760 .offset = w.end,
761 .target = try lf.uavSymbol(pt, uav_val, uav_align),
762762 .addend = @intCast(offset),
763 }) catch |err| switch (err) {
764 error.OutOfMemory => |e| return e,
765 else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}),
766 };
767 const endian = target.cpu.arch.endian();
768 switch (ptr_width_bytes) {
769 2 => try w.writeInt(u16, @intCast(vaddr), endian),
770 4 => try w.writeInt(u32, @intCast(vaddr), endian),
771 8 => try w.writeInt(u64, vaddr, endian),
772 else => unreachable,
773 }
763 });
764
765 try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation
774766}
775767
776768fn lowerNavRef(
......@@ -805,68 +797,14 @@ fn lowerNavRef(
805797 else => {},
806798 }
807799
808 const vaddr = lf.getNavVAddr(pt, nav_index, .{
800 try lf.relocSymAddr(.{
809801 .parent = reloc_parent,
810802 .offset = w.end,
803 .target = try lf.navSymbol(nav_index),
811804 .addend = @intCast(offset),
812 }) catch @panic("TODO rework getNavVAddr");
813 const endian = target.cpu.arch.endian();
814 switch (ptr_width_bytes) {
815 2 => try w.writeInt(u16, @intCast(vaddr), endian),
816 4 => try w.writeInt(u32, @intCast(vaddr), endian),
817 8 => try w.writeInt(u64, vaddr, endian),
818 else => unreachable,
819 }
820}
805 });
821806
822pub fn genNavRef(
823 lf: *link.File,
824 pt: Zcu.PerThread,
825 nav_index: InternPool.Nav.Index,
826) Error!link.File.SymbolId {
827 const zcu = pt.zcu;
828 const ip = &zcu.intern_pool;
829 const nav = ip.getNav(nav_index);
830 log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)});
831
832 const is_threadlocal = nav.resolved.?.@"threadlocal" and zcu.comp.config.any_non_single_threaded;
833 if (lf.cast(.elf)) |elf_file| {
834 const zo = elf_file.zigObjectPtr().?;
835 if (nav.getExtern(ip)) |e| {
836 const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
837 switch (e.linkage) {
838 .strong => {},
839 .weak => zo.symbol(sym_index).flags.weak = true,
840 }
841 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
842 return @fromBackingInt(@intCast(sym_index));
843 } else {
844 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
845 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
846 return @fromBackingInt(@intCast(sym_index));
847 }
848 } else if (lf.cast(.elf2)) |elf| {
849 return elf.navSymbol(nav_index);
850 } else if (lf.cast(.macho)) |macho_file| {
851 const zo = macho_file.getZigObject().?;
852 if (nav.getExtern(ip)) |e| {
853 const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
854 switch (e.linkage) {
855 .strong => {},
856 .weak => zo.symbols.items[sym_index].flags.weak = true,
857 }
858 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
859 return @fromBackingInt(@intCast(sym_index));
860 } else {
861 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
862 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
863 return @fromBackingInt(@intCast(sym_index));
864 }
865 } else if (lf.cast(.coff2)) |coff| {
866 return @fromBackingInt(@intCast(@backingInt(try coff.navSymbol(zcu, nav_index))));
867 } else {
868 std.debug.panic("TODO genNavRef for '{t}'", .{lf.tag});
869 }
807 try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation
870808}
871809
872810/// deprecated legacy type
......@@ -908,13 +846,13 @@ pub fn genTypedValue(
908846 .none => .none,
909847 .undef => .undef,
910848 .immediate => |imm| .{ .immediate = imm },
911 .lea_nav => |nav| .{ .lea_symbol = try genNavRef(lf, pt, nav) },
912 .load_uav => |uav| .{ .load_symbol = try lf.lowerUav(
849 .lea_nav => |nav| .{ .lea_symbol = try lf.navSymbol(nav) },
850 .load_uav => |uav| .{ .load_symbol = try lf.uavSymbol(
913851 pt,
914852 uav.val,
915853 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
916854 ) },
917 .lea_uav => |uav| .{ .lea_symbol = try lf.lowerUav(
855 .lea_uav => |uav| .{ .lea_symbol = try lf.uavSymbol(
918856 pt,
919857 uav.val,
920858 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
src/codegen/aarch64/Mir.zig+2-6
......@@ -93,11 +93,7 @@ pub fn emit(
9393 lf,
9494 zcu,
9595 atom_index,
96 try @import("../../codegen.zig").genNavRef(
97 lf,
98 pt,
99 nav_reloc.nav,
100 ),
96 try lf.navSymbol(nav_reloc.nav),
10197 mir.body[nav_reloc.reloc.label],
10298 body_end - Instruction.size * (1 + nav_reloc.reloc.label),
10399 nav_reloc.reloc.addend,
......@@ -107,7 +103,7 @@ pub fn emit(
107103 lf,
108104 zcu,
109105 atom_index,
110 try lf.lowerUav(
106 try lf.uavSymbol(
111107 pt,
112108 uav_reloc.uav.val,
113109 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),
src/codegen/loongarch/Mir.zig+3-12
......@@ -83,11 +83,7 @@ pub fn emit(
8383 lf,
8484 zcu,
8585 atom_index,
86 try @import("../../codegen.zig").genNavRef(
87 lf,
88 pt,
89 nav_reloc.nav,
90 ),
86 try lf.navSymbol(nav_reloc.nav),
9187 nav_reloc.reloc.type,
9288 body_end - @sizeOf(Instruction) * (1 + nav_reloc.reloc.label),
9389 nav_reloc.reloc.addend,
......@@ -97,7 +93,7 @@ pub fn emit(
9793 lf,
9894 zcu,
9995 atom_index,
100 try lf.lowerUav(
96 try lf.uavSymbol(
10197 pt,
10298 uav_reloc.uav.val,
10399 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),
......@@ -142,16 +138,11 @@ pub fn emit(
142138 ) catch |err|
143139 return zcu.codegenFail(func.owner_nav, "emit reloc failed: {t}", .{err});
144140
145 const func_nav = try @import("../../codegen.zig").genNavRef(
146 lf,
147 pt,
148 func.owner_nav,
149 );
150141 for (mir.internal_relocs) |internal_reloc| emitReloc(
151142 lf,
152143 zcu,
153144 atom_index,
154 func_nav,
145 try lf.navSymbol(func.owner_nav),
155146 internal_reloc.reloc.type,
156147 body_end - @sizeOf(Instruction) * (1 + internal_reloc.reloc.label),
157148 @sizeOf(Instruction) * (@as(i64, @intCast(mir.prologue.len + mir.body.len - internal_reloc.target))),
src/codegen/riscv64/CodeGen.zig+1-1
......@@ -8160,7 +8160,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
81608160 const lf = func.bin_file;
81618161
81628162 const result: codegen.MCValue = if (val.isUndef(pt.zcu))
8163 .{ .load_symbol = try lf.lowerUav(pt, val.toIntern(), .none) }
8163 .{ .load_symbol = try lf.uavSymbol(pt, val.toIntern(), .none) }
81648164 else
81658165 try codegen.genTypedValue(lf, pt, val, func.target);
81668166 const mcv: MCValue = switch (result) {
src/codegen/x86_64/Emit.zig+12-16
......@@ -100,11 +100,7 @@ pub fn emitMir(emit: *Emit) Error!void {
100100 .inst => |inst| .{ .inst = inst },
101101 .table => .table,
102102 .nav => |nav| {
103 const symbol_id = try codegen.genNavRef(
104 emit.bin_file,
105 emit.pt,
106 nav,
107 );
103 const symbol_id = try emit.bin_file.navSymbol(nav);
108104 const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{
109105 .symbol = symbol_id,
110106 .is_extern = switch (@"extern".visibility) {
......@@ -124,7 +120,7 @@ pub fn emitMir(emit: *Emit) Error!void {
124120 }
125121 },
126122 .uav => |uav| .{ .symbol = .{
127 .symbol = try emit.bin_file.lowerUav(
123 .symbol = try emit.bin_file.uavSymbol(
128124 emit.pt,
129125 uav.val,
130126 Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
......@@ -142,7 +138,7 @@ pub fn emitMir(emit: *Emit) Error!void {
142138 else if (emit.bin_file.cast(.macho)) |macho_file|
143139 @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
144140 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})))
145 else if (emit.bin_file.cast(.coff2)) |coff|
141 else if (emit.bin_file.cast(.coff)) |coff|
146142 @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym))))
147143 else
148144 return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
......@@ -157,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {
157153 .type = .FUNC,
158154 }) else if (emit.bin_file.cast(.macho)) |macho_file|
159155 @fromBackingInt(@intCast(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)))
160 else if (emit.bin_file.cast(.coff2)) |coff| @fromBackingInt(@intCast(@backingInt(try coff.globalSymbol(.{
156 else if (emit.bin_file.cast(.coff)) |coff| @fromBackingInt(@intCast(@backingInt(try coff.globalSymbol(.{
161157 .name = extern_func.toSlice(&emit.lower.mir).?,
162158 })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
163159 .is_extern = true,
......@@ -171,7 +167,7 @@ pub fn emitMir(emit: *Emit) Error!void {
171167 switch (lowered_inst.encoding.mnemonic) {
172168 .call => {
173169 reloc.target = .{ .branch = target };
174 if (target.is_dll_import and emit.bin_file.cast(.coff2) != null) {
170 if (target.is_dll_import and emit.bin_file.cast(.coff) != null) {
175171 try emit.encodeInst(try .new(.none, .call, &.{
176172 .{ .mem = .initRip(.ptr, 0) },
177173 }, emit.lower.target), reloc_info);
......@@ -252,7 +248,7 @@ pub fn emitMir(emit: *Emit) Error!void {
252248 }, emit.lower.target), reloc_info),
253249 else => unreachable,
254250 }
255 } else if (emit.bin_file.cast(.coff2)) |_| {
251 } else if (emit.bin_file.cast(.coff)) |_| {
256252 if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {
257253 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
258254 lowered_inst.ops[0],
......@@ -372,7 +368,7 @@ pub fn emitMir(emit: *Emit) Error!void {
372368 }, emit.lower.target), &.{});
373369 },
374370 else => unreachable,
375 } else if (emit.bin_file.cast(.coff2)) |coff| {
371 } else if (emit.bin_file.cast(.coff)) |coff| {
376372 switch (emit.lower.target.cpu.arch) {
377373 else => unreachable,
378374 .x86 => {
......@@ -597,12 +593,12 @@ pub fn emitMir(emit: *Emit) Error!void {
597593 .nav => |nav| @unionInit(
598594 DwarfLoc,
599595 addr_loc,
600 try codegen.genNavRef(emit.bin_file, emit.pt, nav),
596 try emit.bin_file.navSymbol(nav),
601597 ),
602598 .uav => |uav| @unionInit(
603599 DwarfLoc,
604600 addr_loc,
605 try emit.bin_file.lowerUav(
601 try emit.bin_file.uavSymbol(
606602 emit.pt,
607603 uav.val,
608604 Type.fromInterned(uav.orig_ty)
......@@ -845,7 +841,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
845841 if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL;
846842 break :rt .PC32;
847843 } },
848 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
844 ) else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
849845 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
850846 end_offset - 4,
851847 @fromBackingInt(@intCast(@backingInt(target.symbol))),
......@@ -883,7 +879,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
883879 .symbolnum = @intCast(@backingInt(target.symbol)),
884880 },
885881 });
886 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
882 } else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
887883 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
888884 end_offset - 4,
889885 @fromBackingInt(@intCast(@backingInt(target.symbol))),
......@@ -941,7 +937,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
941937 .symbolnum = @intCast(@backingInt(target.symbol)),
942938 },
943939 });
944 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
940 } else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
945941 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
946942 end_offset - 4,
947943 @fromBackingInt(@intCast(@backingInt(target.symbol))),
src/dev.zig+3-3
......@@ -109,10 +109,10 @@ pub const Env = enum {
109109 .spork8_backend,
110110 .lld_linker,
111111 .coff_linker,
112 .coff2_linker,
113112 .elf_linker,
114113 .elf2_linker,
115114 .macho_linker,
115 .macho2_linker,
116116 .c_linker,
117117 .wasm_linker,
118118 .spirv_linker,
......@@ -231,7 +231,7 @@ pub const Env = enum {
231231 .incremental,
232232 .legalize,
233233 .x86_64_backend,
234 .coff2_linker,
234 .coff_linker,
235235 => true,
236236 else => Env.sema.supports(feature),
237237 },
......@@ -321,10 +321,10 @@ pub const Feature = enum {
321321
322322 lld_linker,
323323 coff_linker,
324 coff2_linker,
325324 elf_linker,
326325 elf2_linker,
327326 macho_linker,
327 macho2_linker,
328328 c_linker,
329329 wasm_linker,
330330 spirv_linker,
src/link.zig+552-172
......@@ -481,7 +481,6 @@ pub const File = struct {
481481
482482 // TODO: remove this. libraries are resolved by the frontend.
483483 lib_directories: []const Directory,
484 framework_dirs: []const []const u8,
485484 rpath_list: []const []const u8,
486485
487486 /// Zig compiler development linker flags.
......@@ -501,8 +500,6 @@ pub const File = struct {
501500 headerpad_max_install_names: bool,
502501 /// Remove dylibs that are unreachable by the entry point or exported symbols
503502 dead_strip_dylibs: bool,
504 frameworks: []const MachO.Framework,
505 darwin_sdk_layout: ?MachO.SdkLayout,
506503 /// Force load all members of static archives that implement an
507504 /// Objective-C class or category
508505 force_load_objc: bool,
......@@ -587,7 +584,7 @@ pub const File = struct {
587584 pub fn startProgress(base: *File, prog_node: std.Progress.Node) void {
588585 switch (base.tag) {
589586 else => {},
590 inline .elf2, .coff2 => |tag| {
587 inline .elf2, .coff, .macho2 => |tag| {
591588 dev.check(tag.devFeature());
592589 return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node);
593590 },
......@@ -597,7 +594,7 @@ pub const File = struct {
597594 pub fn endProgress(base: *File) void {
598595 switch (base.tag) {
599596 else => {},
600 inline .elf2, .coff2 => |tag| {
597 inline .elf2, .coff, .macho2 => |tag| {
601598 dev.check(tag.devFeature());
602599 return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress();
603600 },
......@@ -651,11 +648,13 @@ pub const File = struct {
651648 }
652649 base.file = try emit.root_dir.handle.openFile(io, emit.sub_path, .{ .mode = .read_write });
653650 },
654 .elf2, .coff2 => if (base.file == null) {
651 .elf2, .coff, .macho2 => if (base.file == null) {
655652 const mf = if (base.cast(.elf2)) |elf|
656653 &elf.mf
657 else if (base.cast(.coff2)) |coff|
654 else if (base.cast(.coff)) |coff|
658655 &coff.mf
656 else if (base.cast(.macho2)) |macho|
657 &macho.mf
659658 else
660659 unreachable;
661660 mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
......@@ -740,11 +739,13 @@ pub const File = struct {
740739 }
741740 }
742741 },
743 .elf2, .coff2 => if (base.file) |f| {
742 .elf2, .coff, .macho2 => if (base.file) |f| {
744743 const mf = if (base.cast(.elf2)) |elf|
745744 &elf.mf
746 else if (base.cast(.coff2)) |coff|
745 else if (base.cast(.coff)) |coff|
747746 &coff.mf
747 else if (base.cast(.macho2)) |macho|
748 &macho.mf
748749 else
749750 unreachable;
750751 mf.unmap();
......@@ -841,7 +842,7 @@ pub const File = struct {
841842 switch (base.tag) {
842843 .lld => unreachable,
843844 else => {},
844 inline .elf, .elf2, .c, .coff2 => |tag| {
845 inline .elf, .elf2, .c, .coff => |tag| {
845846 dev.check(tag.devFeature());
846847 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);
847848 },
......@@ -899,7 +900,8 @@ pub const File = struct {
899900 .lld => unreachable,
900901 .plan9 => unreachable,
901902 .spirv => {},
902 .coff2 => {},
903 .coff => {},
904 .macho2 => {},
903905 inline else => |tag| {
904906 dev.check(tag.devFeature());
905907 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line);
......@@ -951,7 +953,7 @@ pub const File = struct {
951953 pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool {
952954 switch (base.tag) {
953955 else => return false,
954 inline .elf2, .coff2 => |tag| {
956 inline .elf2, .coff, .macho2 => |tag| {
955957 dev.check(tag.devFeature());
956958 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);
957959 },
......@@ -959,9 +961,10 @@ pub const File = struct {
959961 }
960962
961963 pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void {
964 if (base.comp.zcu.?.llvm_object != null) return;
962965 switch (base.tag) {
963966 else => {},
964 inline .elf2, .coff2 => |tag| {
967 inline .elf2, .coff, .macho2 => |tag| {
965968 dev.check(tag.devFeature());
966969 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);
967970 },
......@@ -1039,6 +1042,7 @@ pub const File = struct {
10391042 pub const RelocInfo = struct {
10401043 parent: Parent,
10411044 offset: u64,
1045 target: SymbolId,
10421046 addend: u32,
10431047
10441048 pub const Parent = union(enum) {
......@@ -1048,16 +1052,9 @@ pub const File = struct {
10481052 };
10491053 };
10501054
1051 /// Get allocated `Nav`'s address in virtual memory.
1052 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
1053 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
1054 /// `Nav`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
1055 /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate
1056 /// the block/atom.
10571055 /// Never called when LLVM is codegenning the ZCU.
1058 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 {
1059 assert(pt.zcu.llvm_object == null);
1060
1056 pub fn relocSymAddr(base: *File, reloc_info: RelocInfo) Error!void {
1057 assert(base.comp.zcu.?.llvm_object == null);
10611058 switch (base.tag) {
10621059 .lld => unreachable,
10631060 .c => unreachable,
......@@ -1067,20 +1064,19 @@ pub const File = struct {
10671064 .spork8 => unreachable,
10681065 inline else => |tag| {
10691066 dev.check(tag.devFeature());
1070 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
1067 return @as(*tag.Type(), @fieldParentPtr("base", base)).relocSymAddr(reloc_info);
10711068 },
10721069 }
10731070 }
10741071
10751072 /// Never called when LLVM is codegenning the ZCU.
1076 pub fn lowerUav(
1073 pub fn uavSymbol(
10771074 base: *File,
10781075 pt: Zcu.PerThread,
1079 decl_val: InternPool.Index,
1080 decl_align: InternPool.Alignment,
1076 uav_val: InternPool.Index,
1077 uav_align: InternPool.Alignment,
10811078 ) Error!SymbolId {
10821079 assert(pt.zcu.llvm_object == null);
1083
10841080 switch (base.tag) {
10851081 .lld => unreachable,
10861082 .c => unreachable,
......@@ -1090,15 +1086,14 @@ pub const File = struct {
10901086 .spork8 => unreachable,
10911087 inline else => |tag| {
10921088 dev.check(tag.devFeature());
1093 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align);
1089 return @as(*tag.Type(), @fieldParentPtr("base", base)).uavSymbol(pt, uav_val, uav_align);
10941090 },
10951091 }
10961092 }
10971093
10981094 /// Never called when LLVM is codegenning the ZCU.
1099 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 {
1095 pub fn navSymbol(base: *File, nav: InternPool.Nav.Index) Error!SymbolId {
11001096 assert(base.comp.zcu.?.llvm_object == null);
1101
11021097 switch (base.tag) {
11031098 .lld => unreachable,
11041099 .c => unreachable,
......@@ -1108,7 +1103,7 @@ pub const File = struct {
11081103 .spork8 => unreachable,
11091104 inline else => |tag| {
11101105 dev.check(tag.devFeature());
1111 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
1106 return @as(*tag.Type(), @fieldParentPtr("base", base)).navSymbol(nav);
11121107 },
11131108 }
11141109 }
......@@ -1238,15 +1233,68 @@ pub const File = struct {
12381233 }
12391234
12401235 pub fn loadInput(base: *File, input: Input) anyerror!void {
1241 if (base.tag == .lld) return;
1242 assert(!base.post_prelink);
1243
1236 if (base.tag != .lld) {
1237 assert(!base.post_prelink);
1238 }
12441239 switch (base.tag) {
1245 inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| {
1240 inline else => |tag| {
12461241 dev.check(tag.devFeature());
12471242 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
12481243 },
1249 else => {},
1244 .c, .spork8, .plan9, .lld => {},
1245 }
1246 }
1247
1248 fn loadDarwinSdkSettings(base: *File, sdk_settings_path: Path) Error!void {
1249 const comp = base.comp;
1250 const io = comp.io;
1251 const arena = comp.arena;
1252 const diags = &comp.link_diags;
1253
1254 const contents = sdk_settings_path.root_dir.handle.readFileAlloc(
1255 io,
1256 sdk_settings_path.sub_path,
1257 arena,
1258 .limited(1024 * 1024 * 256),
1259 ) catch |err| switch (err) {
1260 error.OutOfMemory, error.Canceled => |e| return e,
1261 else => |e| return diags.failParse(sdk_settings_path, "failed to parse Darwin SDK settings: {t}", .{e}),
1262 };
1263
1264 const parsed = std.json.parseFromSlice(std.json.Value, arena, contents, .{}) catch |err| switch (err) {
1265 error.OutOfMemory => |e| return e,
1266 else => |e| return diags.failParse(sdk_settings_path, "failed to parse Darwin SDK settings: {t}", .{e}),
1267 };
1268 const parsed_object = switch (parsed.value) {
1269 .object => |obj| obj,
1270 else => return diags.failParse(sdk_settings_path, "failed to parse Darwin SDK settings: file is not a JSON object", .{}),
1271 };
1272
1273 const version_json = parsed_object.get("MinimalDisplayName") orelse return diags.failParse(
1274 sdk_settings_path,
1275 "failed to parse Darwin SDK settings: 'MinimalDisplayName' missing",
1276 .{},
1277 );
1278 const version_str: []const u8 = switch (version_json) {
1279 .string => |str| str,
1280 else => return diags.failParse(
1281 sdk_settings_path,
1282 "failed to parse Darwin SDK settings: 'MinimalDisplayName' not a string",
1283 .{},
1284 ),
1285 };
1286 const version: DarwinSdkVersion = try .parse(diags, sdk_settings_path, version_str);
1287 try base.setDarwinSdkVersion(version);
1288 }
1289
1290 fn setDarwinSdkVersion(base: *File, version: DarwinSdkVersion) Error!void {
1291 assert(!base.post_prelink);
1292 switch (base.tag) {
1293 inline .macho, .macho2 => |tag| {
1294 dev.check(tag.devFeature());
1295 return @as(*tag.Type(), @fieldParentPtr("base", base)).setDarwinSdkVersion(version);
1296 },
1297 else => unreachable,
12501298 }
12511299 }
12521300
......@@ -1261,7 +1309,7 @@ pub const File = struct {
12611309 }
12621310
12631311 switch (base.tag) {
1264 inline .elf2, .coff2, .wasm, .c => |tag| {
1312 inline .elf2, .coff, .macho2, .wasm, .c => |tag| {
12651313 dev.check(tag.devFeature());
12661314 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
12671315 },
......@@ -1302,10 +1350,11 @@ pub const File = struct {
13021350 }
13031351
13041352 pub const Tag = enum {
1305 coff2,
1353 coff,
13061354 elf,
13071355 elf2,
13081356 macho,
1357 macho2,
13091358 c,
13101359 wasm,
13111360 spirv,
......@@ -1315,10 +1364,11 @@ pub const File = struct {
13151364
13161365 pub fn Type(comptime tag: Tag) type {
13171366 return switch (tag) {
1318 .coff2 => Coff2,
1367 .coff => Coff,
13191368 .elf => Elf,
13201369 .elf2 => Elf2,
13211370 .macho => MachO,
1371 .macho2 => MachO2,
13221372 .c => C,
13231373 .wasm => Wasm,
13241374 .spirv => Spirv,
......@@ -1330,9 +1380,9 @@ pub const File = struct {
13301380
13311381 fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {
13321382 return switch (ofmt) {
1333 .coff => .coff2,
1383 .coff => .coff,
13341384 .elf => if (use_new_linker) .elf2 else .elf,
1335 .macho => .macho,
1385 .macho => if (use_new_linker) .macho2 else .macho,
13361386 .wasm => .wasm,
13371387 .plan9 => .plan9,
13381388 .c => .c,
......@@ -1420,11 +1470,12 @@ pub const File = struct {
14201470
14211471 pub const Lld = @import("link/Lld.zig");
14221472 pub const C = @import("link/C.zig");
1423 pub const Coff2 = @import("link/Coff.zig");
1473 pub const Coff = @import("link/Coff.zig");
14241474 pub const Spork8 = @import("link/Spork8.zig");
14251475 pub const Elf = @import("link/Elf.zig");
14261476 pub const Elf2 = @import("link/Elf2.zig");
14271477 pub const MachO = @import("link/MachO.zig");
1478 pub const MachO2 = @import("link/MachO2.zig");
14281479 pub const Spirv = @import("link/Spirv.zig");
14291480 pub const Wasm = @import("link/Wasm.zig");
14301481 pub const Dwarf = @import("link/Dwarf.zig");
......@@ -1449,6 +1500,8 @@ pub const PrelinkTask = union(enum) {
14491500 /// Tells the linker to load a shared library, possibly one that is a
14501501 /// GNU ld script.
14511502 load_dso: Path,
1503 load_tbd: Path,
1504 load_darwin_sdk_settings: Path,
14521505};
14531506pub const ZcuTask = union(enum) {
14541507 /// Sent once per update, as the very first `ZcuTask` in the update. Indicates that all per-file
......@@ -1503,6 +1556,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
15031556 error.AlreadyReported => return, // error reported via diags
15041557 else => |e| switch (input) {
15051558 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {t}", .{e}),
1559 .tbd => |tbd| diags.addParseError(tbd.path, "failed to parse tbd: {t}", .{e}),
15061560 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),
15071561 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}),
15081562 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}),
......@@ -1524,34 +1578,72 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
15241578 assert(mem.startsWith(u8, flag, "-l"));
15251579 const lib_name = flag["-l".len..];
15261580 switch (comp.config.link_mode) {
1527 .dynamic => {
1528 const dso_path = Path.initCwd(
1581 .dynamic => loaded: {
1582 if (target.os.tag.isDarwin()) {
1583 // Prefer .tbd over .dylib.
1584 const tbd_path: Path = .initCwd(std.fmt.allocPrint(
1585 comp.arena,
1586 "{s}" ++ sep ++ "{s}{s}.tbd",
1587 .{ crt_dir, target.libPrefix(), lib_name },
1588 ) catch return diags.setAllocFailure());
1589 if (tbd_path.root_dir.handle.openFile(io, tbd_path.sub_path, .{})) |file| {
1590 errdefer file.close(io);
1591 base.loadInput(.{ .tbd = .{
1592 .path = tbd_path,
1593 .file = file,
1594 .needed = false,
1595 .weak = false,
1596 .reexport = false,
1597 } }) catch |err| switch (err) {
1598 error.AlreadyReported => return,
1599 error.Canceled => io.recancel(),
1600 else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}),
1601 };
1602 break :loaded;
1603 } else |err| switch (err) {
1604 error.FileNotFound => {},
1605 error.Canceled => io.recancel(),
1606 else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}),
1607 }
1608 }
1609
1610 // Try dynamic library.
1611 const dso_path: Path = .initCwd(std.fmt.allocPrint(
1612 comp.arena,
1613 "{s}" ++ sep ++ "{s}{s}{s}",
1614 .{ crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix() },
1615 ) catch return diags.setAllocFailure());
1616 if (base.openLoadDso(dso_path, .{
1617 .preferred_mode = .dynamic,
1618 .search_strategy = .paths_first,
1619 })) {
1620 break :loaded;
1621 } else |err| switch (err) {
1622 error.FileNotFound => {},
1623 error.AlreadyReported => return,
1624 error.Canceled => io.recancel(),
1625 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1626 }
1627
1628 // Also try static.
1629 const archive_path = Path.initCwd(
15291630 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1530 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
1631 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
15311632 }) catch return diags.setAllocFailure(),
15321633 );
1533 base.openLoadDso(dso_path, .{
1634 if (base.openLoadArchiveQuery(archive_path, .{
15341635 .preferred_mode = .dynamic,
15351636 .search_strategy = .paths_first,
1536 }) catch |err| switch (err) {
1537 error.FileNotFound => {
1538 // Also try static.
1539 const archive_path = Path.initCwd(
1540 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1541 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1542 }) catch return diags.setAllocFailure(),
1543 );
1544 base.openLoadArchiveQuery(archive_path, .{
1545 .preferred_mode = .dynamic,
1546 .search_strategy = .paths_first,
1547 }) catch |archive_err| switch (archive_err) {
1548 error.AlreadyReported => return, // error reported via diags
1549 else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }),
1550 };
1551 },
1552 error.AlreadyReported => return, // error reported via diags
1553 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1554 };
1637 })) {
1638 break :loaded;
1639 } else |archive_err| switch (archive_err) {
1640 error.FileNotFound => {},
1641 error.AlreadyReported => return,
1642 error.Canceled => io.recancel(),
1643 else => |e| diags.addParseError(archive_path, "failed to parse archive: {s}", .{@errorName(e)}),
1644 }
1645
1646 diags.addError("failed to find library '{s}'", .{lib_name});
15551647 },
15561648 .static => {
15571649 const path = Path.initCwd(
......@@ -1571,6 +1663,19 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
15711663 }
15721664 }
15731665
1666 if (target.os.tag.isDarwin()) {
1667 const sdk_settings_path: Path = .initCwd(std.fmt.allocPrint(
1668 comp.arena,
1669 "{s}" ++ sep ++ "SDKSettings.json",
1670 .{libc_installation.darwin_sdk_dir.?},
1671 ) catch return diags.setAllocFailure());
1672 base.loadDarwinSdkSettings(sdk_settings_path) catch |err| switch (err) {
1673 error.OutOfMemory => return diags.setAllocFailure(),
1674 error.Canceled => return io.recancel(),
1675 error.AlreadyReported => return,
1676 };
1677 }
1678
15741679 if (target.os.tag == .windows and target.abi == .msvc) {
15751680 const inputs: []const struct {
15761681 dir: enum { crt, msvc_lib, kernel32_lib },
......@@ -1646,6 +1751,37 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
16461751 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
16471752 };
16481753 },
1754 .load_tbd => |path| {
1755 const prog_node = comp.link_prog_node.start("Parse Shared Library Stub", 0);
1756 defer prog_node.end();
1757 if (path.root_dir.handle.openFile(io, path.sub_path, .{})) |file| {
1758 errdefer file.close(io);
1759 base.loadInput(.{ .tbd = .{
1760 .path = path,
1761 .file = file,
1762 .needed = false,
1763 .weak = false,
1764 .reexport = false,
1765 } }) catch |err| switch (err) {
1766 error.AlreadyReported => return,
1767 error.Canceled => io.recancel(),
1768 else => |e| diags.addParseError(path, "failed to parse tbd: {t}", .{e}),
1769 };
1770 } else |err| switch (err) {
1771 error.FileNotFound => {},
1772 error.Canceled => io.recancel(),
1773 else => |e| diags.addParseError(path, "failed to parse tbd: {t}", .{e}),
1774 }
1775 },
1776 .load_darwin_sdk_settings => |path| {
1777 const prog_node = comp.link_prog_node.start("Parse SDK Settings", 0);
1778 defer prog_node.end();
1779 base.loadDarwinSdkSettings(path) catch |err| switch (err) {
1780 error.OutOfMemory => return diags.setAllocFailure(),
1781 error.Canceled => return io.recancel(),
1782 error.AlreadyReported => return,
1783 };
1784 },
16491785 }
16501786}
16511787pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void {
......@@ -1820,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {
18201956 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
18211957 /// Who knows! Fuck around and find out.
18221958 ambiguous_name: AmbiguousNameQuery,
1959 framework_query: FrameworkQuery,
18231960
18241961 pub const NameQuery = struct {
18251962 name: []const u8,
......@@ -1858,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {
18581995 }
18591996 };
18601997
1998 pub const FrameworkQuery = struct {
1999 name: []const u8,
2000 needed: bool,
2001 weak: bool,
2002 };
2003
18612004 pub const SearchStrategy = enum {
18622005 paths_first,
18632006 mode_first,
......@@ -1872,6 +2015,8 @@ pub const Input = union(enum) {
18722015 /// May not be a GNU ld script. Those are resolved when converting from
18732016 /// `UnresolvedInput` to `Input` values.
18742017 dso: Dso,
2018 /// Only possible when targeting Darwin.
2019 tbd: Tbd,
18752020
18762021 pub const Object = struct {
18772022 path: Path,
......@@ -1896,24 +2041,32 @@ pub const Input = union(enum) {
18962041 pub const FallbackSoname = enum { basename, full_path };
18972042 };
18982043
2044 pub const Tbd = struct {
2045 path: Path,
2046 file: Io.File,
2047 needed: bool,
2048 weak: bool,
2049 reexport: bool,
2050 };
2051
18992052 pub fn path(input: Input) Path {
19002053 return switch (input) {
19012054 .object, .archive => |obj| obj.path,
1902 inline .res, .dso => |x| x.path,
2055 inline .res, .dso, .tbd => |x| x.path,
19032056 };
19042057 }
19052058
19062059 pub fn pathAndFile(input: Input) struct { Path, Io.File } {
19072060 return switch (input) {
19082061 .object, .archive => |obj| .{ obj.path, obj.file },
1909 inline .res, .dso => |x| .{ x.path, x.file },
2062 inline .res, .dso, .tbd => |x| .{ x.path, x.file },
19102063 };
19112064 }
19122065
19132066 pub fn taskName(input: Input) []const u8 {
19142067 return switch (input) {
19152068 .object, .archive => |obj| obj.path.basename(),
1916 inline .res, .dso => |x| x.path.basename(),
2069 inline .res, .dso, .tbd => |x| x.path.basename(),
19172070 };
19182071 }
19192072};
......@@ -1946,6 +2099,15 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
19462099 man.hash.add(dso.reexport);
19472100 man.hash.add(dso.fallback_soname);
19482101 },
2102 .tbd => |tbd| {
2103 _ = try man.addInputPath(tbd.path, .{
2104 .handle = .{ .file = tbd.file },
2105 .request_handle = true,
2106 });
2107 man.hash.add(tbd.needed);
2108 man.hash.add(tbd.weak);
2109 man.hash.add(tbd.reexport);
2110 },
19492111 }
19502112 }
19512113}
......@@ -1961,6 +2123,7 @@ pub fn resolveInputs(
19612123 /// Allocated with `gpa`.
19622124 resolved_inputs: *std.ArrayList(Input),
19632125 lib_directories: []const Cache.Directory,
2126 framework_directories: []const Cache.Directory,
19642127 color: std.zig.Color,
19652128) Allocator.Error!void {
19662129 var checked_paths: std.ArrayList(u8) = .empty;
......@@ -1972,6 +2135,7 @@ pub fn resolveInputs(
19722135 var archive_dedup: ArchiveDedupMap = .empty;
19732136 defer archive_dedup.deinit(gpa);
19742137
2138 // Allocated with `arena`.
19752139 var failed_libs: std.ArrayList(struct {
19762140 name: []const u8,
19772141 strategy: UnresolvedInput.SearchStrategy,
......@@ -1979,6 +2143,12 @@ pub fn resolveInputs(
19792143 preferred_mode: std.lang.LinkMode,
19802144 }) = .empty;
19812145
2146 // Allocated with `arena`.
2147 var failed_frameworks: std.ArrayList(struct {
2148 name: []const u8,
2149 checked_paths: []const u8,
2150 }) = .empty;
2151
19822152 // Convert external system libs into a stack so that items can be
19832153 // pushed to it.
19842154 //
......@@ -2180,17 +2350,47 @@ pub fn resolveInputs(
21802350 }
21812351 continue;
21822352 },
2353 .framework_query => |framework_query| {
2354 checked_paths.clearRetainingCapacity();
2355 for (framework_directories) |framework_directory| {
2356 switch (try resolveFrameworkInput(
2357 gpa,
2358 arena,
2359 io,
2360 resolved_inputs,
2361 &checked_paths,
2362 &ld_script_bytes,
2363 &archive_dedup,
2364 framework_directory,
2365 framework_query,
2366 )) {
2367 .ok => continue :syslib,
2368 .no_match => {},
2369 }
2370 }
2371 try failed_frameworks.append(arena, .{
2372 .name = framework_query.name,
2373 .checked_paths = try arena.dupe(u8, checked_paths.items),
2374 });
2375 continue :syslib;
2376 },
21832377 }
21842378 comptime unreachable;
21852379 }
21862380
2187 if (failed_libs.items.len > 0) {
2381 if (failed_libs.items.len > 0 or failed_frameworks.items.len > 0) {
21882382 for (failed_libs.items) |f| {
21892383 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
21902384 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{
21912385 f.preferred_mode, f.name, f.strategy, searched_paths,
21922386 });
21932387 }
2388 for (failed_frameworks.items) |f| {
2389 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2390 std.log.err("unable to find system framework {q}. searched paths:{s}", .{
2391 f.name, searched_paths,
2392 });
2393 }
21942394 std.process.exit(1);
21952395 }
21962396}
......@@ -2235,16 +2435,14 @@ fn resolveLibInput(
22352435 else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }),
22362436 };
22372437 errdefer file.close(io);
2238 return finishResolveLibInput(
2239 io,
2240 resolved_inputs,
2241 archive_dedup,
2242 test_path,
2243 file,
2244 link_mode,
2245 name_query.query,
2246 .basename,
2247 );
2438 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2439 .path = test_path,
2440 .file = file,
2441 .needed = name_query.query.needed,
2442 .weak = name_query.query.weak,
2443 .reexport = name_query.query.reexport,
2444 } });
2445 return .ok;
22482446 }
22492447
22502448 {
......@@ -2282,16 +2480,15 @@ fn resolveLibInput(
22822480 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
22832481 };
22842482 errdefer file.close(io);
2285 return finishResolveLibInput(
2286 io,
2287 resolved_inputs,
2288 archive_dedup,
2289 test_path,
2290 file,
2291 link_mode,
2292 name_query.query,
2293 .basename,
2294 );
2483 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2484 .path = test_path,
2485 .file = file,
2486 .needed = name_query.query.needed,
2487 .weak = name_query.query.weak,
2488 .reexport = name_query.query.reexport,
2489 .fallback_soname = .basename,
2490 } });
2491 return .ok;
22952492 }
22962493
22972494 // In the case of MinGW, the main check will be .lib but we also need to
......@@ -2307,16 +2504,13 @@ fn resolveLibInput(
23072504 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
23082505 };
23092506 errdefer file.close(io);
2310 return finishResolveLibInput(
2311 io,
2312 resolved_inputs,
2313 archive_dedup,
2314 test_path,
2315 file,
2316 link_mode,
2317 name_query.query,
2318 .basename,
2319 );
2507 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2508 .path = test_path,
2509 .file = file,
2510 .must_link = name_query.query.must_link,
2511 .hidden = name_query.query.hidden,
2512 });
2513 return .ok;
23202514 }
23212515
23222516 // In the case of OpenBSD, dynamic libraries are always versioned, without
......@@ -2378,6 +2572,145 @@ fn resolveLibInput(
23782572 return .no_match;
23792573}
23802574
2575fn resolveFrameworkInput(
2576 gpa: Allocator,
2577 arena: Allocator,
2578 io: Io,
2579 /// Allocated via `gpa`.
2580 resolved_inputs: *std.ArrayList(Input),
2581 /// Allocated via `gpa`.
2582 checked_paths: *std.ArrayList(u8),
2583 /// Allocated via `gpa`.
2584 ld_script_bytes: *std.ArrayList(u8),
2585 /// Allocated via `gpa`.
2586 archive_dedup: *ArchiveDedupMap,
2587 framework_directory: Directory,
2588 framework_query: UnresolvedInput.FrameworkQuery,
2589) Allocator.Error!ResolveLibInputResult {
2590 const sep = std.fs.path.sep_str;
2591
2592 tbd: {
2593 const test_path: Path = .{
2594 .root_dir = framework_directory,
2595 .sub_path = try std.fmt.allocPrint(
2596 arena,
2597 "{s}.framework" ++ sep ++ "{s}.tbd",
2598 .{ framework_query.name, framework_query.name },
2599 ),
2600 };
2601 try checked_paths.print(gpa, "\n {f}", .{test_path});
2602 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2603 error.FileNotFound => break :tbd,
2604 else => |e| fatal("unable to search for tbd library {qf}: {t}", .{ test_path, e }),
2605 };
2606 errdefer file.close(io);
2607 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2608 .path = test_path,
2609 .file = file,
2610 .needed = framework_query.needed,
2611 .weak = framework_query.weak,
2612 .reexport = false,
2613 } });
2614 return .ok;
2615 }
2616
2617 dylib: {
2618 const test_path: Path = .{
2619 .root_dir = framework_directory,
2620 .sub_path = try std.fmt.allocPrint(
2621 arena,
2622 "{s}.framework" ++ sep ++ "{s}.dylib",
2623 .{ framework_query.name, framework_query.name },
2624 ),
2625 };
2626 try checked_paths.print(gpa, "\n {f}", .{test_path});
2627 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2628 error.FileNotFound => break :dylib,
2629 else => |e| fatal("unable to search for dynamic library {qf}: {t}", .{ test_path, e }),
2630 };
2631 errdefer file.close(io);
2632 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2633 .path = test_path,
2634 .file = file,
2635 .needed = framework_query.needed,
2636 .weak = framework_query.weak,
2637 .reexport = false,
2638 .fallback_soname = .basename,
2639 } });
2640 return .ok;
2641 }
2642
2643 ambiguous: {
2644 const test_path: Path = .{
2645 .root_dir = framework_directory,
2646 .sub_path = try std.fmt.allocPrint(
2647 arena,
2648 "{s}.framework" ++ sep ++ "{s}",
2649 .{ framework_query.name, framework_query.name },
2650 ),
2651 };
2652 try checked_paths.print(gpa, "\n {f}", .{test_path});
2653 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2654 error.FileNotFound => break :ambiguous,
2655 else => |e| fatal("unable to search for framework library {qf}: {t}", .{ test_path, e }),
2656 };
2657 errdefer file.close(io);
2658
2659 const macho_magics: []const [4]u8 = &.{
2660 @bitCast(std.macho.MH_MAGIC),
2661 @bitCast(std.macho.MH_CIGAM),
2662 @bitCast(std.macho.MH_MAGIC_64),
2663 @bitCast(std.macho.MH_CIGAM_64),
2664 @bitCast(std.macho.FAT_MAGIC),
2665 @bitCast(std.macho.FAT_CIGAM),
2666 @bitCast(std.macho.FAT_MAGIC_64),
2667 @bitCast(std.macho.FAT_CIGAM_64),
2668 };
2669 try ld_script_bytes.resize(gpa, @max(4, std.macho.ARMAG.len));
2670 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
2671 fatal("failed to read {qf}: {t}", .{ test_path, err });
2672 const buf = ld_script_bytes.items[0..n];
2673
2674 for (macho_magics) |*macho_magic| {
2675 if (mem.startsWith(u8, buf, macho_magic)) {
2676 // Appears to be a Mach-O file, so a dylib.
2677 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2678 .path = test_path,
2679 .file = file,
2680 .needed = framework_query.needed,
2681 .weak = framework_query.weak,
2682 .reexport = false,
2683 .fallback_soname = .basename,
2684 } });
2685 return .ok;
2686 }
2687 }
2688
2689 if (mem.startsWith(u8, buf, std.macho.ARMAG)) {
2690 // Appears to be an archive file.
2691 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2692 .path = test_path,
2693 .file = file,
2694 .must_link = false,
2695 .hidden = false,
2696 });
2697 return .ok;
2698 }
2699
2700 // It doesn't look like a dylib or archive, so assume it's a tbd.
2701 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2702 .path = test_path,
2703 .file = file,
2704 .needed = framework_query.needed,
2705 .weak = framework_query.weak,
2706 .reexport = false,
2707 } });
2708 return .ok;
2709 }
2710
2711 return .no_match;
2712}
2713
23812714/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so
23822715/// that linker implementations do not need to open and scan the archive just to determine that they
23832716/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions
......@@ -2401,45 +2734,21 @@ const ArchiveDedupAdapter = struct {
24012734 }
24022735};
24032736
2404fn finishResolveLibInput(
2737fn addResolvedStaticLibInput(
24052738 io: Io,
24062739 resolved_inputs: *std.ArrayList(Input),
24072740 archive_dedup: *ArchiveDedupMap,
2408 path: Path,
2409 file: Io.File,
2410 link_mode: std.lang.LinkMode,
2411 query: UnresolvedInput.Query,
2412 fallback_soname: Input.Dso.FallbackSoname,
2413) ResolveLibInputResult {
2414 switch (link_mode) {
2415 .static => {
2416 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2417 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx);
2418 if (gop.found_existing) {
2419 // Ignore duplicate archive input
2420 file.close(io);
2421 return .ok;
2422 }
2423 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2424 resolved_inputs.appendAssumeCapacity(.{ .archive = .{
2425 .path = path,
2426 .file = file,
2427 .must_link = query.must_link,
2428 .hidden = query.hidden,
2429 } });
2430 },
2431 .dynamic => resolved_inputs.appendAssumeCapacity(.{
2432 .dso = .{
2433 .path = path,
2434 .file = file,
2435 .needed = query.needed,
2436 .weak = query.weak,
2437 .reexport = query.reexport,
2438 .fallback_soname = fallback_soname,
2439 },
2440 }),
2741 archive: Input.Object,
2742) void {
2743 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2744 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(archive.path, ctx);
2745 if (gop.found_existing) {
2746 // Ignore duplicate archive input
2747 archive.file.close(io);
2748 } else {
2749 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2750 resolved_inputs.appendAssumeCapacity(.{ .archive = archive });
24412751 }
2442 return .ok;
24432752}
24442753
24452754fn resolvePathInput(
......@@ -2535,19 +2844,24 @@ fn resolvePathInputLib(
25352844 try archive_dedup.ensureUnusedCapacity(gpa, 1);
25362845
25372846 const test_path: Path = pq.path;
2847
2848 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2849 error.FileNotFound => return .no_match,
2850 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{
2851 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2852 }),
2853 };
2854 errdefer file.close(io);
2855
25382856 // In the case of shared libraries, they might actually be "linker scripts"
25392857 // that contain references to other libraries.
2540 if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) {
2541 .static_library, .shared_library => true,
2542 else => false,
2543 }) {
2544 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2545 error.FileNotFound => return .no_match,
2546 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{
2547 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2548 }),
2549 };
2550 errdefer file.close(io);
2858 ld_script: {
2859 if (!pq.query.allow_so_scripts) break :ld_script;
2860 if (target.ofmt != .elf) break :ld_script;
2861 switch (Compilation.classifyFileExt(test_path.sub_path)) {
2862 .static_library, .shared_library => {},
2863 else => break :ld_script,
2864 }
25512865 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
25522866 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
25532867 fatal("failed to read {qf}: {t}", .{ test_path, err });
......@@ -2557,16 +2871,7 @@ fn resolvePathInputLib(
25572871 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
25582872 {
25592873 // Appears to be an ELF or archive file.
2560 return finishResolveLibInput(
2561 io,
2562 resolved_inputs,
2563 archive_dedup,
2564 test_path,
2565 file,
2566 link_mode,
2567 pq.query,
2568 fallback_soname,
2569 );
2874 break :ld_script;
25702875 }
25712876 const stat = file.stat(io) catch |err|
25722877 fatal("failed to stat {f}: {t}", .{ test_path, err });
......@@ -2630,21 +2935,23 @@ fn resolvePathInputLib(
26302935 return .ok;
26312936 }
26322937
2633 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2634 error.FileNotFound => return .no_match,
2635 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),
2636 };
2637 errdefer file.close(io);
2638 return finishResolveLibInput(
2639 io,
2640 resolved_inputs,
2641 archive_dedup,
2642 test_path,
2643 file,
2644 link_mode,
2645 pq.query,
2646 fallback_soname,
2647 );
2938 switch (link_mode) {
2939 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2940 .path = test_path,
2941 .file = file,
2942 .needed = pq.query.needed,
2943 .weak = pq.query.weak,
2944 .reexport = pq.query.reexport,
2945 .fallback_soname = fallback_soname,
2946 } }),
2947 .static => addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2948 .path = test_path,
2949 .file = file,
2950 .must_link = pq.query.must_link,
2951 .hidden = pq.query.hidden,
2952 }),
2953 }
2954 return .ok;
26482955}
26492956
26502957pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {
......@@ -2699,7 +3006,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool {
26993006pub fn countObjectInputs(inputs: []const Input) usize {
27003007 var count: usize = 0;
27013008 for (inputs) |input| switch (input) {
2702 .dso => continue,
3009 .dso, .tbd => continue,
27033010 .res, .object, .archive => count += 1,
27043011 };
27053012 return count;
......@@ -2709,7 +3016,80 @@ pub fn countObjectInputs(inputs: []const Input) usize {
27093016pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
27103017 for (inputs) |input| switch (input) {
27113018 .object, .archive => |obj| return obj,
2712 .res, .dso => continue,
3019 .res, .dso, .tbd => continue,
27133020 };
27143021 return null;
27153022}
3023
3024pub const DarwinSdkVersion = packed struct(u32) {
3025 patch: u8,
3026 minor: u8,
3027 major: u16,
3028
3029 fn parse(
3030 diags: *Diags,
3031 /// Passed to `Diags.failParse` on error.
3032 sdk_settings_path: Path,
3033 version_str: []const u8,
3034 ) error{AlreadyReported}!DarwinSdkVersion {
3035 var it = std.mem.splitScalar(u8, version_str, '.');
3036
3037 const major = try parseComponent(
3038 .major,
3039 diags,
3040 sdk_settings_path,
3041 version_str,
3042 it.first(),
3043 );
3044 const minor = if (it.next()) |component_str| try parseComponent(
3045 .minor,
3046 diags,
3047 sdk_settings_path,
3048 version_str,
3049 component_str,
3050 ) else return diags.failParse(
3051 sdk_settings_path,
3052 "failed to parse Darwin SDK version {q}: missing minor version",
3053 .{version_str},
3054 );
3055 const patch = if (it.next()) |component_str| try parseComponent(
3056 .patch,
3057 diags,
3058 sdk_settings_path,
3059 version_str,
3060 component_str,
3061 ) else 0; // Apple sometimes omit the patch version
3062
3063 if (it.next() != null) return diags.failParse(
3064 sdk_settings_path,
3065 "failed to parse Darwin SDK version {q}: too many version components",
3066 .{version_str},
3067 );
3068
3069 return .{ .major = major, .minor = minor, .patch = patch };
3070 }
3071
3072 fn parseComponent(
3073 comptime component: enum { major, minor, patch },
3074 diags: *Diags,
3075 /// Passed to `Diags.failParse` on error.
3076 sdk_settings_path: Path,
3077 /// Only used for error messages.
3078 version_str: []const u8,
3079 component_str: []const u8,
3080 ) error{AlreadyReported}!@FieldType(DarwinSdkVersion, @tagName(component)) {
3081 const Int = @FieldType(DarwinSdkVersion, @tagName(component));
3082 return std.fmt.parseInt(Int, component_str, 10) catch |err| switch (err) {
3083 error.Overflow => return diags.failParse(
3084 sdk_settings_path,
3085 "failed to parse Darwin SDK version {q}: {t} version {q} too large",
3086 .{ version_str, component, component_str },
3087 ),
3088 error.InvalidCharacter => return diags.failParse(
3089 sdk_settings_path,
3090 "failed to parse Darwin SDK version {q}: invalid {t} version {q}",
3091 .{ version_str, component, component_str },
3092 ),
3093 };
3094 }
3095};
src/link/Coff.zig+15-35
......@@ -1607,7 +1607,7 @@ fn create(
16071607 errdefer file.close(io);
16081608 coff.* = .{
16091609 .base = .{
1610 .tag = .coff2,
1610 .tag = .coff,
16111611
16121612 .comp = comp,
16131613 .emit = path,
......@@ -2935,18 +2935,19 @@ fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.Na
29352935 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
29362936 return @fromBackingInt(@intCast(sym_gop.index));
29372937}
2938pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
2938pub fn navSymbol(coff: *Coff, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId {
2939 const zcu = coff.base.comp.zcu.?;
29392940 const ip = &zcu.intern_pool;
29402941 const nav = ip.getNav(nav_index);
2941 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(.{
2942 if (nav.getExtern(ip)) |@"extern"| return @bitCast(try coff.globalSymbol(.{
29422943 .name = @"extern".name.toSlice(ip),
29432944 .lib_name = @"extern".lib_name.toSlice(ip),
29442945 // TODO: Threadlocal as well?
29452946 .type = if (ip.isFunctionType(nav.resolved.?.type)) .code else .data,
29462947 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,
2947 });
2948 }));
29482949 const nmi = try coff.navMapIndex(zcu, nav_index);
2949 return nmi.symbol(coff);
2950 return @bitCast(nmi.symbol(coff));
29502951}
29512952
29522953fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
......@@ -2956,10 +2957,6 @@ fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
29562957 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
29572958 return @fromBackingInt(@intCast(sym_gop.index));
29582959}
2959pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
2960 const umi = try coff.uavMapIndex(uav_val);
2961 return umi.symbol(coff);
2962}
29632960
29642961pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
29652962 const gpa = coff.base.comp.gpa;
......@@ -2972,24 +2969,8 @@ pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
29722969 return sym_gop.value_ptr.*;
29732970}
29742971
2975pub fn getNavVAddr(
2976 coff: *Coff,
2977 pt: Zcu.PerThread,
2978 nav: InternPool.Nav.Index,
2979 reloc_info: link.File.RelocInfo,
2980) link.Error!u64 {
2981 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
2982}
2983
2984pub fn getUavVAddr(
2985 coff: *Coff,
2986 uav: InternPool.Index,
2987 reloc_info: link.File.RelocInfo,
2988) link.Error!u64 {
2989 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
2990}
2991
2992pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) link.Error!u64 {
2972pub fn relocSymAddr(coff: *Coff, reloc_info: link.File.RelocInfo) link.Error!void {
2973 const target_si: Symbol.Index = @bitCast(reloc_info.target);
29932974 try coff.addReloc(
29942975 @fromBackingInt(@intCast(@backingInt(reloc_info.parent.atom_index))),
29952976 reloc_info.offset,
......@@ -3001,10 +2982,6 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
30012982 .I386 => .{ .I386 = .DIR32 },
30022983 },
30032984 );
3004
3005 var vaddr: u64 = target_si.get(coff).rva;
3006 if (coff.isImage()) vaddr += coff.optionalHeaderField(.image_base);
3007 return vaddr;
30082985}
30092986
30102987/// Caller guarantees there is capacity for one member and two nodes
......@@ -3935,6 +3912,7 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
39353912 coff.loadDll(dso.path, &fr) catch |err|
39363913 return coff.failLoadInput(err, &fr, dso.path);
39373914 },
3915 .tbd => unreachable,
39383916 }
39393917}
39403918
......@@ -5580,13 +5558,15 @@ pub fn updateContainerType(
55805558 };
55815559}
55825560
5583pub fn lowerUav(
5561pub fn uavSymbol(
55845562 coff: *Coff,
55855563 pt: Zcu.PerThread,
55865564 uav_val: InternPool.Index,
55875565 uav_align: InternPool.Alignment,
55885566) link.Error!link.File.SymbolId {
5589 const zcu = pt.zcu;
5567 _ = pt;
5568
5569 const zcu = coff.base.comp.zcu.?;
55905570 const gpa = zcu.gpa;
55915571
55925572 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
......@@ -7471,8 +7451,8 @@ fn updateExportInner(
74717451
74727452 try coff.symbols.ensureUnusedCapacity(gpa, 1);
74737453 const exported_si: Symbol.Index = switch (exp.exported) {
7474 .nav => |nav| try coff.navSymbol(zcu, nav),
7475 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(
7454 .nav => |nav| @bitCast(try coff.navSymbol(nav)),
7455 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.uavSymbol(
74767456 pt,
74777457 uav,
74787458 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
src/link/Elf.zig+10-8
......@@ -463,21 +463,21 @@ pub fn deinit(self: *Elf) void {
463463 self.dump_argv_list.deinit(gpa);
464464}
465465
466pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
467 return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info);
466pub fn navSymbol(self: *Elf, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId {
467 return self.zigObjectPtr().?.navSymbol(self, nav);
468468}
469469
470pub fn lowerUav(
470pub fn relocSymAddr(self: *Elf, reloc_info: link.File.RelocInfo) !void {
471 return self.zigObjectPtr().?.relocSymAddr(self, reloc_info);
472}
473
474pub fn uavSymbol(
471475 self: *Elf,
472476 pt: Zcu.PerThread,
473477 uav: InternPool.Index,
474478 explicit_alignment: InternPool.Alignment,
475479) !link.File.SymbolId {
476 return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment);
477}
478
479pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
480 return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info);
480 return self.zigObjectPtr().?.uavSymbol(self, pt, uav, explicit_alignment);
481481}
482482
483483/// Returns end pos of collision, if any.
......@@ -722,6 +722,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
722722 const argv = &self.dump_argv_list;
723723 switch (input) {
724724 .res => unreachable,
725 .tbd => unreachable,
725726 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
726727 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),
727728 }
......@@ -729,6 +730,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
729730
730731 switch (input) {
731732 .res => unreachable,
733 .tbd => unreachable,
732734 .object => |obj| try parseObject(self, obj),
733735 .archive => |obj| if (self.base.isStaticLib()) {
734736 // Ignore static library inputs when generating a static library.
src/link/Elf/ZigObject.zig+30-42
......@@ -917,53 +917,42 @@ pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
917917 return code;
918918}
919919
920pub fn getNavVAddr(
921 self: *ZigObject,
920pub fn navSymbol(
921 zo: *ZigObject,
922922 elf_file: *Elf,
923 pt: Zcu.PerThread,
924923 nav_index: InternPool.Nav.Index,
925 reloc_info: link.File.RelocInfo,
926) !u64 {
927 const zcu = pt.zcu;
924) link.Error!link.File.SymbolId {
925 const zcu = elf_file.base.comp.zcu.?;
928926 const ip = &zcu.intern_pool;
929927 const nav = ip.getNav(nav_index);
930 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
931 const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
932 elf_file,
933 nav.name.toSlice(ip),
934 @"extern".lib_name.toSlice(ip),
935 ) else try self.getOrCreateMetadataForNav(zcu, nav_index);
936 const this_sym = self.symbol(this_sym_index);
937 const vaddr = this_sym.address(.{}, elf_file);
938 switch (reloc_info.parent) {
939 .none => unreachable,
940 .atom_index => |atom_index| {
941 const parent_atom = self.symbol(@backingInt(atom_index)).atom(elf_file).?;
942 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
943 try parent_atom.addReloc(elf_file.base.comp.gpa, .{
944 .r_offset = reloc_info.offset,
945 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type,
946 .r_addend = reloc_info.addend,
947 }, self);
948 },
949 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
950 .source_off = @intCast(reloc_info.offset),
951 .target_sym = @fromBackingInt(@intCast(this_sym_index)),
952 .target_off = reloc_info.addend,
953 }),
928 if (nav.getExtern(ip)) |@"extern"| {
929 const sym_index = try zo.getGlobalSymbol(
930 elf_file,
931 nav.name.toSlice(ip),
932 @"extern".lib_name.toSlice(ip),
933 );
934 if (@"extern".linkage == .weak) {
935 zo.symbol(sym_index).flags.weak = true;
936 }
937 if (nav.resolved.?.@"threadlocal") {
938 zo.symbol(sym_index).flags.is_tls = true;
939 }
940 return @fromBackingInt(sym_index);
941 } else {
942 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
943 if (nav.resolved.?.@"threadlocal") {
944 zo.symbol(sym_index).flags.is_tls = true;
945 }
946 return @fromBackingInt(sym_index);
954947 }
955 return @intCast(vaddr);
956948}
957949
958pub fn getUavVAddr(
950pub fn relocSymAddr(
959951 self: *ZigObject,
960952 elf_file: *Elf,
961 uav: InternPool.Index,
962953 reloc_info: link.File.RelocInfo,
963) !u64 {
964 const sym_index = self.uavs.get(uav).?.symbol_index;
965 const sym = self.symbol(sym_index);
966 const vaddr = sym.address(.{}, elf_file);
954) !void {
955 const this_sym_index = @backingInt(reloc_info.target);
967956 switch (reloc_info.parent) {
968957 .none => unreachable,
969958 .atom_index => |atom_index| {
......@@ -971,20 +960,19 @@ pub fn getUavVAddr(
971960 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
972961 try parent_atom.addReloc(elf_file.base.comp.gpa, .{
973962 .r_offset = reloc_info.offset,
974 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
963 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type,
975964 .r_addend = reloc_info.addend,
976965 }, self);
977966 },
978967 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
979968 .source_off = @intCast(reloc_info.offset),
980 .target_sym = @fromBackingInt(@intCast(sym_index)),
969 .target_sym = @fromBackingInt(@intCast(this_sym_index)),
981970 .target_off = reloc_info.addend,
982971 }),
983972 }
984 return @intCast(vaddr);
985973}
986974
987pub fn lowerUav(
975pub fn uavSymbol(
988976 self: *ZigObject,
989977 elf_file: *Elf,
990978 pt: Zcu.PerThread,
......@@ -1889,7 +1877,7 @@ pub fn updateExports(
18891877 break :blk self.navs.getPtr(nav).?;
18901878 },
18911879 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1892 _ = try self.lowerUav(elf_file, pt, uav, .none);
1880 _ = try self.uavSymbol(elf_file, pt, uav, .none);
18931881 break :blk self.uavs.getPtr(uav).?;
18941882 },
18951883 };
src/link/Elf2.zig+6-35
......@@ -4324,36 +4324,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.Fil
43244324 const s: Symbol.Id = .local(nmi.symbol(elf));
43254325 return s.toTypeErased();
43264326}
4327pub fn uavSymbol(
4328 elf: *Elf,
4329 uav_val: InternPool.Index,
4330 uav_align: InternPool.Alignment,
4331) link.Error!link.File.SymbolId {
4332 const diags = &elf.base.comp.link_diags;
4333 const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
4334 else => |e| return e,
4335 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
4336 };
4337 const s: Symbol.Id = .local(umi.symbol(elf));
4338 return s.toTypeErased();
4339}
4340pub fn getNavVAddr(
4341 elf: *Elf,
4342 pt: Zcu.PerThread,
4343 nav: InternPool.Nav.Index,
4344 reloc_info: link.File.RelocInfo,
4345) link.Error!u64 {
4346 _ = pt;
4347 return elf.getVAddr(reloc_info, try elf.navSymbol(nav));
4348}
4349pub fn getUavVAddr(
4350 elf: *Elf,
4351 uav_val: InternPool.Index,
4352 reloc_info: link.File.RelocInfo,
4353) link.Error!u64 {
4354 return elf.getVAddr(reloc_info, try elf.uavSymbol(uav_val, .none));
4355}
4356pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 {
4327pub fn relocSymAddr(elf: *Elf, reloc_info: link.File.RelocInfo) link.Error!void {
43574328 try elf.addReloc(
43584329 switch (reloc_info.parent) {
43594330 .none => unreachable,
......@@ -4361,13 +4332,12 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy
43614332 .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni),
43624333 },
43634334 reloc_info.offset,
4364 target,
4335 reloc_info.target,
43654336 reloc_info.addend,
43664337 .absAddr(elf),
43674338 );
4368 return Symbol.Id.fromTypeErased(target).value(elf);
43694339}
4370pub fn lowerUav(
4340pub fn uavSymbol(
43714341 elf: *Elf,
43724342 pt: Zcu.PerThread,
43734343 uav_val: InternPool.Index,
......@@ -7176,7 +7146,6 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
71767146 },
71777147 };
71787148 },
7179 .res => unreachable,
71807149 .dso => |dso| {
71817150 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
71827151 var fr = dso.file.reader(io, &buf);
......@@ -7193,6 +7162,8 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
71937162 },
71947163 };
71957164 },
7165 .res => unreachable,
7166 .tbd => unreachable,
71967167 }
71977168}
71987169fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
......@@ -12384,7 +12355,7 @@ pub fn printNode(
1238412355 mf_node.flags.alignment.toByteUnits(),
1238512356 mf_node.flags.position,
1238612357 if (mf_node.flags.bubbles_moved) " bubbles_moved" else "",
12387 if (mf_node.flags.resized) " moved" else "",
12358 if (mf_node.flags.moved) " moved" else "",
1238812359 if (mf_node.flags.resized) " resized" else "",
1238912360 if (mf_node.flags.enable_next_moved) " enable_next_moved" else "",
1239012361 if (mf_node.flags.next_moved) " next_moved" else "",
src/link/Lld.zig+5-1
......@@ -309,7 +309,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void {
309309
310310 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);
311311 for (comp.link_inputs) |input| switch (input) {
312 .dso, .archive => {}, // static archives should not contain shared libraries or other static archives
312 .dso, .tbd, .archive => {}, // static archives should not contain shared libraries or other static archives
313313 .res, .object => {
314314 const path = try input.path().toStringZ(arena);
315315 object_files.appendAssumeCapacity(path);
......@@ -561,6 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
561561 argv.appendAssumeCapacity(try obj.path.toString(arena));
562562 }
563563 },
564 .tbd => unreachable,
564565 };
565566
566567 for (comp.c_objects.items) |c_object| {
......@@ -1102,6 +1103,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11021103
11031104 for (base.comp.link_inputs) |link_input| switch (link_input) {
11041105 .res => unreachable, // Windows-only
1106 .tbd => unreachable, // Darwin-only
11051107 .dso => continue,
11061108 .object, .archive => |obj| {
11071109 if (obj.must_link and !whole_archive) {
......@@ -1163,6 +1165,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11631165
11641166 for (base.comp.link_inputs) |link_input| switch (link_input) {
11651167 .res => unreachable, // Windows-only
1168 .tbd => unreachable, // Darwin-only
11661169 .object, .archive => continue,
11671170 .dso => |dso| {
11681171 const lib_as_needed = !dso.needed;
......@@ -1596,6 +1599,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
15961599 .dso => |dso| {
15971600 try argv.append(try dso.path.toString(arena));
15981601 },
1602 .tbd => unreachable,
15991603 .res => unreachable,
16001604 };
16011605 if (whole_archive) {
src/link/MachO.zig+88-318
......@@ -4,6 +4,8 @@ pub const Relocation = @import("MachO/Relocation.zig");
44
55base: link.File,
66
7all_inputs: std.ArrayList(link.Input),
8
79rpath_list: []const []const u8,
810
911/// Debug symbols bundle (or dSym).
......@@ -104,9 +106,7 @@ has_tlv: AtomicBool = AtomicBool.init(false),
104106binds_to_weak: AtomicBool = AtomicBool.init(false),
105107weak_defines: AtomicBool = AtomicBool.init(false),
106108
107/// Options
108/// SDK layout
109sdk_layout: ?SdkLayout,
109// Options
110110/// Size of the __PAGEZERO segment.
111111pagezero_size: ?u64,
112112/// Minimum space for future expansion of the load commands.
......@@ -119,10 +119,6 @@ dead_strip_dylibs: bool,
119119undefined_treatment: UndefinedTreatment,
120120/// TODO: delete this, libraries need to be resolved by the frontend instead
121121lib_directories: []const Directory,
122/// Resolved list of framework search directories
123framework_dirs: []const []const u8,
124/// List of input frameworks
125frameworks: []const Framework,
126122/// Install name for the dylib.
127123/// TODO: unify with soname
128124install_name: ?[]const u8,
......@@ -132,7 +128,7 @@ compatibility_version: ?std.SemanticVersion,
132128/// Entry name
133129entry_name: ?[]const u8,
134130platform: Platform,
135sdk_version: ?std.SemanticVersion,
131sdk_version: ?link.DarwinSdkVersion,
136132/// When set to true, the linker will hoist all dylibs including system dependent dylibs.
137133no_implicit_dylibs: bool = false,
138134/// Whether the linker should parse and always force load objects containing ObjC in archives.
......@@ -190,13 +186,12 @@ pub fn createEmpty(
190186 .file = null,
191187 .build_id = options.build_id,
192188 },
189 .all_inputs = .empty,
193190 .rpath_list = options.rpath_list,
194191 .pagezero_size = options.pagezero_size,
195192 .headerpad_size = options.headerpad_size,
196193 .headerpad_max_install_names = options.headerpad_max_install_names,
197194 .dead_strip_dylibs = options.dead_strip_dylibs,
198 .sdk_layout = options.darwin_sdk_layout,
199 .frameworks = options.frameworks,
200195 .install_name = options.install_name,
201196 .entitlements = options.entitlements,
202197 .compatibility_version = options.compatibility_version,
......@@ -207,11 +202,10 @@ pub fn createEmpty(
207202 .named => |name| name,
208203 },
209204 .platform = Platform.fromTarget(target),
210 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,
205 .sdk_version = null,
211206 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
212207 // TODO delete this, directories must instead be resolved by the frontend
213208 .lib_directories = options.lib_directories,
214 .framework_dirs = options.framework_dirs,
215209 .force_load_objc = options.force_load_objc,
216210 .discard_local_symbols = options.discard_local_symbols,
217211 };
......@@ -271,6 +265,8 @@ pub fn deinit(self: *MachO) void {
271265 const gpa = comp.gpa;
272266 const io = comp.io;
273267
268 self.all_inputs.deinit(gpa);
269
274270 if (self.d_sym) |*d_sym| {
275271 d_sym.deinit();
276272 }
......@@ -334,6 +330,11 @@ pub fn deinit(self: *MachO) void {
334330 self.thunks.deinit(gpa);
335331}
336332
333pub fn loadInput(self: *MachO, input: link.Input) link.Error!void {
334 const gpa = self.base.comp.gpa;
335 try self.all_inputs.append(gpa, input);
336}
337
337338pub fn flush(
338339 self: *MachO,
339340 arena: Allocator,
......@@ -343,6 +344,8 @@ pub fn flush(
343344 const tracy = trace(@src());
344345 defer tracy.end();
345346
347 _ = arena;
348
346349 const comp = self.base.comp;
347350 const gpa = comp.gpa;
348351 const io = comp.io;
......@@ -351,146 +354,40 @@ pub fn flush(
351354 const sub_prog_node = prog_node.start("MachO Flush", 0);
352355 defer sub_prog_node.end();
353356
354 const zcu_obj_path: ?Path = p: {
355 const zcu = comp.zcu orelse break :p null;
356 const llvm_object = zcu.llvm_object orelse break :p null;
357 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
358 };
357 // TODO: due to https://codeberg.org/ziglang/zig/issues/36946, we receive link inputs in a
358 // non-deterministic order. To ensure that this linker behaves deterministically, sort the
359 // inputs into a consistent order now.
360 {
361 const InputSortContext = struct {
362 fn lessThan(ctx: @This(), lhs: link.Input, rhs: link.Input) bool {
363 _ = ctx;
364 // We can't sort by full path because that could vary between runs where we still
365 // want reproducibility (e.g. the path is affected by cwd). So instead, let's sort
366 // by basename---that's good enough for our purposes since we never link two
367 // vendored libraries/objects with the same name.
368 const lhs_name = fs.path.basename(lhs.path().sub_path);
369 const rhs_name = fs.path.basename(rhs.path().sub_path);
370 return std.mem.order(u8, lhs_name, rhs_name).compare(.lt);
371 }
372 };
373 std.mem.sort(
374 link.Input,
375 self.all_inputs.items,
376 @as(InputSortContext, .{}),
377 InputSortContext.lessThan,
378 );
379 }
359380
360381 // --verbose-link
361382 if (comp.verbose_link) try self.dumpArgv(comp);
362383
363384 if (self.getZigObject()) |zo| try zo.flush(self, tid);
364 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path);
365 if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path);
385 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp);
386 if (self.base.isObject()) return relocatable.flushObject(self, comp);
366387
367 var positionals = std.array_list.Managed(link.Input).init(gpa);
368 defer positionals.deinit();
369
370 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
371
372 for (comp.link_inputs) |link_input| switch (link_input) {
373 .dso => continue, // handled below
374 .object, .archive => positionals.appendAssumeCapacity(link_input),
375 .res => unreachable,
376 };
377
378 // This is a set of object files emitted by clang in a single `build-exe` invocation.
379 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
380 // in this set.
381 try positionals.ensureUnusedCapacity(comp.c_objects.items.len);
382 for (comp.c_objects.items) |c_object| {
383 positionals.appendAssumeCapacity(try link.openObjectInput(io, diags, c_object.status.success.object_path));
384 }
385
386 if (zcu_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path));
387
388 if (comp.config.any_sanitize_thread) {
389 try positionals.append(try link.openObjectInput(io, diags, comp.tsan_lib.?.full_object_path));
390 }
391
392 if (comp.config.any_fuzz) {
393 try positionals.append(try link.openArchiveInput(io, diags, comp.fuzzer_lib.?.full_object_path, false, false));
394 }
395
396 if (comp.ubsan_rt_lib) |crt_file| {
397 const path = crt_file.full_object_path;
398 self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err|
399 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
400 } else if (comp.ubsan_rt_obj) |crt_file| {
401 const path = crt_file.full_object_path;
402 self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err|
403 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
404 }
405
406 for (positionals.items) |link_input| {
407 self.classifyInputFile(link_input) catch |err|
408 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
409 }
410
411 var system_libs = std.array_list.Managed(SystemLib).init(gpa);
412 defer system_libs.deinit();
413
414 // frameworks
415 try system_libs.ensureUnusedCapacity(self.frameworks.len);
416 for (self.frameworks) |info| {
417 system_libs.appendAssumeCapacity(.{
418 .needed = info.needed,
419 .weak = info.weak,
420 .path = info.path,
421 });
422 }
423
424 // libc++ dep
425 if (comp.config.link_libcpp) {
426 try system_libs.ensureUnusedCapacity(2);
427 system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
428 system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
429 }
430
431 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
432 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
433
434 if (comp.config.link_libc and is_exe_or_dyn_lib) {
435 if (comp.zigc_static_lib) |zigc| {
436 const path = zigc.full_object_path;
437 self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err|
438 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
439 }
440 }
441
442 // libc/libSystem dep
443 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {
444 error.MissingLibSystem => {}, // already reported
445 else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}),
446 };
447
448 for (comp.link_inputs) |link_input| switch (link_input) {
449 .object, .archive => continue,
450 .res => unreachable,
451 .dso => {
452 self.classifyInputFile(link_input) catch |err|
453 diags.addParseError(link_input.path(), "failed to parse input file: {t}", .{err});
454 },
455 };
456
457 for (system_libs.items) |lib| {
458 switch (Compilation.classifyFileExt(lib.path.sub_path)) {
459 .shared_library => {
460 const dso_input = try link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport);
461 self.classifyInputFile(dso_input) catch |err|
462 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});
463 },
464 .static_library => {
465 const archive_input = try link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden);
466 self.classifyInputFile(archive_input) catch |err|
467 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});
468 },
469 else => {
470 dso: {
471 const dso_input = link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport) catch break :dso;
472 self.classifyInputFile(dso_input) catch break :dso;
473 continue;
474 }
475 ar: {
476 const archive_input = link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden) catch break :ar;
477 self.classifyInputFile(archive_input) catch break :ar;
478 continue;
479 }
480 diags.addParseError(lib.path, "unknown file extension", .{});
481 },
482 }
483 }
484
485 // Finally, link against compiler_rt.
486 if (comp.compiler_rt_lib) |crt_file| {
487 const path = crt_file.full_object_path;
488 self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err|
489 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
490 } else if (comp.compiler_rt_obj) |crt_file| {
491 const path = crt_file.full_object_path;
492 self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err|
493 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
388 for (self.all_inputs.items) |input| {
389 self.classifyInputFile(input) catch |err|
390 diags.addParseError(input.path(), "failed to read input file: {t}", .{err});
494391 }
495392
496393 try self.parseInputFiles();
......@@ -666,9 +563,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
666563 for (comp.link_inputs) |link_input| switch (link_input) {
667564 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
668565 .res => |res| try argv.append(try res.path.toString(arena)),
669 .dso => |dso| {
670 try argv.append(try dso.path.toString(arena));
671 },
566 .dso => |dso| try argv.append(try dso.path.toString(arena)),
567 .tbd => |tbd| try argv.append(try tbd.path.toString(arena)),
672568 };
673569
674570 for (comp.c_objects.items) |c_object| {
......@@ -694,12 +590,20 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
694590
695591 try argv.append("-platform_version");
696592 try argv.append(@tagName(self.platform.os_tag));
697 try argv.append(try std.fmt.allocPrint(arena, "{f}", .{self.platform.version}));
593 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{
594 self.platform.version.major,
595 self.platform.version.minor,
596 self.platform.version.patch,
597 }));
698598
699599 if (self.sdk_version) |ver| {
700600 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
701601 } else {
702 try argv.append(try std.fmt.allocPrint(arena, "{f}", .{self.platform.version}));
602 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{
603 self.platform.version.major,
604 self.platform.version.minor,
605 self.platform.version.patch,
606 }));
703607 }
704608
705609 if (comp.sysroot) |syslibroot| {
......@@ -754,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
754658 }
755659
756660 for (comp.link_inputs) |link_input| switch (link_input) {
757 .dso => continue, // handled below
661 .dso, .tbd => continue, // handled below
758662 .res => unreachable, // windows only
759663 .object, .archive => |obj| {
760664 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
......@@ -788,7 +692,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
788692 for (comp.link_inputs) |link_input| switch (link_input) {
789693 .object, .archive => continue, // handled above
790694 .res => unreachable, // windows only
791 .dso => |dso| {
695 inline .dso, .tbd => |dso| {
792696 try argv.ensureUnusedCapacity(2);
793697 if (dso.needed) {
794698 argv.appendAssumeCapacity("-needed-l");
......@@ -801,20 +705,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
801705 },
802706 };
803707
804 for (self.framework_dirs) |f_dir| {
708 for (comp.framework_dirs) |f_dir| {
805709 try argv.append("-F");
806 try argv.append(f_dir);
807 }
808
809 for (self.frameworks) |framework| {
810 const name = framework.path.stem();
811 const arg = if (framework.needed)
812 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
813 else if (framework.weak)
814 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
815 else
816 try std.fmt.allocPrint(arena, "-framework {s}", .{name});
817 try argv.append(arg);
710 try argv.append(f_dir.path orelse ".");
818711 }
819712
820713 if (comp.config.link_libcpp) {
......@@ -836,46 +729,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
836729 try Compilation.dumpArgv(io, argv.items);
837730}
838731
839/// TODO delete this, libsystem must be resolved when setting up the compilation pipeline
840pub fn resolveLibSystem(
841 self: *MachO,
842 arena: Allocator,
843 comp: *Compilation,
844 out_libs: anytype,
845) !void {
846 const io = comp.io;
847 const diags = &comp.link_diags;
848
849 var test_path = std.array_list.Managed(u8).init(arena);
850 var checked_paths = std.array_list.Managed([]const u8).init(arena);
851
852 success: {
853 if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {
854 .sdk => {
855 const dir = try fs.path.join(arena, &.{ comp.sysroot.?, "usr", "lib" });
856 if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success;
857 },
858 .vendored => {
859 const dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "darwin" });
860 if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success;
861 },
862 };
863
864 for (self.lib_directories) |directory| {
865 if (try accessLibPath(arena, io, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success;
866 }
867
868 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
869 return error.MissingLibSystem;
870 }
871
872 const libsystem_path = Path.initCwd(try arena.dupe(u8, test_path.items));
873 try out_libs.append(.{
874 .needed = true,
875 .path = libsystem_path,
876 });
877}
878
879732pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
880733 const tracy = trace(@src());
881734 defer tracy.end();
......@@ -888,7 +741,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
888741 log.debug("classifying input file {f}", .{path});
889742
890743 const fh = try self.addFileHandle(file);
891 var buffer: [Archive.SARMAG]u8 = undefined;
744 var buffer: [macho.ARMAG.len]u8 = undefined;
892745
893746 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
894747 const offset = if (fat_arch) |fa| fa.offset else 0;
......@@ -903,7 +756,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
903756 return;
904757 }
905758 if (readArMagic(io, file, offset, &buffer) catch null) |ar_magic| blk: {
906 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
759 if (!mem.eql(u8, ar_magic, macho.ARMAG)) break :blk;
907760 try self.addArchive(input.archive, fh, fat_arch);
908761 return;
909762 }
......@@ -933,10 +786,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6
933786 return hdr;
934787}
935788
936pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 {
789pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[macho.ARMAG.len]u8) ![]const u8 {
937790 const nread = try file.readPositionalAll(io, buffer, offset);
938791 if (nread != buffer.len) return error.InputOutput;
939 return buffer[0..Archive.SARMAG];
792 return buffer[0..macho.ARMAG.len];
940793}
941794
942795fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {
......@@ -1159,10 +1012,6 @@ fn parseDependentDylibs(self: *MachO) !void {
11591012 const comp = self.base.comp;
11601013 const gpa = comp.gpa;
11611014 const io = comp.io;
1162 const framework_dirs = self.framework_dirs;
1163
1164 // TODO delete this, directories must instead be resolved by the frontend
1165 const lib_directories = self.lib_directories;
11661015
11671016 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
11681017 defer arena_alloc.deinit();
......@@ -1197,14 +1046,14 @@ fn parseDependentDylibs(self: *MachO) !void {
11971046 const stem = fs.path.stem(id.name);
11981047
11991048 // Framework
1200 for (framework_dirs) |dir| {
1049 for (comp.framework_dirs) |dir| {
12011050 test_path.clearRetainingCapacity();
1202 if (try accessFrameworkPath(arena, io, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
1051 if (try accessFrameworkPath(arena, io, &test_path, &checked_paths, dir.path orelse ".", stem)) break :full_path test_path.items;
12031052 }
12041053
12051054 // Library
12061055 const lib_name = eatPrefix(stem, "lib") orelse stem;
1207 for (lib_directories) |lib_directory| {
1056 for (self.lib_directories) |lib_directory| {
12081057 test_path.clearRetainingCapacity();
12091058 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
12101059 }
......@@ -3124,21 +2973,21 @@ pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {
31242973 return self.getZigObject().?.freeNav(nav);
31252974}
31262975
3127pub fn getNavVAddr(self: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
3128 return self.getZigObject().?.getNavVAddr(self, pt, nav_index, reloc_info);
2976pub fn navSymbol(self: *MachO, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId {
2977 return self.getZigObject().?.navSymbol(self, nav);
31292978}
31302979
3131pub fn lowerUav(
2980pub fn relocSymAddr(self: *MachO, reloc_info: link.File.RelocInfo) link.Error!void {
2981 return self.getZigObject().?.relocSymAddr(self, reloc_info);
2982}
2983
2984pub fn uavSymbol(
31322985 self: *MachO,
31332986 pt: Zcu.PerThread,
31342987 uav: InternPool.Index,
31352988 explicit_alignment: InternPool.Alignment,
31362989) !link.File.SymbolId {
3137 return self.getZigObject().?.lowerUav(self, pt, uav, explicit_alignment);
3138}
3139
3140pub fn getUavVAddr(self: *MachO, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
3141 return self.getZigObject().?.getUavVAddr(self, uav, reloc_info);
2990 return self.getZigObject().?.uavSymbol(self, pt, uav, explicit_alignment);
31422991}
31432992
31442993pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
......@@ -4231,7 +4080,7 @@ pub const null_sym = macho.nlist_64{
42314080pub const Platform = struct {
42324081 os_tag: std.Target.Os.Tag,
42334082 abi: std.Target.Abi,
4234 version: std.SemanticVersion,
4083 version: link.DarwinSdkVersion,
42354084
42364085 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
42374086 /// the extracted minimum platform version.
......@@ -4258,7 +4107,7 @@ pub const Platform = struct {
42584107 => .simulator,
42594108 else => .none,
42604109 },
4261 .version = appleVersionToSemanticVersion(cmd.minos),
4110 .version = @fromBackingInt(cmd.minos),
42624111 };
42634112 },
42644113 .VERSION_MIN_IPHONEOS,
......@@ -4277,7 +4126,7 @@ pub const Platform = struct {
42774126 else => unreachable,
42784127 },
42794128 .abi = .none,
4280 .version = appleVersionToSemanticVersion(cmd.version),
4129 .version = @fromBackingInt(cmd.version),
42814130 };
42824131 },
42834132 else => unreachable,
......@@ -4285,17 +4134,18 @@ pub const Platform = struct {
42854134 }
42864135
42874136 pub fn fromTarget(target: *const std.Target) Platform {
4137 const semver = target.os.version_range.semver.min;
42884138 return .{
42894139 .os_tag = target.os.tag,
42904140 .abi = target.abi,
4291 .version = target.os.version_range.semver.min,
4141 .version = .{
4142 .major = @intCast(semver.major),
4143 .minor = @intCast(semver.minor),
4144 .patch = @intCast(semver.patch),
4145 },
42924146 };
42934147 }
42944148
4295 pub fn toAppleVersion(plat: Platform) u32 {
4296 return semanticVersionToAppleVersion(plat.version);
4297 }
4298
42994149 pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
43004150 return switch (plat.os_tag) {
43014151 .driverkit => .DRIVERKIT,
......@@ -4312,7 +4162,7 @@ pub const Platform = struct {
43124162 pub fn isBuildVersionCompatible(plat: Platform) bool {
43134163 inline for (supported_platforms) |sup_plat| {
43144164 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
4315 return sup_plat[2] <= plat.toAppleVersion();
4165 return sup_plat[2] <= @backingInt(plat.version);
43164166 }
43174167 }
43184168 return false;
......@@ -4321,7 +4171,7 @@ pub const Platform = struct {
43214171 pub fn isVersionMinCompatible(plat: Platform) bool {
43224172 inline for (supported_platforms) |sup_plat| {
43234173 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
4324 return sup_plat[3] <= plat.toAppleVersion();
4174 return sup_plat[3] <= @backingInt(plat.version);
43254175 }
43264176 }
43274177 return false;
......@@ -4380,86 +4230,8 @@ const supported_platforms = [_]SupportedPlatforms{
43804230};
43814231// zig fmt: on
43824232
4383pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
4384 const major = version.major;
4385 const minor = version.minor;
4386 const patch = version.patch;
4387 return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
4388}
4389
4390pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
4391 return .{
4392 .major = @as(u16, @truncate(version >> 16)),
4393 .minor = @as(u8, @truncate(version >> 8)),
4394 .patch = @as(u8, @truncate(version)),
4395 };
4396}
4397
4398fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersion {
4399 const gpa = comp.gpa;
4400
4401 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
4402 defer arena_allocator.deinit();
4403 const arena = arena_allocator.allocator();
4404
4405 const io = comp.io;
4406
4407 const sdk_dir = switch (sdk_layout) {
4408 .sdk => comp.sysroot.?,
4409 .vendored => fs.path.join(arena, &.{ comp.dirs.zig_lib.path.?, "libc", "darwin" }) catch return null,
4410 };
4411 if (readSdkVersionFromSettings(arena, io, sdk_dir)) |ver| {
4412 return parseSdkVersion(ver);
4413 } else |_| {
4414 // Read from settings should always succeed when vendored.
4415 // TODO: convert to fatal linker error
4416 if (sdk_layout == .vendored) @panic("zig installation bug: unable to parse SDK version");
4417 }
4418
4419 // infer from pathname
4420 const stem = fs.path.stem(sdk_dir);
4421 const start = for (stem, 0..) |c, i| {
4422 if (std.ascii.isDigit(c)) break i;
4423 } else stem.len;
4424 const end = for (stem[start..], start..) |c, i| {
4425 if (std.ascii.isDigit(c) or c == '.') continue;
4426 break i;
4427 } else stem.len;
4428 return parseSdkVersion(stem[start..end]);
4429}
4430
4431// Official Apple SDKs ship with a `SDKSettings.json` located at the top of SDK fs layout.
4432// Use property `MinimalDisplayName` to determine version.
4433// The file/property is also available with vendored libc.
4434fn readSdkVersionFromSettings(arena: Allocator, io: Io, dir: []const u8) ![]const u8 {
4435 const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" });
4436 const contents = try Io.Dir.cwd().readFileAlloc(io, sdk_path, arena, .limited(std.math.maxInt(u16)));
4437 const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
4438 if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
4439 return error.SdkVersionFailure;
4440}
4441
4442// Versions reported by Apple aren't exactly semantically valid as they usually omit
4443// the patch component, so we parse SDK value by hand.
4444fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion {
4445 var parsed: std.SemanticVersion = .{
4446 .major = 0,
4447 .minor = 0,
4448 .patch = 0,
4449 };
4450
4451 const parseNext = struct {
4452 fn parseNext(it: anytype) ?u16 {
4453 const nn = it.next() orelse return null;
4454 return std.fmt.parseInt(u16, nn, 10) catch null;
4455 }
4456 }.parseNext;
4457
4458 var it = std.mem.splitAny(u8, raw, ".");
4459 parsed.major = parseNext(&it) orelse return null;
4460 parsed.minor = parseNext(&it) orelse return null;
4461 parsed.patch = parseNext(&it) orelse 0;
4462 return parsed;
4233pub fn setDarwinSdkVersion(self: *MachO, version: link.DarwinSdkVersion) link.Error!void {
4234 self.sdk_version = version;
44634235}
44644236
44654237/// When allocating, the ideal_capacity is calculated by
......@@ -4497,7 +4269,7 @@ const SystemLib = struct {
44974269 .must_link = obj.must_link,
44984270 .hidden = obj.hidden,
44994271 },
4500 .dso => |dso| .{
4272 inline .dso, .tbd => |dso| .{
45014273 .path = dso.path,
45024274 .needed = dso.needed,
45034275 .weak = dso.weak,
......@@ -4507,8 +4279,6 @@ const SystemLib = struct {
45074279 }
45084280};
45094281
4510pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
4511
45124282const UndefinedTreatment = enum {
45134283 @"error",
45144284 warn,
src/link/MachO/Archive.zig+4-15
......@@ -17,7 +17,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
1717 const offset = if (fat_arch) |ar| ar.offset else 0;
1818 const end_pos = if (fat_arch) |ar| offset + ar.size else (try handle.stat(io)).size;
1919
20 var pos: usize = offset + SARMAG;
20 var pos: usize = offset + macho.ARMAG.len;
2121 while (true) {
2222 if (pos >= end_pos) break;
2323 if (!mem.isAligned(pos, 2)) pos += 1;
......@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
3030 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
3131 pos += @sizeOf(ar_hdr);
3232
33 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
33 if (!mem.eql(u8, &hdr.ar_fmag, macho.ARFMAG)) {
3434 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{
35 std.ascii.hexEscape(ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower),
35 std.ascii.hexEscape(macho.ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower),
3636 });
3737 }
3838
......@@ -116,17 +116,6 @@ pub fn writeHeader(
116116 }
117117}
118118
119// Archive files start with the ARMAG identifying string. Then follows a
120// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
121// member indicates, for each member file.
122/// String that begins an archive file.
123pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
124/// Size of that string.
125pub const SARMAG: u4 = 8;
126
127/// String in ar_fmag at the end of each header.
128const ARFMAG: *const [2:0]u8 = "`\n";
129
130119pub const SYMDEF = "__.SYMDEF";
131120pub const SYMDEF64 = "__.SYMDEF_64";
132121pub const SYMDEF_SORTED = "__.SYMDEF SORTED";
......@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {
146135 /// File size, in ASCII decimal.
147136 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,
148137 /// Always contains ARFMAG.
149 ar_fmag: [2]u8 = ARFMAG.*,
138 ar_fmag: [2]u8 = macho.ARFMAG.*,
150139
151140 fn date(self: ar_hdr) !u64 {
152141 const value = mem.trimEnd(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});
src/link/MachO/ZigObject.zig+28-46
......@@ -611,60 +611,43 @@ pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.E
611611 assert(!self.debug_strtab_dirty);
612612}
613613
614pub fn getNavVAddr(
615 self: *ZigObject,
614pub fn navSymbol(
615 zo: *ZigObject,
616616 macho_file: *MachO,
617 pt: Zcu.PerThread,
618617 nav_index: InternPool.Nav.Index,
619 reloc_info: link.File.RelocInfo,
620) !u64 {
621 const zcu = pt.zcu;
618) link.Error!link.File.SymbolId {
619 const zcu = macho_file.base.comp.zcu.?;
622620 const ip = &zcu.intern_pool;
623621 const nav = ip.getNav(nav_index);
624 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
625 const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
626 macho_file,
627 nav.name.toSlice(ip),
628 @"extern".lib_name.toSlice(ip),
629 ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
630 const sym = self.symbols.items[sym_index];
631 const vaddr = sym.getAddress(.{}, macho_file);
632 switch (reloc_info.parent) {
633 .none => unreachable,
634 .atom_index => |atom_index| {
635 const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?;
636 try parent_atom.addReloc(macho_file, .{
637 .tag = .@"extern",
638 .offset = @intCast(reloc_info.offset),
639 .target = sym_index,
640 .addend = reloc_info.addend,
641 .type = .unsigned,
642 .meta = .{
643 .pcrel = false,
644 .has_subtractor = false,
645 .length = 3,
646 .symbolnum = @intCast(sym.nlist_idx),
647 },
648 });
649 },
650 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
651 .source_off = @intCast(reloc_info.offset),
652 .target_sym = @fromBackingInt(@intCast(sym_index)),
653 .target_off = reloc_info.addend,
654 }),
622 if (nav.getExtern(ip)) |@"extern"| {
623 const sym_index = try zo.getGlobalSymbol(
624 macho_file,
625 nav.name.toSlice(ip),
626 @"extern".lib_name.toSlice(ip),
627 );
628 if (@"extern".linkage == .weak) {
629 zo.symbols.items[sym_index].flags.weak = true;
630 }
631 if (nav.resolved.?.@"threadlocal") {
632 zo.symbols.items[sym_index].flags.tlv = true;
633 }
634 return @fromBackingInt(sym_index);
635 } else {
636 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
637 if (nav.resolved.?.@"threadlocal") {
638 zo.symbols.items[sym_index].flags.tlv = true;
639 }
640 return @fromBackingInt(sym_index);
655641 }
656 return vaddr;
657642}
658643
659pub fn getUavVAddr(
644pub fn relocSymAddr(
660645 self: *ZigObject,
661646 macho_file: *MachO,
662 uav: InternPool.Index,
663647 reloc_info: link.File.RelocInfo,
664) !u64 {
665 const sym_index = self.uavs.get(uav).?.symbol_index;
648) !void {
649 const sym_index = @backingInt(reloc_info.target);
666650 const sym = self.symbols.items[sym_index];
667 const vaddr = sym.getAddress(.{}, macho_file);
668651 switch (reloc_info.parent) {
669652 .none => unreachable,
670653 .atom_index => |atom_index| {
......@@ -689,10 +672,9 @@ pub fn getUavVAddr(
689672 .target_off = reloc_info.addend,
690673 }),
691674 }
692 return vaddr;
693675}
694676
695pub fn lowerUav(
677pub fn uavSymbol(
696678 self: *ZigObject,
697679 macho_file: *MachO,
698680 pt: Zcu.PerThread,
......@@ -1281,7 +1263,7 @@ pub fn updateExports(
12811263 break :blk self.navs.getPtr(nav).?;
12821264 },
12831265 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1284 _ = try self.lowerUav(macho_file, pt, uav, .none);
1266 _ = try self.uavSymbol(macho_file, pt, uav, .none);
12851267 break :blk self.uavs.getPtr(uav).?;
12861268 },
12871269 };
src/link/MachO/load_commands.zig+7-12
......@@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator;
99const DebugSymbols = @import("DebugSymbols.zig");
1010const Dylib = @import("Dylib.zig");
1111const MachO = @import("../MachO.zig");
12const link = @import("../../link.zig");
1213
1314pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1415
......@@ -296,7 +297,7 @@ pub fn writeRpathLC(rpath: []const u8, writer: *Writer) !void {
296297 }
297298}
298299
299pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: *Writer) !void {
300pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?link.DarwinSdkVersion, writer: *Writer) !void {
300301 const cmd: macho.LC = switch (platform.os_tag) {
301302 .macos => .VERSION_MIN_MACOSX,
302303 .ios, .maccatalyst => .VERSION_MIN_IPHONEOS,
......@@ -306,24 +307,18 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer
306307 };
307308 try writer.writeAll(mem.asBytes(&macho.version_min_command{
308309 .cmd = cmd,
309 .version = platform.toAppleVersion(),
310 .sdk = if (sdk_version) |ver|
311 MachO.semanticVersionToAppleVersion(ver)
312 else
313 platform.toAppleVersion(),
310 .version = @backingInt(platform.version),
311 .sdk = @backingInt(sdk_version orelse platform.version),
314312 }));
315313}
316314
317pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: *Writer) !void {
315pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?link.DarwinSdkVersion, writer: *Writer) !void {
318316 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
319317 try writer.writeStruct(@as(macho.build_version_command, .{
320318 .cmdsize = cmdsize,
321319 .platform = platform.toApplePlatform(),
322 .minos = platform.toAppleVersion(),
323 .sdk = if (sdk_version) |ver|
324 MachO.semanticVersionToAppleVersion(ver)
325 else
326 platform.toAppleVersion(),
320 .minos = @backingInt(platform.version),
321 .sdk = @backingInt(sdk_version orelse platform.version),
327322 .ntools = 1,
328323 }), .little);
329324 try writer.writeAll(mem.asBytes(&macho.build_tool_version{
src/link/MachO/relocatable.zig+31-48
......@@ -1,25 +1,25 @@
1pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void {
2 const gpa = comp.gpa;
1pub fn flushObject(macho_file: *MachO, comp: *Compilation) link.Error!void {
32 const io = comp.io;
43 const diags = &comp.link_diags;
54
6 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
7 var positionals = std.array_list.Managed(link.Input).init(gpa);
8 defer positionals.deinit();
9 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
10 positionals.appendSliceAssumeCapacity(comp.link_inputs);
11
12 for (comp.c_objects.items) |c_object| {
13 try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path));
14 }
15
16 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path));
17
18 if (macho_file.getZigObject() == null and positionals.items.len == 1) {
5 one_input: {
6 if (macho_file.getZigObject() != null) {
7 break :one_input;
8 }
9 var only_input: ?link.Input = null;
10 for (macho_file.all_inputs.items) |input| switch (input) {
11 .res => unreachable,
12 .dso, .tbd => {},
13 .object, .archive => if (only_input == null) {
14 only_input = input;
15 } else {
16 break :one_input;
17 },
18 };
1919 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
2020 // debug info segments/sections (this is apparently by design by Apple), we copy
2121 // the *only* input file over.
22 const path = positionals.items[0].path();
22 const path = (only_input orelse break :one_input).path();
2323 const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err|
2424 return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });
2525 const stat = in_file.stat(io) catch |err|
......@@ -29,10 +29,12 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
2929 return;
3030 }
3131
32 for (positionals.items) |link_input| {
33 macho_file.classifyInputFile(link_input) catch |err|
34 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
35 }
32 for (macho_file.all_inputs.items) |input| switch (input) {
33 .res => unreachable,
34 .dso, .tbd => {},
35 .object, .archive => macho_file.classifyInputFile(input) catch |err|
36 diags.addParseError(input.path(), "failed to read input file: {t}", .{err}),
37 };
3638
3739 if (diags.hasErrors()) return error.AlreadyReported;
3840
......@@ -75,35 +77,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
7577 try writeHeader(macho_file, ncmds, sizeofcmds);
7678}
7779
78pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void {
80pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void {
7981 const gpa = comp.gpa;
80 const io = comp.io;
8182 const diags = &macho_file.base.comp.link_diags;
8283
83 var positionals = std.array_list.Managed(link.Input).init(gpa);
84 defer positionals.deinit();
85
86 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
87 positionals.appendSliceAssumeCapacity(comp.link_inputs);
88
89 for (comp.c_objects.items) |c_object| {
90 try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path));
91 }
92
93 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path));
94
95 if (comp.compiler_rt_strat == .obj) {
96 try positionals.append(try link.openObjectInput(io, diags, comp.compiler_rt_obj.?.full_object_path));
97 }
98
99 if (comp.ubsan_rt_strat == .obj) {
100 try positionals.append(try link.openObjectInput(io, diags, comp.ubsan_rt_obj.?.full_object_path));
101 }
102
103 for (positionals.items) |link_input| {
104 macho_file.classifyInputFile(link_input) catch |err|
105 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
106 }
84 for (macho_file.all_inputs.items) |input| switch (input) {
85 .res => unreachable,
86 .dso, .tbd => {},
87 .object, .archive => macho_file.classifyInputFile(input) catch |err|
88 diags.addParseError(input.path(), "failed to read input file: {t}", .{err}),
89 };
10790
10891 if (diags.hasErrors()) return error.AlreadyReported;
10992
......@@ -168,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
168151
169152 // Update file offsets of contributing objects
170153 const total_size: usize = blk: {
171 var pos: usize = Archive.SARMAG;
154 var pos: usize = macho.ARMAG.len;
172155 pos += @sizeOf(Archive.ar_hdr);
173156 pos += Archive.SYMDEF.len + 1;
174157 pos = mem.alignForward(usize, pos, 8);
......@@ -211,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
211194 var writer: Writer = .fixed(buffer);
212195
213196 // Write magic
214 writer.writeAll(Archive.ARMAG) catch unreachable;
197 writer.writeAll(macho.ARMAG) catch unreachable;
215198
216199 // Write symtab
217200 ar_symtab.write(format, macho_file, &writer) catch |err|
src/link/MachO2.zig created+1357
......@@ -0,0 +1,1357 @@
1const MachO = @This();
2
3base: link.File,
4options: link.File.OpenOptions,
5
6const_prog_node: std.Progress.Node,
7synth_prog_node: std.Progress.Node,
8input_prog_node: std.Progress.Node,
9
10mf: MappedFile,
11nodes: std.MultiArrayList(Node),
12
13node: struct {
14 macho: MappedFile.Node.Index,
15 header: MappedFile.Node.Index,
16 symtab: MappedFile.Node.Index,
17 strtab: MappedFile.Node.Index,
18},
19
20lc: struct {
21 offsets: std.ArrayList(u32),
22 pagezero: LoadCommand,
23 symtab: LoadCommand,
24 entry_point: ?LoadCommand,
25},
26/// Does not contain an entry for the `__PAGEZERO` segment.
27segments: std.MultiArrayList(struct {
28 lc: LoadCommand,
29 node: MappedFile.Node.Index,
30}),
31sections: std.MultiArrayList(struct {
32 lc: LoadCommand,
33 node: MappedFile.Node.Index,
34 first_symbol: Symbol.Index.Optional,
35 index: u8,
36}),
37/// Accessed with `Segment.NameAdapter` using `[]const u8` keys.
38segments_by_name: std.array_hash_map.Custom(void, void, void, true),
39/// Accessed with `Section.NameAdapter` using `Section.NameAdapter.SegmentAndSection`.
40sections_by_name: std.array_hash_map.Custom(void, void, void, true),
41
42/// Accessed with `String.Adapter` using `[]const u8` keys.
43strtab: std.array_hash_map.Custom(String, void, void, true),
44
45symtab: std.ArrayList(Symbol),
46
47sections_changed_index: std.array_hash_map.Auto(Section, void),
48
49const Error = link.Error || error{MappedFileIo};
50
51/// Stable reference to a particular load command (or `std.macho.section_64`, despite those not
52/// technically being load commands). Underlying value is an index into `MachO.lc.offsets`.
53const LoadCommand = enum(u32) {
54 _,
55 fn offset(lc: LoadCommand, macho: *const MachO) u32 {
56 return macho.lc.offsets.items[@backingInt(lc)];
57 }
58 fn ptrConst(lc: LoadCommand, macho: *const MachO, comptime Cmd: type) *align(8) const Cmd {
59 return @ptrCast(@alignCast(
60 macho.node.header.sliceConst(&macho.mf)[lc.offset(macho)..][0..@sizeOf(Cmd)],
61 ));
62 }
63 fn ptr(lc: LoadCommand, macho: *MachO, comptime Cmd: type) *align(8) Cmd {
64 return @ptrCast(@alignCast(
65 macho.node.header.slice(&macho.mf)[lc.offset(macho)..][0..@sizeOf(Cmd)],
66 ));
67 }
68};
69
70/// Stable reference to a segment defined by a `std.macho.segment_command_64` load command.
71///
72/// This type is backed by `u8` instead of a larger type because Mach-O itself uses 8-bit indices to
73/// refer to sections, so the limitation exists regardless.
74const Segment = enum(u8) {
75 _,
76
77 fn lc(seg: Segment, macho: *const MachO) LoadCommand {
78 return macho.segments.items(.lc)[@backingInt(seg)];
79 }
80 fn lcPtrConst(seg: Segment, macho: *const MachO) *const std.macho.segment_command_64 {
81 return seg.lc(macho).ptrConst(macho, std.macho.segment_command_64);
82 }
83 fn lcPtr(seg: Segment, macho: *MachO) *std.macho.segment_command_64 {
84 return seg.lc(macho).ptr(macho, std.macho.segment_command_64);
85 }
86
87 fn name(seg: Segment, macho: *const MachO) []const u8 {
88 return std.mem.sliceTo(&seg.lcPtrConst(macho).segname, 0);
89 }
90
91 fn vaddr(seg: Segment, macho: *const MachO) u64 {
92 return macho.targetLoad(&seg.lcPtrConst(macho).vmaddr);
93 }
94
95 fn node(seg: Segment, macho: *const MachO) MappedFile.Node.Index {
96 return macho.segments.items(.node)[@backingInt(seg)];
97 }
98
99 const NameAdapter = struct {
100 macho: *const MachO,
101 pub fn eql(ctx: NameAdapter, a_seg_name: []const u8, _: void, b_seg_raw: usize) bool {
102 const b_seg: Segment = @fromBackingInt(@intCast(b_seg_raw));
103 return std.mem.eql(u8, a_seg_name, b_seg.name(ctx.macho));
104 }
105 pub fn hash(ctx: NameAdapter, a_seg_name: []const u8) u32 {
106 _ = ctx;
107 return std.array_hash_map.hashString(a_seg_name);
108 }
109 };
110};
111
112/// Stable reference to a section defined by a `std.macho.section_64` in the load commands.
113///
114/// This type is backed by `u8` instead of a larger type because Mach-O itself uses 8-bit indices to
115/// refer to sections, so the limitation exists regardless.
116///
117/// However, the backing integer of a `Section` is *not* the same as the section index, because
118/// section indices can change. For the index of a section, see `Section.index`.
119const Section = enum(u8) {
120 _,
121
122 fn lc(sect: Section, macho: *const MachO) LoadCommand {
123 return macho.sections.items(.lc)[@backingInt(sect)];
124 }
125 fn lcPtrConst(sect: Section, macho: *const MachO) *const std.macho.section_64 {
126 return sect.lc(macho).ptrConst(macho, std.macho.section_64);
127 }
128 fn lcPtr(sect: Section, macho: *MachO) *std.macho.section_64 {
129 return sect.lc(macho).ptr(macho, std.macho.section_64);
130 }
131
132 fn name(sect: Section, macho: *const MachO) []const u8 {
133 return std.mem.sliceTo(&sect.lcPtrConst(macho).sectname, 0);
134 }
135
136 fn segmentName(sect: Section, macho: *const MachO) []const u8 {
137 return std.mem.sliceTo(&sect.lcPtrConst(macho).segname, 0);
138 }
139
140 fn node(sect: Section, macho: *const MachO) MappedFile.Node.Index {
141 return macho.sections.items(.node)[@backingInt(sect)];
142 }
143
144 /// Returns this section's index in the Mach-O file. Section indices are implicitly assigned
145 /// based on the order of load commands, so this value may change in response to segments or
146 /// sections being added. When this value changes for a given `Section`, it is added to
147 /// `MachO.sections_changed_index`, so that references to the section index can be updated.
148 fn index(sect: Section, macho: *const MachO) u8 {
149 return macho.sections.items(.index)[@backingInt(sect)];
150 }
151
152 /// Returns the head of a linked list of symbols defined relative to this section.
153 fn firstSymbol(sect: Section, macho: *MachO) *Symbol.Index.Optional {
154 return &macho.sections.items(.first_symbol)[@backingInt(sect)];
155 }
156
157 const NameAdapter = struct {
158 macho: *const MachO,
159 const SegmentAndSection = struct {
160 segment: []const u8,
161 section: []const u8,
162 };
163 pub fn eql(ctx: NameAdapter, a_name: SegmentAndSection, _: void, b_sect_raw: usize) bool {
164 const b_sect: Section = @fromBackingInt(@intCast(b_sect_raw));
165 return std.mem.eql(u8, a_name.segment, b_sect.segmentName(ctx.macho)) and
166 std.mem.eql(u8, a_name.section, b_sect.name(ctx.macho));
167 }
168 pub fn hash(ctx: NameAdapter, a_name: SegmentAndSection) u32 {
169 _ = ctx;
170 var h: std.hash.Wyhash = .init(a_name.section.len);
171 h.update(a_name.section);
172 h.update(a_name.segment);
173 return @truncate(h.final());
174 }
175 };
176};
177
178const Symbol = struct {
179 next_in_section: Symbol.Index.Optional,
180
181 const Index = enum(u32) {
182 _,
183 fn ptr(i: Index, macho: *MachO) *Symbol {
184 return &macho.symtab.items[@backingInt(i)];
185 }
186 fn nlist(i: Index, macho: *MachO) *std.macho.nlist_64 {
187 return &macho.nlistSlice()[@backingInt(i)];
188 }
189 const Optional = enum(u32) {
190 none = std.math.maxInt(u32),
191 _,
192 fn unwrap(o: Optional) ?Index {
193 return switch (o) {
194 .none => null,
195 _ => @fromBackingInt(@backingInt(o)),
196 };
197 }
198 fn wrap(i: Index) Optional {
199 return @bitCast(i);
200 }
201 };
202 };
203};
204
205const Node = union(enum) {
206 macho,
207 /// Contains the `mach_header_64` structure and all load commands. Inside of the `.segment` node for the `__TEXT` segment.
208 macho_header,
209
210 symtab,
211 strtab,
212
213 segment: Segment,
214 section: Section,
215};
216fn getNode(macho: *const MachO, ni: MappedFile.Node.Index) Node {
217 return macho.nodes.get(@backingInt(ni));
218}
219
220pub fn open(
221 arena: Allocator,
222 comp: *Compilation,
223 path: std.Build.Cache.Path,
224 options: link.File.OpenOptions,
225) !*MachO {
226 return create(arena, comp, path, options);
227}
228pub fn createEmpty(
229 arena: Allocator,
230 comp: *Compilation,
231 path: std.Build.Cache.Path,
232 options: link.File.OpenOptions,
233) !*MachO {
234 return create(arena, comp, path, options);
235}
236fn create(
237 arena: Allocator,
238 comp: *Compilation,
239 path: std.Build.Cache.Path,
240 options: link.File.OpenOptions,
241) !*MachO {
242 const io = comp.io;
243
244 const macho = try arena.create(MachO);
245 const file = try path.root_dir.handle.createFile(io, path.sub_path, .{
246 .read = true,
247 .permissions = link.File.determinePermissions(comp.config.output_mode, comp.config.link_mode),
248 });
249
250 macho.* = .{
251 .base = .{
252 .tag = .macho2,
253
254 .comp = comp,
255 .emit = path,
256
257 .file = file,
258 .gc_sections = false,
259 .print_gc_sections = false,
260 .build_id = .none,
261 .allow_shlib_undefined = false,
262 .stack_size = 0,
263 },
264 .options = options,
265
266 .const_prog_node = .none,
267 .synth_prog_node = .none,
268 .input_prog_node = .none,
269
270 .mf = try .init(file, comp.gpa, io),
271 .nodes = .empty,
272
273 .node = undefined,
274 .lc = .{
275 .offsets = .empty,
276 .pagezero = undefined,
277 .symtab = undefined,
278 .entry_point = null,
279 },
280 .segments = .empty,
281 .sections = .empty,
282 .segments_by_name = .empty,
283 .sections_by_name = .empty,
284
285 .strtab = .empty,
286 .symtab = .empty,
287
288 .sections_changed_index = .empty,
289 };
290 errdefer macho.deinit();
291
292 try macho.initInner();
293
294 return macho;
295}
296fn initInner(macho: *MachO) !void {
297 const comp = macho.base.comp;
298 const gpa = comp.gpa;
299
300 macho.node.macho = .root;
301 try macho.nodes.append(gpa, .macho);
302
303 const arch: enum { aarch64, x86_64 } = switch (comp.getTarget().cpu.arch) {
304 .aarch64 => .aarch64,
305 .x86_64 => .x86_64,
306 else => |arch| std.debug.panic("TODO: unsupported target arch {t}", .{arch}),
307 };
308
309 const mach_header_64 = std.macho.mach_header_64;
310
311 // The `__TEXT` segment is special because it contains the mach header and load commands, so we
312 // must create the text segment node before we can actually call `addSegment`.
313 const text_seg_node = try macho.node.macho.addOnlyHeaderChild(gpa, &macho.mf, .{
314 .alignment = macho.mf.flags.block_size.max(macho.targetPageAlign()),
315 .moved = true, // required by `addSegment`
316 .bubbles_moved = false,
317 });
318 try macho.nodes.append(gpa, .{ .segment = undefined }); // populated by `addSegment`
319
320 macho.node.header = try text_seg_node.addOnlyHeaderChild(gpa, &macho.mf, .{
321 .size = @sizeOf(mach_header_64),
322 .alignment = .@"8",
323 });
324 try macho.nodes.append(gpa, .macho_header);
325
326 const mach_header: *mach_header_64 = @ptrCast(@alignCast(
327 macho.node.header.slice(&macho.mf)[0..@sizeOf(mach_header_64)],
328 ));
329 mach_header.* = .{
330 .cputype = switch (arch) {
331 .aarch64 => std.macho.CPU_TYPE_ARM64,
332 .x86_64 => std.macho.CPU_TYPE_X86_64,
333 },
334 .cpusubtype = switch (arch) {
335 .aarch64 => std.macho.CPU_SUBTYPE_ARM_ALL,
336 .x86_64 => std.macho.CPU_SUBTYPE_X86_64_ALL,
337 },
338 .filetype = switch (comp.config.output_mode) {
339 .Exe => std.macho.MH_EXECUTE,
340 .Lib => switch (comp.config.link_mode) {
341 .static => std.macho.MH_OBJECT,
342 .dynamic => std.macho.MH_DYLIB,
343 },
344 .Obj => std.macho.MH_OBJECT,
345 },
346 .ncmds = 0,
347 .sizeofcmds = 0,
348 .flags = std.macho.MH_DYLDLINK | std.macho.MH_PIE,
349 };
350 if (macho.targetEndian() != std.lang.Endian.native) {
351 std.mem.byteSwapAllFields(mach_header_64, mach_header);
352 }
353
354 // The "__LINKEDIT" segment is special in that it needs to be at the end of the Mach-O file, so
355 // like "__TEXT", we'll construct its node here instead of in `addSegment`.
356 const linkedit_seg_node = try macho.node.macho.addFooterChildBefore(gpa, &macho.mf, .none, .{
357 .alignment = macho.mf.flags.block_size.max(macho.targetPageAlign()),
358 .moved = true, // required by `addSegment`
359 .bubbles_moved = false,
360 });
361 try macho.nodes.append(gpa, .{ .segment = undefined }); // populated by `addSegment`
362
363 // The "__PAGEZERO" segment must come before any other segment load commands. It is inaccessible
364 // and occupies 4 GiB (apparently to catch invalid 32-bit pointer accesses).
365 macho.lc.pagezero = try macho.appendSimpleLoadCommand(std.macho.segment_command_64, .{
366 .cmdsize = @sizeOf(std.macho.segment_command_64),
367 .segname = "__PAGEZERO\x00\x00\x00\x00\x00\x00".*,
368 .vmaddr = 0,
369 .vmsize = 0x1_0000_0000,
370 .fileoff = 0,
371 .filesize = 0,
372 .maxprot = .{},
373 .initprot = .{},
374 .nsects = 0,
375 .flags = 0,
376 });
377 const text_seg = try macho.addSegment(.{
378 .name = "__TEXT",
379 .prot = .{ .READ = true, .EXEC = true },
380 .existing_node = text_seg_node,
381 });
382 const data_seg = try macho.addSegment(.{
383 .name = "__DATA",
384 .prot = .{ .READ = true, .WRITE = true },
385 });
386 const linkedit_seg = try macho.addSegment(.{
387 .name = "__LINKEDIT",
388 .prot = .{ .READ = true },
389 .existing_node = linkedit_seg_node,
390 });
391
392 _ = try macho.addSection(.{
393 .segment = text_seg,
394 .name = "__text",
395 .@"align" = .@"4",
396 .flags = .{ .some_instructions = true },
397 });
398 _ = try macho.addSection(.{
399 .segment = text_seg,
400 .name = "__const",
401 .@"align" = .@"2",
402 .flags = .{},
403 });
404 _ = try macho.addSection(.{
405 .segment = data_seg,
406 .name = "__data",
407 .@"align" = .@"2",
408 .flags = .{},
409 });
410
411 macho.node.symtab = try linkedit_seg.node(macho).addFloatingChild(gpa, &macho.mf, .{
412 .alignment = macho.mf.flags.block_size.max(.of(std.macho.nlist_64)),
413 .moved = true, // so that `symoff` in the symtab load command will be set
414 });
415 try macho.nodes.append(gpa, .symtab);
416
417 macho.node.strtab = try linkedit_seg.node(macho).addFloatingChild(gpa, &macho.mf, .{
418 .alignment = macho.mf.flags.block_size,
419 .moved = true, // so that `stroff` in the symtab load command will be set
420 });
421 try macho.nodes.append(gpa, .strtab);
422
423 try macho.node.strtab.ensureMinimumSize(gpa, &macho.mf, 1);
424 macho.node.strtab.slice(&macho.mf)[0] = 0;
425
426 _ = try macho.appendSimpleLoadCommand(std.macho.dyld_info_command, .{});
427
428 macho.lc.symtab = try macho.appendSimpleLoadCommand(std.macho.symtab_command, .{
429 .symoff = 0,
430 .nsyms = 0,
431 .stroff = 0,
432 .strsize = 1, // empty string (null terminator)
433 });
434
435 const want_dyld_path: bool = switch (comp.config.output_mode) {
436 .Obj => false,
437 .Lib => switch (comp.config.link_mode) {
438 .static => false,
439 .dynamic => comp.root_mod.resolved_target.is_explicit_dynamic_linker,
440 },
441 .Exe => true,
442 };
443 if (want_dyld_path) {
444 if (comp.getTarget().dynamic_linker.get()) |dyld_path| {
445 const cmd = try macho.appendLoadCommand(std.macho.dylinker_command, .{
446 .cmd = .LOAD_DYLINKER,
447 .cmdsize = std.mem.alignForward(u32, @intCast(
448 @sizeOf(std.macho.dylinker_command) + dyld_path.len + 1,
449 ), 8),
450 .name = @sizeOf(std.macho.dylinker_command),
451 });
452 @memcpy(cmd.trailing[0..dyld_path.len], dyld_path);
453 @memset(cmd.trailing[dyld_path.len..], 0);
454 }
455 }
456
457 switch (comp.config.output_mode) {
458 .Obj, .Lib => {},
459 .Exe => macho.lc.entry_point = try macho.appendSimpleLoadCommand(std.macho.entry_point_command, .{
460 .entryoff = 0,
461 .stacksize = macho.options.stack_size orelse 0,
462 }),
463 }
464
465 _ = try macho.appendSimpleLoadCommand(std.macho.uuid_command, .{
466 .uuid = @splat(0),
467 });
468
469 try macho.addSimpleSymbol("some_function", 0x1234, "__TEXT", "__text");
470 try macho.addSimpleSymbol("my_cool_data", 0x5678, "__DATA", "__data");
471
472 try macho.updateSegmentLoadAddresses();
473}
474/// TODO: this function doesn't actually do anything particularly useful, it's just a placeholder
475/// for actual symbol creation logic so that we can test adding stuff to the symtab.
476fn addSimpleSymbol(
477 macho: *MachO,
478 name: []const u8,
479 value: u64,
480 segment_name: []const u8,
481 section_name: []const u8,
482) Error!void {
483 const gpa = macho.base.comp.gpa;
484 try macho.symtab.ensureUnusedCapacity(gpa, 1);
485 try macho.node.symtab.ensureMinimumSize(gpa, &macho.mf, (macho.symtab.items.len + 1) * @sizeOf(std.macho.nlist_64));
486
487 const strx = try macho.string(name);
488 const section = macho.sectionByName(segment_name, section_name).?;
489
490 const symtab_ptr = macho.lc.symtab.ptr(macho, std.macho.symtab_command);
491 assert(macho.targetLoad(&symtab_ptr.nsyms) == macho.symtab.items.len);
492 macho.targetStore(&symtab_ptr.nsyms, @intCast(macho.symtab.items.len + 1));
493
494 const sym_index: Symbol.Index = @fromBackingInt(@intCast(macho.symtab.items.len));
495 macho.symtab.appendAssumeCapacity(.{
496 .next_in_section = section.firstSymbol(macho).*,
497 });
498 section.firstSymbol(macho).* = .wrap(sym_index);
499
500 sym_index.nlist(macho).* = .{
501 .n_strx = @backingInt(strx),
502 .n_type = .{ .bits = .{
503 .ext = true,
504 .type = .sect,
505 .pext = false,
506 .is_stab = 0,
507 } },
508 .n_sect = section.index(macho),
509 .n_desc = .{
510 .arm_thumb_def = false,
511 .referenced_dynamically = false,
512 .discarded_or_no_dead_strip = false,
513 .weak_ref = false,
514 .weak_def_or_ref_to_weak = false,
515 .symbol_resolver = false,
516 .alt_entry = false,
517 },
518 .n_value = value,
519 };
520 if (macho.targetEndian() != std.lang.Endian.native) {
521 std.mem.byteSwapAllFields(std.macho.nlist_64, sym_index.nlist(macho));
522 }
523}
524
525fn addSegment(macho: *MachO, opts: struct {
526 name: []const u8,
527 prot: std.macho.vm_prot_t,
528 /// The caller is allowed to create the section's node themselves, in which case it is passed in
529 /// here. The caller must initialize the `MachO.nodes` entry to `.{ .segment = undefined }`, and
530 /// the node must be created with the option `.moved = true`.
531 existing_node: ?MappedFile.Node.Index = null,
532}) Error!Segment {
533 const gpa = macho.base.comp.gpa;
534
535 const segment_command_64 = std.macho.segment_command_64;
536
537 // We limit the number of segments to 255, because there can only be 255 *sections*, so there's
538 // no need to allow more. See corresponding check in `addSection`.
539 if (macho.segments.len == 255) {
540 return macho.base.comp.link_diags.fail("maximum segment count exceeded", .{});
541 }
542
543 try macho.nodes.ensureUnusedCapacity(gpa, 1);
544 try macho.segments.ensureUnusedCapacity(gpa, 1);
545 try macho.segments_by_name.ensureUnusedCapacity(gpa, 1);
546
547 const root_ni: MappedFile.Node.Index = .root;
548
549 const lc = try macho.appendSimpleLoadCommand(std.macho.segment_command_64, .{
550 .cmdsize = @sizeOf(segment_command_64),
551 .segname = name: {
552 var name: [16]u8 = undefined;
553 @memcpy(name[0..opts.name.len], opts.name);
554 @memset(name[opts.name.len..], 0);
555 break :name name;
556 },
557 .vmaddr = 0,
558 .vmsize = 0,
559 .fileoff = 0, // to be set by `flushMoved`
560 .filesize = 0,
561 .maxprot = opts.prot,
562 .initprot = opts.prot,
563 .nsects = 0,
564 .flags = 0,
565 });
566
567 const segment: Segment = @fromBackingInt(@intCast(macho.segments.len));
568 const segment_node: MappedFile.Node.Index = node: {
569 if (opts.existing_node) |node| {
570 assert(node.hasMoved(&macho.mf)); // so that `fileoff` will be set
571 assert(macho.nodes.items(.tags)[@backingInt(node)] == .segment);
572 macho.nodes.set(@backingInt(node), .{ .segment = segment });
573 break :node node;
574 }
575 const node = try root_ni.addFloatingChild(gpa, &macho.mf, .{
576 .alignment = macho.mf.flags.block_size.max(macho.targetPageAlign()),
577 .moved = true, // so that `fileoff` will be set
578 .bubbles_moved = false,
579 });
580 macho.nodes.appendAssumeCapacity(.{ .segment = segment });
581 break :node node;
582 };
583 macho.segments.appendAssumeCapacity(.{
584 .lc = lc,
585 .node = segment_node,
586 });
587
588 {
589 const gop = macho.segments_by_name.getOrPutAssumeCapacityAdapted(
590 opts.name,
591 @as(Segment.NameAdapter, .{ .macho = macho }),
592 );
593 assert(!gop.found_existing);
594 assert(gop.index == @backingInt(segment));
595 }
596
597 return segment;
598}
599
600const String = enum(u32) {
601 empty = 0,
602 _,
603 fn slice(s: String, macho: *const MachO) [:0]const u8 {
604 const overlong = macho.node.strtab.slice(&macho.mf)[@backingInt(s)..];
605 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
606 }
607
608 const Adapter = struct {
609 macho: *const MachO,
610 pub fn eql(ctx: Adapter, lhs_slice: []const u8, rhs: String, _: usize) bool {
611 const macho = ctx.macho;
612 return std.mem.eql(u8, lhs_slice, rhs.slice(macho));
613 }
614 pub fn hash(ctx: Adapter, s: []const u8) u32 {
615 _ = ctx;
616 return std.array_hash_map.hashString(s);
617 }
618 };
619};
620
621fn string(macho: *MachO, slice: []const u8) Error!String {
622 const gpa = macho.base.comp.gpa;
623
624 const old_strsize = macho.targetLoad(
625 &macho.lc.symtab.ptr(macho, std.macho.symtab_command).strsize,
626 );
627 try macho.node.strtab.ensureMinimumSize(gpa, &macho.mf, old_strsize + slice.len + 1);
628
629 try macho.strtab.ensureUnusedCapacity(gpa, 1);
630
631 errdefer comptime unreachable;
632
633 const adapter: String.Adapter = .{ .macho = macho };
634 const gop = macho.strtab.getOrPutAssumeCapacityAdapted(slice, adapter);
635 if (!gop.found_existing) {
636 macho.targetStore(
637 &macho.lc.symtab.ptr(macho, std.macho.symtab_command).strsize,
638 @intCast(old_strsize + slice.len + 1),
639 );
640 gop.key_ptr.* = @fromBackingInt(old_strsize);
641 const dest_slice = macho.node.strtab.slice(&macho.mf)[old_strsize..];
642 @memcpy(dest_slice[0..slice.len], slice);
643 dest_slice[slice.len] = 0;
644 }
645 return gop.key_ptr.*;
646}
647
648fn nlistSlice(macho: *MachO) []std.macho.nlist_64 {
649 const nsyms = macho.targetLoad(
650 &macho.lc.symtab.ptr(macho, std.macho.symtab_command).nsyms,
651 );
652 return @ptrCast(@alignCast(
653 macho.node.symtab.slice(&macho.mf)[0 .. nsyms * @sizeOf(std.macho.nlist_64)],
654 ));
655}
656
657fn updateSegmentLoadAddresses(macho: *MachO) Allocator.Error!void {
658 // TODO: this segment load address allocation logic is bad because it moves segments quite
659 // frequently. We should avoid that by introducing padding between segments so that they have
660 // space to grow in-place. We could also consider allowing a segment which can't fit to jump
661 // over another segment, like in `Elf2.allocateSegmentLoadAddress`.
662 var cur_addr: u64 = start_addr: {
663 const pagezero_ptr = macho.lc.pagezero.ptr(macho, std.macho.segment_command_64);
664 assert(macho.targetLoad(&pagezero_ptr.vmaddr) == 0);
665 break :start_addr macho.targetLoad(&pagezero_ptr.vmsize);
666 };
667 for (0..macho.segments.len) |segment_raw| {
668 const segment: Segment = @fromBackingInt(@intCast(segment_raw));
669
670 _, const size: u64 = segment.node(macho).location(&macho.mf).resolve(&macho.mf);
671 const cmd_ptr = segment.lcPtr(macho);
672
673 macho.targetStore(&cmd_ptr.vmsize, size);
674
675 const old_addr = macho.targetLoad(&cmd_ptr.vmaddr);
676 if (old_addr >= cur_addr) {
677 cur_addr = old_addr;
678 } else {
679 macho.targetStore(&cmd_ptr.vmaddr, cur_addr);
680 try segment.node(macho).childrenMoved(macho.base.comp.gpa, &macho.mf);
681 }
682
683 cur_addr = macho.targetPageAlign().forward(cur_addr + size);
684 }
685}
686
687/// Like `appendLoadCommand`, but asserts that `cmd.cmdsize` exactly equals `@sizeOf(Cmd)` and does
688/// not return the (empty) trailing slice.
689fn appendSimpleLoadCommand(
690 macho: *MachO,
691 comptime Cmd: type,
692 cmd: Cmd,
693) Error!LoadCommand {
694 comptime assert(@sizeOf(Cmd) % 8 == 0);
695 assert(cmd.cmdsize == @sizeOf(Cmd));
696 return (try macho.appendLoadCommand(Cmd, cmd)).lc;
697}
698
699/// Asserts that the first two fields of `Cmd` are `cmd: LC` and `cmdsize: u32`, and that the value
700/// of `lc.cmdsize` is 8-byte aligned.
701fn appendLoadCommand(
702 macho: *MachO,
703 comptime Cmd: type,
704 cmd: Cmd,
705) Error!struct {
706 lc: LoadCommand,
707 trailing: []u8,
708} {
709 const gpa = macho.base.comp.gpa;
710
711 try macho.lc.offsets.ensureUnusedCapacity(gpa, 1);
712
713 comptime {
714 const s = @typeInfo(Cmd).@"struct";
715 assert(s.layout == .@"extern");
716 // First field of load command should be `cmd: LC`
717 assert(std.mem.eql(u8, s.field_names[0], "cmd"));
718 assert(s.field_types[0] == std.macho.LC);
719 // Second field of load command should be `cmdsize: u32`
720 assert(std.mem.eql(u8, s.field_names[1], "cmdsize"));
721 assert(s.field_types[1] == u32);
722 }
723
724 const cmd_size = @as(*const std.macho.load_command, @ptrCast(&cmd)).cmdsize;
725 assert(cmd_size % 8 == 0);
726
727 const mach_header_64 = std.macho.mach_header_64;
728 const old_cmds_size: u32 = size: {
729 const mach_header: *const mach_header_64 = @ptrCast(@alignCast(
730 macho.node.header.sliceConst(&macho.mf)[0..@sizeOf(mach_header_64)],
731 ));
732 break :size macho.targetLoad(&mach_header.sizeofcmds);
733 };
734 const new_cmds_size = old_cmds_size + cmd_size;
735 try macho.node.header.ensureMinimumSize(gpa, &macho.mf, @sizeOf(mach_header_64) + new_cmds_size);
736
737 const header_slice = macho.node.header.slice(&macho.mf);
738 const mach_header: *mach_header_64 = @ptrCast(@alignCast(
739 header_slice[0..@sizeOf(mach_header_64)],
740 ));
741 macho.targetStore(&mach_header.sizeofcmds, new_cmds_size);
742 macho.targetStore(&mach_header.ncmds, macho.targetLoad(&mach_header.ncmds) + 1);
743
744 const cmd_slice: []align(8) u8 = @alignCast(
745 header_slice[@sizeOf(mach_header_64) + old_cmds_size ..][0..cmd_size],
746 );
747
748 const cmd_ptr: *Cmd = @ptrCast(cmd_slice[0..@sizeOf(Cmd)]);
749 cmd_ptr.* = cmd;
750 if (macho.targetEndian() != std.lang.Endian.native) {
751 std.mem.byteSwapAllFields(Cmd, cmd_ptr);
752 }
753
754 const lc: LoadCommand = @fromBackingInt(@intCast(macho.lc.offsets.items.len));
755 macho.lc.offsets.appendAssumeCapacity(@sizeOf(mach_header_64) + old_cmds_size);
756
757 return .{ .lc = lc, .trailing = cmd_slice[@sizeOf(Cmd)..] };
758}
759
760fn addSection(macho: *MachO, opts: struct {
761 name: []const u8,
762 segment: Segment,
763 @"align": MappedFile.Alignment,
764 /// This matches the layout of `std.macho.section_64.flags`.
765 flags: packed struct(u32) {
766 _unused0: u10 = 0,
767 some_instructions: bool = false,
768 _unused1: u21 = 0,
769 },
770}) Error!Section {
771 const gpa = macho.base.comp.gpa;
772
773 const mach_header_64 = std.macho.mach_header_64;
774 const segment_command_64 = std.macho.segment_command_64;
775 const section_64 = std.macho.section_64;
776
777 // Check if we can actually fit another section. The limit is 255 because Mach-O implicitly
778 // assigns 8-bit indices to sections starting from 1 (0 is reserved).
779 if (macho.sections.len == 255) {
780 return macho.base.comp.link_diags.fail("maximum section count exceeded", .{});
781 }
782
783 const old_cmds_size: u32 = size: {
784 const mach_header: *const mach_header_64 = @ptrCast(@alignCast(
785 macho.node.header.sliceConst(&macho.mf)[0..@sizeOf(mach_header_64)],
786 ));
787 break :size macho.targetLoad(&mach_header.sizeofcmds);
788 };
789 const new_cmds_size = old_cmds_size + @sizeOf(section_64);
790 try macho.node.header.ensureMinimumSize(gpa, &macho.mf, @sizeOf(mach_header_64) + new_cmds_size);
791
792 try macho.nodes.ensureUnusedCapacity(gpa, 1);
793 try macho.lc.offsets.ensureUnusedCapacity(gpa, 1);
794 try macho.sections.ensureUnusedCapacity(gpa, 1);
795 try macho.sections_by_name.ensureUnusedCapacity(gpa, 1);
796
797 const section: Section = @fromBackingInt(@intCast(macho.sections.len));
798 const section_node = try opts.segment.node(macho).addFloatingChild(gpa, &macho.mf, .{
799 .alignment = opts.@"align",
800 .moved = true, // so that `offset` will be set
801 });
802 macho.nodes.appendAssumeCapacity(.{ .section = section });
803 macho.sections.appendAssumeCapacity(.{
804 .lc = @fromBackingInt(@intCast(macho.lc.offsets.items.len)),
805 .node = section_node,
806 .first_symbol = .none,
807 .index = undefined, // populated later
808 });
809
810 {
811 const adapter: Section.NameAdapter = .{ .macho = macho };
812 const key: Section.NameAdapter.SegmentAndSection = .{
813 .segment = opts.segment.name(macho),
814 .section = opts.name,
815 };
816 const gop = macho.sections_by_name.getOrPutAssumeCapacityAdapted(key, adapter);
817 assert(!gop.found_existing);
818 assert(gop.index == @backingInt(section));
819 }
820
821 const header_slice = macho.node.header.slice(&macho.mf);
822
823 {
824 const mach_header: *mach_header_64 = @ptrCast(@alignCast(
825 header_slice[0..@sizeOf(mach_header_64)],
826 ));
827 macho.targetStore(&mach_header.sizeofcmds, new_cmds_size);
828 }
829
830 const seg_cmd = opts.segment.lcPtr(macho);
831 const old_nsects = macho.targetLoad(&seg_cmd.nsects);
832 const old_cmdsize = macho.targetLoad(&seg_cmd.cmdsize);
833 assert(old_cmdsize == @sizeOf(segment_command_64) + old_nsects * @sizeOf(section_64));
834 const new_cmdsize = old_cmdsize + @sizeOf(section_64);
835 macho.targetStore(&seg_cmd.cmdsize, new_cmdsize);
836 macho.targetStore(&seg_cmd.nsects, old_nsects + 1);
837
838 const seg_cmd_off = opts.segment.lc(macho).offset(macho);
839 const offset = seg_cmd_off + old_cmdsize;
840
841 // Shift forward every load command which comes after this segment command, to make space
842 // for the new `section_64` (and update the offsets in `macho.lc.offsets` accordingly).
843 @memmove(
844 header_slice[seg_cmd_off + new_cmdsize .. @sizeOf(mach_header_64) + new_cmds_size],
845 header_slice[seg_cmd_off + old_cmdsize .. @sizeOf(mach_header_64) + old_cmds_size],
846 );
847 for (macho.lc.offsets.items) |*other_off| {
848 if (other_off.* >= offset) {
849 other_off.* += @sizeOf(section_64);
850 }
851 }
852 // Only now do we append our *own* offset (otherwise the above loop would have changed it!).
853 macho.lc.offsets.appendAssumeCapacity(offset);
854
855 // Increment the index of every section after where we've inserted ourselves.
856 {
857 var new_section_index: u8 = 1;
858 try macho.sections_changed_index.ensureUnusedCapacity(gpa, macho.sections.len);
859 for (0..macho.sections.len) |other_section_raw| {
860 const other_section: Section = @fromBackingInt(@intCast(other_section_raw));
861 if (other_section == section) continue;
862 if (other_section.lc(macho).offset(macho) > offset) {
863 macho.sections.items(.index)[@backingInt(other_section)] += 1;
864 if (other_section.firstSymbol(macho).* != .none) {
865 macho.sections_changed_index.putAssumeCapacity(other_section, {});
866 }
867 } else {
868 new_section_index += 1;
869 }
870 }
871 macho.sections.items(.index)[@backingInt(section)] = new_section_index;
872 }
873
874 // Write the new `section_64`.
875 const section_cmd: *section_64 = @ptrCast(@alignCast(
876 header_slice[offset..][0..@sizeOf(section_64)],
877 ));
878 section_cmd.* = .{
879 .sectname = name: {
880 var name: [16]u8 = undefined;
881 @memcpy(name[0..opts.name.len], opts.name);
882 @memset(name[opts.name.len..], 0);
883 break :name name;
884 },
885 .segname = seg_cmd.segname,
886 .addr = 0,
887 .size = 0,
888 .offset = 0,
889 .@"align" = opts.@"align".toLog2Units(),
890 .reloff = 0,
891 .nreloc = 0,
892 .flags = @bitCast(opts.flags),
893 };
894 if (macho.targetEndian() != std.lang.Endian.native) {
895 std.mem.byteSwapAllFields(section_64, section_cmd);
896 }
897
898 return section;
899}
900
901fn segmentByName(macho: *const MachO, name: []const u8) ?Segment {
902 const adapter: Segment.NameAdapter = .{ .macho = macho };
903 if (macho.segments_by_name.getIndexAdapted(name, adapter)) |segment_raw| {
904 return @fromBackingInt(@intCast(segment_raw));
905 } else {
906 return null;
907 }
908}
909
910fn sectionByName(macho: *const MachO, segment_name: []const u8, section_name: []const u8) ?Section {
911 const adapter: Section.NameAdapter = .{ .macho = macho };
912 const key: Section.NameAdapter.SegmentAndSection = .{
913 .segment = segment_name,
914 .section = section_name,
915 };
916 if (macho.sections_by_name.getIndexAdapted(key, adapter)) |section_raw| {
917 return @fromBackingInt(@intCast(section_raw));
918 } else {
919 return null;
920 }
921}
922
923fn targetEndian(macho: *const MachO) std.lang.Endian {
924 _ = macho;
925 return .little;
926}
927fn targetPageAlign(macho: *const MachO) MappedFile.Alignment {
928 _ = macho;
929 return .fromByteUnits(0x4000);
930}
931fn targetLoad(macho: *const MachO, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
932 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
933 const Child = pointer_ty.child;
934 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
935 return switch (@typeInfo(Child)) {
936 else => @compileError(@typeName(Child)),
937 .int => std.mem.toNative(Child, ptr.*, macho.targetEndian()),
938 .@"enum" => |@"enum"| @fromBackingInt(macho.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))),
939 .@"struct" => |@"struct"| @bitCast(
940 macho.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
941 ),
942 };
943}
944fn targetStore(macho: *const MachO, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
945 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
946 const Child = pointer_ty.child;
947 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
948 return switch (@typeInfo(Child)) {
949 else => @compileError(@typeName(Child)),
950 .int => ptr.* = std.mem.nativeTo(Child, val, macho.targetEndian()),
951 .@"enum" => |@"enum"| macho.targetStore(
952 @as(*align(alignment) @"enum".tag_type, @ptrCast(ptr)),
953 @backingInt(val),
954 ),
955 .@"struct" => |@"struct"| macho.targetStore(
956 @as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr)),
957 @bitCast(val),
958 ),
959 };
960}
961
962pub fn deinit(macho: *MachO) void {
963 const gpa = macho.base.comp.gpa;
964 macho.mf.deinit(gpa);
965 macho.nodes.deinit(gpa);
966 macho.segments.deinit(gpa);
967 macho.sections.deinit(gpa);
968 macho.segments_by_name.deinit(gpa);
969 macho.sections_by_name.deinit(gpa);
970 macho.strtab.deinit(gpa);
971 macho.symtab.deinit(gpa);
972 macho.sections_changed_index.deinit(gpa);
973 macho.* = undefined;
974}
975
976pub fn startProgress(macho: *MachO, prog_node: std.Progress.Node) void {
977 prog_node.increaseEstimatedTotalItems(4);
978 macho.const_prog_node = prog_node.start("Constants", 0);
979 macho.synth_prog_node = prog_node.start("Synthetics", 0);
980 macho.input_prog_node = prog_node.start("Inputs", 0);
981 macho.mf.update_prog_node = prog_node.start("Relocations", macho.mf.updates.items.len);
982}
983pub fn endProgress(macho: *MachO) void {
984 macho.const_prog_node.end();
985 macho.const_prog_node = .none;
986 macho.synth_prog_node.end();
987 macho.synth_prog_node = .none;
988 macho.input_prog_node.end();
989 macho.input_prog_node = .none;
990 macho.mf.update_prog_node.end();
991 macho.mf.update_prog_node = .none;
992}
993
994pub fn navSymbol(macho: *MachO, nav_id: InternPool.Nav.Index) link.Error!link.File.SymbolId {
995 _ = macho;
996 _ = nav_id;
997 @panic("TODO");
998}
999pub fn uavSymbol(
1000 macho: *MachO,
1001 pt: Zcu.PerThread,
1002 uav_val: InternPool.Index,
1003 uav_align: InternPool.Alignment,
1004) link.Error!link.File.SymbolId {
1005 _ = macho;
1006 _ = pt;
1007 _ = uav_val;
1008 _ = uav_align;
1009 @panic("TODO");
1010}
1011pub fn relocSymAddr(
1012 macho: *MachO,
1013 reloc_info: link.File.RelocInfo,
1014) link.Error!void {
1015 _ = macho;
1016 _ = reloc_info;
1017 @panic("TODO");
1018}
1019
1020pub fn loadInput(macho: *MachO, input: link.Input) link.Error!void {
1021 const diags = &macho.base.comp.link_diags;
1022 macho.loadInputInner(input) catch |err| switch (err) {
1023 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{macho.mf.io_err.?}),
1024 else => |e| return e,
1025 };
1026}
1027fn loadInputInner(macho: *MachO, input: link.Input) Error!void {
1028 switch (input) {
1029 .res => unreachable,
1030 .object => @panic("TODO(MachO2): load object"),
1031 .archive => @panic("TODO(MachO2): load archive"),
1032 .dso => @panic("TODO(MachO2): load dso"),
1033 .tbd => |tbd| {
1034 log.debug("load tbd {qf}", .{tbd.path.fmtEscapeString()});
1035
1036 // TODO: obviously this is completely wrong, we need to parse the tbd to determine the
1037 // dylib install name; but for now this lets us link libSystem
1038 const prefix = "/usr/lib/";
1039 const name = std.fs.path.stem(tbd.path.sub_path);
1040 const suffix = ".dylib";
1041
1042 const cmd = try macho.appendLoadCommand(std.macho.dylib_command, .{
1043 .cmd = .LOAD_DYLIB,
1044 .cmdsize = std.mem.alignForward(u32, @intCast(
1045 @sizeOf(std.macho.dylib_command) + prefix.len + name.len + suffix.len + 1,
1046 ), 8),
1047 .dylib = .{
1048 .name = @sizeOf(std.macho.dylib_command),
1049 .timestamp = 0,
1050 .current_version = 0,
1051 .compatibility_version = 0,
1052 },
1053 });
1054 @memcpy(cmd.trailing[0..prefix.len], prefix);
1055 @memcpy(cmd.trailing[prefix.len..][0..name.len], name);
1056 @memcpy(cmd.trailing[prefix.len + name.len ..][0..suffix.len], suffix);
1057 @memset(cmd.trailing[prefix.len + name.len + suffix.len ..], 0);
1058 },
1059 }
1060}
1061pub fn setDarwinSdkVersion(macho: *MachO, sdk_version: link.DarwinSdkVersion) link.Error!void {
1062 const comp = macho.base.comp;
1063 const diags = &comp.link_diags;
1064 const target = comp.getTarget();
1065
1066 const os_min_version: link.DarwinSdkVersion = .{
1067 .major = @intCast(target.os.version_range.semver.min.major),
1068 .minor = @intCast(target.os.version_range.semver.min.minor),
1069 .patch = @intCast(target.os.version_range.semver.min.patch),
1070 };
1071
1072 _ = macho.appendSimpleLoadCommand(std.macho.version_min_command, .{
1073 .cmd = switch (target.os.tag) {
1074 .macos => .VERSION_MIN_MACOSX,
1075 .ios => .VERSION_MIN_IPHONEOS,
1076 .tvos => .VERSION_MIN_TVOS,
1077 .watchos => .VERSION_MIN_WATCHOS,
1078 .maccatalyst, .driverkit, .visionos => |os_tag| std.debug.panic(
1079 "TODO(MachO2): sdk version for '{t}'",
1080 .{os_tag},
1081 ),
1082 else => unreachable,
1083 },
1084 .version = @backingInt(os_min_version),
1085 .sdk = @backingInt(sdk_version),
1086 }) catch |err| switch (err) {
1087 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{macho.mf.io_err.?}),
1088 else => |e| return e,
1089 };
1090}
1091pub fn prelink(macho: *MachO, prog_node: std.Progress.Node) link.Error!void {
1092 _ = macho;
1093 _ = prog_node;
1094}
1095pub fn flush(
1096 macho: *MachO,
1097 arena: Allocator,
1098 tid: Zcu.PerThread.Id,
1099 prog_node: std.Progress.Node,
1100) link.Error!void {
1101 const diags = &macho.base.comp.link_diags;
1102 _ = arena;
1103 _ = prog_node;
1104
1105 try macho.updateSegmentLoadAddresses();
1106 while (try macho.idle(tid)) {}
1107
1108 if (macho.lc.entry_point) |entry_point_lc| {
1109 // TODO: actually find the entry point.
1110 // For now, we just set the entry point to the start of the "__TEXT,__text" section.
1111 const entry_node = macho.sectionByName("__TEXT", "__text").?.node(macho);
1112 const entry_file_off: u64, _ = entry_node.location(&macho.mf).resolve(&macho.mf);
1113 const entry_point_ptr = entry_point_lc.ptr(macho, std.macho.entry_point_command);
1114 macho.targetStore(&entry_point_ptr.entryoff, @intCast(entry_file_off));
1115 }
1116
1117 macho.mf.flush() catch |err| switch (err) {
1118 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{macho.mf.io_err.?}),
1119 else => |e| return e,
1120 };
1121
1122 if (macho.options.enable_link_snapshots)
1123 macho.dumpStderr(tid) catch |err|
1124 return diags.fail("dumping link snapshot failed: {t}", .{err});
1125}
1126pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void {
1127 _ = macho;
1128 _ = pt;
1129 @panic("TODO");
1130}
1131pub fn idle(macho: *MachO, tid: Zcu.PerThread.Id) link.Error!bool {
1132 _ = tid;
1133
1134 macho.mf.nodes_lock.lock();
1135 defer macho.mf.nodes_lock.unlock();
1136
1137 task: {
1138 if (macho.sections_changed_index.pop()) |kv| {
1139 const section = kv.key;
1140 var sym_it = section.firstSymbol(macho).*;
1141 while (sym_it.unwrap()) |sym| : (sym_it = sym.ptr(macho).next_in_section) {
1142 macho.targetStore(&sym.nlist(macho).n_sect, section.index(macho));
1143 }
1144 break :task;
1145 }
1146
1147 while (macho.mf.updates.pop()) |ni| {
1148 const clean_moved = ni.cleanMoved(&macho.mf);
1149 const clean_resized = ni.cleanResized(&macho.mf);
1150 const clean_next_moved = ni.cleanNextMoved(&macho.mf);
1151 if (!clean_moved and !clean_resized and !clean_next_moved) continue;
1152
1153 const sub_prog_node = macho.mf.update_prog_node.start(@tagName(macho.getNode(ni)), 0);
1154 defer sub_prog_node.end();
1155
1156 if (clean_moved) try macho.flushMoved(ni);
1157 if (clean_resized) try macho.flushResized(ni);
1158
1159 break :task;
1160 }
1161 }
1162
1163 return macho.sections_changed_index.count() > 0 or
1164 macho.mf.updates.items.len > 0;
1165}
1166fn flushMoved(macho: *MachO, ni: MappedFile.Node.Index) link.Error!void {
1167 macho.flushMachOFileOffset(ni);
1168 switch (macho.getNode(ni)) {
1169 .macho => unreachable,
1170 .macho_header => {},
1171 .segment => return, // segments don't bubble moved
1172 .section => |section| {
1173 const segment_vaddr: u64 = macho.getNode(ni.parent(&macho.mf).unwrap().?).segment.vaddr(macho);
1174 const section_offset = ni.location(&macho.mf).resolve(&macho.mf)[0];
1175 macho.targetStore(&section.lcPtr(macho).addr, segment_vaddr + section_offset);
1176 },
1177 .symtab => {},
1178 .strtab => {},
1179 }
1180 try ni.childrenMoved(macho.base.comp.gpa, &macho.mf);
1181}
1182fn flushMachOFileOffset(macho: *MachO, ni: MappedFile.Node.Index) void {
1183 const offset = ni.fileLocation(&macho.mf, false).offset;
1184 switch (macho.getNode(ni)) {
1185 .macho => unreachable,
1186 .macho_header => {
1187 assert(offset == 0);
1188 },
1189 .segment => |segment| {
1190 macho.targetStore(&segment.lcPtr(macho).fileoff, @intCast(offset));
1191 var child_oni = ni.first(&macho.mf);
1192 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&macho.mf)) {
1193 macho.flushMachOFileOffset(child_ni);
1194 }
1195 },
1196 .section => |section| {
1197 macho.targetStore(&section.lcPtr(macho).offset, @intCast(offset));
1198 },
1199 .symtab => {
1200 const symtab_cmd = macho.lc.symtab.ptr(macho, std.macho.symtab_command);
1201 macho.targetStore(&symtab_cmd.symoff, @intCast(offset));
1202 },
1203 .strtab => {
1204 const symtab_cmd = macho.lc.symtab.ptr(macho, std.macho.symtab_command);
1205 macho.targetStore(&symtab_cmd.stroff, @intCast(offset));
1206 },
1207 }
1208}
1209fn flushResized(macho: *MachO, ni: MappedFile.Node.Index) link.Error!void {
1210 const size = ni.location(&macho.mf).resolve(&macho.mf)[1];
1211 switch (macho.getNode(ni)) {
1212 .macho => {},
1213 .macho_header => {},
1214 .segment => |segment| {
1215 macho.targetStore(&segment.lcPtr(macho).filesize, size);
1216 },
1217 .section => |section| {
1218 macho.targetStore(&section.lcPtr(macho).size, size);
1219 },
1220 .symtab, .strtab => {
1221 // Don't modify the symtab load command's `nsyms` or `strsize` field here; we only do
1222 // that when we actually add strings or symbols.
1223 },
1224 }
1225}
1226
1227pub fn updateFunc(
1228 macho: *MachO,
1229 pt: Zcu.PerThread,
1230 func_index: InternPool.Index,
1231 mir: *const codegen.AnyMir,
1232) link.Error!void {
1233 _ = macho;
1234 _ = pt;
1235 _ = func_index;
1236 _ = mir;
1237 @panic("TODO");
1238}
1239pub fn updateNav(macho: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
1240 _ = macho;
1241 _ = pt;
1242 _ = nav_index;
1243 @panic("TODO");
1244}
1245pub fn updateExports(
1246 macho: *MachO,
1247 pt: Zcu.PerThread,
1248 export_indices: []const Zcu.Export.Index,
1249) link.Error!void {
1250 _ = macho;
1251 _ = pt;
1252 _ = export_indices;
1253 @panic("TODO");
1254}
1255
1256fn dumpStderr(macho: *MachO, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void {
1257 const comp = macho.base.comp;
1258 const io = comp.io;
1259 var buffer: [512]u8 = undefined;
1260 const stderr = try io.lockStderr(&buffer, null);
1261 defer io.unlockStderr();
1262 const w = &stderr.file_writer.interface;
1263 _ = macho.dump(w, tid) catch |err| switch (err) {
1264 error.WriteFailed => return stderr.file_writer.err.?,
1265 };
1266}
1267
1268pub fn dump(macho: *MachO, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
1269 if (macho.options.enable_link_snapshots) {
1270 try macho.printNode(tid, w, .root, 0);
1271 return .enabled;
1272 }
1273 return .disabled;
1274}
1275fn printNode(
1276 macho: *const MachO,
1277 tid: Zcu.PerThread.Id,
1278 w: *Io.Writer,
1279 ni: MappedFile.Node.Index,
1280 indent: usize,
1281) Io.Writer.Error!void {
1282 const node = macho.getNode(ni);
1283 try w.splatByteAll(' ', indent);
1284 switch (node) {
1285 .macho => try w.writeAll("macho"),
1286 .macho_header => try w.writeAll("macho_header"),
1287 .section => |section| try w.print("section({q})", .{section.name(macho)}),
1288 .segment => |segment| try w.print("segment({q})", .{segment.name(macho)}),
1289 .symtab => try w.writeAll("symtab"),
1290 .strtab => try w.writeAll("strtab"),
1291 }
1292 {
1293 const mf_node = &macho.mf.nodes.items[@backingInt(ni)];
1294 const off, const size = mf_node.location().resolve(&macho.mf);
1295 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}{s}{s}\n", .{
1296 @backingInt(ni),
1297 off,
1298 size,
1299 mf_node.flags.alignment.toByteUnits(),
1300 mf_node.flags.position,
1301 if (mf_node.flags.bubbles_moved) " bubbles_moved" else "",
1302 if (mf_node.flags.moved) " moved" else "",
1303 if (mf_node.flags.resized) " resized" else "",
1304 if (mf_node.flags.enable_next_moved) " enable_next_moved" else "",
1305 if (mf_node.flags.next_moved) " next_moved" else "",
1306 if (mf_node.flags.has_content) " has_content" else "",
1307 });
1308 }
1309 if (ni.first(&macho.mf).unwrap()) |first_ni| {
1310 // non-leaf, just print children
1311 var child_ni = first_ni;
1312 while (true) {
1313 try macho.printNode(tid, w, child_ni, indent + 1);
1314 child_ni = child_ni.next(&macho.mf).unwrap() orelse break;
1315 }
1316 return;
1317 }
1318 const start_address: usize, const end_address: usize = file_loc: {
1319 const file_loc = ni.fileLocation(&macho.mf, false);
1320 break :file_loc .{ @intCast(file_loc.offset), @intCast(file_loc.offset + file_loc.size) };
1321 };
1322 var address = start_address;
1323 const line_len = 0x10;
1324 while (true) : (address = @min(std.mem.alignForward(usize, address + 1, line_len), end_address)) {
1325 try w.splatByteAll(' ', indent + 1);
1326 try w.print("{x:0>8}", .{address});
1327 if (address == end_address) break try w.writeByte('\n');
1328 try w.splatByteAll(' ', 2);
1329 const start_byte_address = std.mem.alignBackward(usize, address, line_len);
1330 const end_byte_address = start_byte_address + line_len;
1331 for (start_byte_address..end_byte_address) |byte_address|
1332 if (byte_address < start_address or byte_address >= end_address)
1333 try w.splatByteAll(' ', 3)
1334 else
1335 try w.print("{x:0>2} ", .{macho.mf.memory_map.memory[byte_address]});
1336 try w.writeByte(' ');
1337 for (start_byte_address..@min(end_address, end_byte_address)) |byte_address|
1338 try w.writeByte(if (byte_address < start_address or byte_address >= end_address) ' ' else char: {
1339 const byte = macho.mf.memory_map.memory[byte_address];
1340 break :char if (std.ascii.isPrint(byte)) byte else '.';
1341 });
1342 try w.writeByte('\n');
1343 }
1344}
1345
1346const std = @import("std");
1347const assert = std.debug.assert;
1348const log = std.log.scoped(.link);
1349const Allocator = std.mem.Allocator;
1350const Io = std.Io;
1351
1352const link = @import("../link.zig");
1353const codegen = @import("../codegen.zig");
1354const Compilation = @import("../Compilation.zig");
1355const InternPool = @import("../InternPool.zig");
1356const MappedFile = @import("MappedFile.zig");
1357const Zcu = @import("../Zcu.zig");
src/link/MappedFile.zig+6-6
......@@ -495,12 +495,12 @@ pub const Node = extern struct {
495495 }
496496
497497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
498 var parent_ni = ni;
499 while (parent_ni != .root) {
500 const parent_node = parent_ni.get(mf);
501 if (!parent_node.flags.bubbles_moved) break;
502 if (parent_node.flags.moved) return true;
503 parent_ni = parent_node.parent.unwrap().?;
498 var cur_ni = ni;
499 while (cur_ni != .root) {
500 if (cur_ni.get(mf).flags.moved) return true;
501 const parent_ni = cur_ni.parent(mf).unwrap().?;
502 if (!parent_ni.get(mf).flags.bubbles_moved) break;
503 cur_ni = parent_ni;
504504 }
505505 return false;
506506 }
src/link/Wasm.zig+2
......@@ -3783,6 +3783,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
37833783 switch (input) {
37843784 .res => unreachable,
37853785 .dso => unreachable,
3786 .tbd => unreachable,
37863787 .object, .archive => |obj| {
37873788 try argv.append(gpa, try obj.path.toString(comp.arena));
37883789 },
......@@ -3792,6 +3793,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
37923793 switch (input) {
37933794 .res => unreachable,
37943795 .dso => unreachable,
3796 .tbd => unreachable,
37953797 .object => |obj| try parseObject(wasm, obj),
37963798 .archive => |obj| try parseArchive(wasm, obj),
37973799 }
src/main.zig+126-150
......@@ -893,13 +893,6 @@ const ArgsIterator = struct {
893893 }
894894};
895895
896/// Similar to `link.Framework` except it doesn't store yet unresolved
897/// path to the framework.
898const Framework = struct {
899 needed: bool = false,
900 weak: bool = false,
901};
902
903896const CliModule = struct {
904897 root_path: []const u8,
905898 root_src_path: []const u8,
......@@ -1117,12 +1110,12 @@ fn buildOutputType(
11171110
11181111 .llvm_m_args = .empty,
11191112 .sysroot = null,
1120 .lib_directories = .empty, // populated by createModule()
11211113 .lib_dir_args = .empty, // populated from CLI arg parsing
1114 .lib_directories = .empty, // populated by createModule()
1115 .framework_dir_args = .empty, // populated from CLI arg parsing
1116 .framework_directories = .empty, // populated by createModule()
11221117 .libc_installation = null,
11231118 .want_native_include_dirs = false,
1124 .frameworks = .empty,
1125 .framework_dirs = .empty,
11261119 .rpath_list = .empty,
11271120 .each_lib_rpath = null,
11281121 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),
......@@ -1275,13 +1268,25 @@ fn buildOutputType(
12751268 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
12761269 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
12771270 } else if (mem.eql(u8, arg, "-F")) {
1278 try create_module.framework_dirs.append(arena, args_iter.nextOrFatal());
1271 try create_module.framework_dir_args.append(arena, args_iter.nextOrFatal());
12791272 } else if (mem.eql(u8, arg, "-framework")) {
1280 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{});
1273 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1274 .name = args_iter.nextOrFatal(),
1275 .needed = false,
1276 .weak = false,
1277 } });
12811278 } else if (mem.eql(u8, arg, "-weak_framework")) {
1282 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true });
1279 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1280 .name = args_iter.nextOrFatal(),
1281 .needed = false,
1282 .weak = true,
1283 } });
12831284 } else if (mem.eql(u8, arg, "-needed_framework")) {
1284 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
1285 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
1286 .name = args_iter.nextOrFatal(),
1287 .needed = true,
1288 .weak = false,
1289 } });
12851290 } else if (mem.eql(u8, arg, "-install_name")) {
12861291 install_name = args_iter.nextOrFatal();
12871292 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
......@@ -1396,11 +1401,11 @@ fn buildOutputType(
13961401 } else if (mem.eql(u8, arg, "-iframework")) {
13971402 const path = args_iter.nextOrFatal();
13981403 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1399 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1404 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
14001405 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
14011406 const path = args_iter.nextOrFatal();
14021407 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1403 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1408 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
14041409 } else if (mem.eql(u8, arg, "--version")) {
14051410 const next_arg = args_iter.nextOrFatal();
14061411 version = std.SemanticVersion.parse(next_arg) catch |err| {
......@@ -1828,7 +1833,7 @@ fn buildOutputType(
18281833 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
18291834 try create_module.lib_dir_args.append(arena, rest);
18301835 } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {
1831 try create_module.framework_dirs.append(arena, rest);
1836 try create_module.framework_dir_args.append(arena, rest);
18321837 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
18331838 // We don't know whether this library is part of libc
18341839 // or libc++ until we resolve the target, so we append
......@@ -2532,8 +2537,12 @@ fn buildOutputType(
25322537 disable_c_depfile = true;
25332538 try cc_argv.appendSlice(arena, it.other_args);
25342539 },
2535 .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),
2536 .framework => try create_module.frameworks.put(arena, it.only_arg, .{}),
2540 .framework_dir => try create_module.framework_dir_args.append(arena, it.only_arg),
2541 .framework => try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2542 .name = it.only_arg,
2543 .needed = false,
2544 .weak = false,
2545 } }),
25372546 .nostdlibinc => create_module.want_native_include_dirs = false,
25382547 .strip => mod_opts.strip = true,
25392548 .exec_model => {
......@@ -2561,7 +2570,11 @@ fn buildOutputType(
25612570 },
25622571 .name_done = false,
25632572 } }),
2564 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2573 .weak_framework => try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2574 .name = it.only_arg,
2575 .needed = false,
2576 .weak = true,
2577 } }),
25652578 .headerpad_max_install_names => headerpad_max_install_names = true,
25662579 .compress_debug_sections => {
25672580 if (it.only_arg.len == 0) {
......@@ -2914,11 +2927,23 @@ fn buildOutputType(
29142927 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });
29152928 };
29162929 } else if (mem.eql(u8, arg, "-framework")) {
2917 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{});
2930 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2931 .name = linker_args_it.nextOrFatal(),
2932 .needed = false,
2933 .weak = false,
2934 } });
29182935 } else if (mem.eql(u8, arg, "-weak_framework")) {
2919 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true });
2936 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2937 .name = linker_args_it.nextOrFatal(),
2938 .needed = false,
2939 .weak = true,
2940 } });
29202941 } else if (mem.eql(u8, arg, "-needed_framework")) {
2921 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2942 try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{
2943 .name = linker_args_it.nextOrFatal(),
2944 .needed = true,
2945 .weak = false,
2946 } });
29222947 } else if (mem.eql(u8, arg, "-needed_library")) {
29232948 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
29242949 .name = linker_args_it.nextOrFatal(),
......@@ -3400,59 +3425,6 @@ fn buildOutputType(
34003425 }
34013426 }
34023427
3403 var resolved_frameworks = std.array_list.Managed(Compilation.Framework).init(arena);
3404
3405 if (create_module.frameworks.keys().len > 0) {
3406 var test_path = std.array_list.Managed(u8).init(gpa);
3407 defer test_path.deinit();
3408
3409 var checked_paths = std.array_list.Managed(u8).init(gpa);
3410 defer checked_paths.deinit();
3411
3412 var failed_frameworks = std.array_list.Managed(struct {
3413 name: []const u8,
3414 checked_paths: []const u8,
3415 }).init(arena);
3416
3417 framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| {
3418 checked_paths.clearRetainingCapacity();
3419
3420 for (create_module.framework_dirs.items) |framework_dir_path| {
3421 if (try accessFrameworkPath(
3422 io,
3423 &test_path,
3424 &checked_paths,
3425 framework_dir_path,
3426 framework_name,
3427 )) {
3428 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3429 try resolved_frameworks.append(.{
3430 .needed = info.needed,
3431 .weak = info.weak,
3432 .path = path,
3433 });
3434 continue :framework;
3435 }
3436 }
3437
3438 try failed_frameworks.append(.{
3439 .name = framework_name,
3440 .checked_paths = try arena.dupe(u8, checked_paths.items),
3441 });
3442 }
3443
3444 if (failed_frameworks.items.len > 0) {
3445 for (failed_frameworks.items) |f| {
3446 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
3447 std.log.err("unable to find framework {q}. searched paths: {s}", .{
3448 f.name, searched_paths,
3449 });
3450 }
3451 process.exit(1);
3452 }
3453 }
3454 // After this point, resolved_frameworks is used instead of frameworks.
3455
34563428 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
34573429 const total_obj_count = create_module.c_source_files.items.len +
34583430 @intFromBool(root_src_file != null) +
......@@ -3689,6 +3661,7 @@ fn buildOutputType(
36893661 .emit_docs = emit_docs_resolved,
36903662 .emit_implib = emit_implib_resolved,
36913663 .lib_directories = create_module.lib_directories.items,
3664 .framework_directories = create_module.framework_directories.items,
36923665 .rpath_list = create_module.rpath_list.items,
36933666 .symbol_wrap_set = symbol_wrap_set,
36943667 .c_source_files = create_module.c_source_files.items,
......@@ -3697,8 +3670,6 @@ fn buildOutputType(
36973670 .rc_includes = rc_includes,
36983671 .mingw_unicode_entry_point = mingw_unicode_entry_point,
36993672 .link_inputs = create_module.link_inputs.items,
3700 .framework_dirs = create_module.framework_dirs.items,
3701 .frameworks = resolved_frameworks.items,
37023673 .windows_lib_names = create_module.windows_libs.keys(),
37033674 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,
37043675 .want_ubsan_rt = want_ubsan_rt,
......@@ -4041,13 +4012,13 @@ const CreateModule = struct {
40414012 /// CPU features.
40424013 llvm_m_args: std.ArrayList([]const u8),
40434014 sysroot: ?[]const u8,
4044 lib_directories: std.ArrayList(Directory),
40454015 lib_dir_args: std.ArrayList([]const u8),
4016 lib_directories: std.ArrayList(Directory),
4017 framework_dir_args: std.ArrayList([]const u8),
4018 framework_directories: std.ArrayList(Directory),
40464019 libc_installation: ?LibCInstallation,
40474020 want_native_include_dirs: bool,
4048 frameworks: std.array_hash_map.String(Framework),
40494021 native_system_include_paths: []const []const u8,
4050 framework_dirs: std.ArrayList([]const u8),
40514022 rpath_list: std.ArrayList([]const u8),
40524023 each_lib_rpath: ?bool,
40534024 libc_paths_file: ?[]const u8,
......@@ -4158,7 +4129,7 @@ fn createModule(
41584129 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
41594130 defer unresolved_link_inputs.deinit(gpa);
41604131 try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);
4161 var any_name_queries_remaining = false;
4132 var any_named_library_queries = false;
41624133 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
41634134 .name_query => |nq| {
41644135 const lib_name = nq.name;
......@@ -4199,39 +4170,52 @@ fn createModule(
41994170 }
42004171
42014172 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4202 any_name_queries_remaining = true;
4173 any_named_library_queries = true;
4174 },
4175 .framework_query => {
4176 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4177 any_named_library_queries = true;
42034178 },
42044179 else => {
42054180 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
42064181 },
42074182 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
42084183
4209 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
4184 if (any_named_library_queries) create_module.want_native_include_dirs = true;
42104185
42114186 // Resolve the library path arguments with respect to sysroot.
42124187 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
4213 if (create_module.sysroot) |root| {
4214 for (create_module.lib_dir_args.items) |lib_dir_arg| {
4215 if (fs.path.isAbsolute(lib_dir_arg)) {
4216 const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];
4217 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
4218 addLibDirectoryWarn(io, &create_module.lib_directories, full_path);
4219 } else {
4220 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4188 for (create_module.lib_dir_args.items) |dir_arg| {
4189 const path: []const u8 = path: {
4190 if (fs.path.isAbsolute(dir_arg)) {
4191 if (create_module.sysroot) |sysroot| {
4192 // Change the path root to the given sysroot.
4193 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4194 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4195 }
42214196 }
4222 }
4223 } else {
4224 for (create_module.lib_dir_args.items) |lib_dir_arg| {
4225 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4226 }
4197 break :path dir_arg;
4198 };
4199 appendLibDirOrWarn(io, &create_module.lib_directories, path);
42274200 }
42284201 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
42294202
4230 if (resolved_target.is_native_os and target.os.tag.isDarwin()) {
4231 // If we want to link against frameworks, we need system headers.
4232 if (create_module.frameworks.count() > 0)
4233 create_module.want_native_include_dirs = true;
4203 // Likewise for framework path arguments.
4204 try create_module.framework_directories.ensureUnusedCapacity(arena, create_module.framework_dir_args.items.len);
4205 for (create_module.framework_dir_args.items) |dir_arg| {
4206 const path: []const u8 = path: {
4207 if (fs.path.isAbsolute(dir_arg)) {
4208 if (create_module.sysroot) |sysroot| {
4209 // Change the path root to the given sysroot.
4210 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4211 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4212 }
4213 }
4214 break :path dir_arg;
4215 };
4216 appendLibDirOrWarn(io, &create_module.framework_directories, path);
42344217 }
4218 create_module.framework_dir_args = undefined; // From here we use framework_directories instead.
42354219
42364220 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
42374221 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
......@@ -4253,11 +4237,17 @@ fn createModule(
42534237
42544238 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
42554239
4256 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
42574240 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
42584241
42594242 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
4260 for (paths.lib_dirs.items) |path| addLibDirectoryWarn2(io, &create_module.lib_directories, path, true);
4243 for (paths.lib_dirs.items) |path| {
4244 appendLibDirOrWarnAllowMissing(io, &create_module.lib_directories, path);
4245 }
4246
4247 try create_module.framework_directories.ensureUnusedCapacity(arena, paths.framework_dirs.items.len);
4248 for (paths.framework_dirs.items) |path| {
4249 appendLibDirOrWarnAllowMissing(io, &create_module.framework_directories, path);
4250 }
42614251 }
42624252
42634253 if (create_module.libc_paths_file) |paths_file| {
......@@ -4266,7 +4256,7 @@ fn createModule(
42664256 }
42674257
42684258 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
4269 any_name_queries_remaining)
4259 any_named_library_queries)
42704260 {
42714261 if (create_module.libc_installation == null) {
42724262 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{
......@@ -4277,9 +4267,19 @@ fn createModule(
42774267 fatal("unable to find native libc installation: {t}", .{err});
42784268 };
42794269 }
4270
42804271 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
4281 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
4282 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);
4272
4273 appendLibDirOrWarn(
4274 io,
4275 &create_module.lib_directories,
4276 create_module.libc_installation.?.msvc_lib_dir.?,
4277 );
4278 appendLibDirOrWarn(
4279 io,
4280 &create_module.lib_directories,
4281 create_module.libc_installation.?.kernel32_lib_dir.?,
4282 );
42834283 }
42844284
42854285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
......@@ -4291,6 +4291,7 @@ fn createModule(
42914291 &unresolved_link_inputs,
42924292 &create_module.link_inputs,
42934293 create_module.lib_directories.items,
4294 create_module.framework_directories.items,
42944295 color,
42954296 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});
42964297
......@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {
60856086 };
60866087};
60876088
6088fn accessFrameworkPath(
6089 io: Io,
6090 test_path: *std.array_list.Managed(u8),
6091 checked_paths: *std.array_list.Managed(u8),
6092 framework_dir_path: []const u8,
6093 framework_name: []const u8,
6094) !bool {
6095 const sep = fs.path.sep_str;
6096
6097 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
6098 test_path.clearRetainingCapacity();
6099 try test_path.print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
6100 framework_dir_path, framework_name, framework_name, ext,
6101 });
6102 try checked_paths.print("\n {s}", .{test_path.items});
6103 Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) {
6104 error.FileNotFound => continue,
6105 else => |e| fatal("unable to search for {s} framework {q}: {t}", .{
6106 ext, test_path.items, e,
6107 }),
6108 };
6109 return true;
6110 }
6111
6112 return false;
6113}
6114
61156089fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
61166090 return stringToEnum(std.zig.RcIncludes, arg) orelse
61176091 fatal("unsupported rc includes type: {q}", .{arg});
......@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
62226196 return false;
62236197}
62246198
6225fn addLibDirectoryWarn(io: Io, lib_directories: *std.ArrayList(Directory), path: []const u8) void {
6226 return addLibDirectoryWarn2(io, lib_directories, path, false);
6199fn appendLibDirOrWarn(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6200 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6201 dirs.appendAssumeCapacity(.{
6202 .path = path,
6203 .handle = handle,
6204 });
6205 } else |err| {
6206 warn("unable to open library directory {q}: {t}", .{ path, err });
6207 }
62276208}
6228
6229fn addLibDirectoryWarn2(
6230 io: Io,
6231 lib_directories: *std.ArrayList(Directory),
6232 path: []const u8,
6233 ignore_not_found: bool,
6234) void {
6235 lib_directories.appendAssumeCapacity(.{
6236 .handle = Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
6237 if (err == error.FileNotFound and ignore_not_found) return;
6238 warn("unable to open library directory {q}: {t}", .{ path, err });
6239 return;
6240 },
6241 .path = path,
6242 });
6209fn appendLibDirOrWarnAllowMissing(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6210 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6211 dirs.appendAssumeCapacity(.{
6212 .path = path,
6213 .handle = handle,
6214 });
6215 } else |err| switch (err) {
6216 error.FileNotFound => {}, // ignore
6217 else => |e| warn("unable to open library directory {q}: {t}", .{ path, e }),
6218 }
62436219}
62446220
62456221const IoImpl = switch (build_options.io_mode) {
src/target.zig+2-2
......@@ -299,7 +299,7 @@ pub fn preferNewLinkerOverLld(target: *const std.Target) bool {
299299/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.
300300pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {
301301 return switch (ofmt) {
302 .elf => true,
302 .elf, .macho => true,
303303 else => false,
304304 };
305305}
......@@ -510,7 +510,7 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 {
510510 },
511511 // On SerenityOS libc includes libm, libpthread, libdl, and libssp.
512512 .serenity => &.{"-lc"},
513 else => &.{},
513 else => if (target.os.tag.isDarwin()) &.{"-lSystem"} else &.{},
514514 };
515515 return result;
516516}