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) {...@@ -1145,16 +1145,16 @@ pub const LC = enum(u32) {
1145};1145};
11461146
1147/// the mach magic number1147/// the mach magic number
1148pub const MH_MAGIC = 0xfeedface;1148pub const MH_MAGIC: u32 = 0xfeedface;
11491149
1150/// NXSwapInt(MH_MAGIC)1150/// NXSwapInt(MH_MAGIC)
1151pub const MH_CIGAM = 0xcefaedfe;1151pub const MH_CIGAM: u32 = 0xcefaedfe;
11521152
1153/// the 64-bit mach magic number1153/// the 64-bit mach magic number
1154pub const MH_MAGIC_64 = 0xfeedfacf;1154pub const MH_MAGIC_64: u32 = 0xfeedfacf;
11551155
1156/// NXSwapInt(MH_MAGIC_64)1156/// NXSwapInt(MH_MAGIC_64)
1157pub const MH_CIGAM_64 = 0xcffaedfe;1157pub const MH_CIGAM_64: u32 = 0xcffaedfe;
11581158
1159/// relocatable object file1159/// relocatable object file
1160pub const MH_OBJECT = 0x1;1160pub const MH_OBJECT = 0x1;
...@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;...@@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000;
1281// Constants for the flags field of the fat_header1281// Constants for the flags field of the fat_header
12821282
1283/// the fat magic number1283/// the fat magic number
1284pub const FAT_MAGIC = 0xcafebabe;1284pub const FAT_MAGIC: u32 = 0xcafebabe;
12851285
1286/// NXSwapLong(FAT_MAGIC)1286/// NXSwapLong(FAT_MAGIC)
1287pub const FAT_CIGAM = 0xbebafeca;1287pub const FAT_CIGAM: u32 = 0xbebafeca;
12881288
1289/// the 64-bit fat magic number1289/// the 64-bit fat magic number
1290pub const FAT_MAGIC_64 = 0xcafebabf;1290pub const FAT_MAGIC_64: u32 = 0xcafebabf;
12911291
1292/// NXSwapLong(FAT_MAGIC_64)1292/// NXSwapLong(FAT_MAGIC_64)
1293pub const FAT_CIGAM_64 = 0xbfbafeca;1293pub const FAT_CIGAM_64: u32 = 0xbfbafeca;
12941294
1295/// Segment flags1295/// Segment flags
1296/// The file contents for this segment is for the high part of the VM space, the low part1296/// 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) {...@@ -2212,3 +2212,11 @@ pub const CompactUnwindEncoding = packed struct(u32) {
2212 has_lsda: u1,2212 has_lsda: u1,
2213 start: u1,2213 start: u1,
2214};2214};
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,...@@ -11,15 +11,6 @@ libc_include_dir_list: []const []const u8,
11libc_installation: ?*const LibCInstallation,11libc_installation: ?*const LibCInstallation,
12libc_framework_dir_list: []const []const u8,12libc_framework_dir_list: []const []const u8,
13sysroot: ?[]const u8,13sysroot: ?[]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
24pub fn detect(15pub fn detect(
25 arena: Allocator,16 arena: Allocator,
...@@ -37,7 +28,6 @@ pub fn detect(...@@ -37,7 +28,6 @@ pub fn detect(
37 .libc_installation = null,28 .libc_installation = null,
38 .libc_framework_dir_list = &.{},29 .libc_framework_dir_list = &.{},
39 .sysroot = null,30 .sysroot = null,
40 .darwin_sdk_layout = null,
41 };31 };
42 }32 }
4333
...@@ -102,13 +92,11 @@ pub fn detect(...@@ -102,13 +92,11 @@ pub fn detect(
102 .libc_installation = null,92 .libc_installation = null,
103 .libc_framework_dir_list = &.{},93 .libc_framework_dir_list = &.{},
104 .sysroot = null,94 .sysroot = null,
105 .darwin_sdk_layout = null,
106 };95 };
107}96}
10897
109fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *const LibCInstallation) !LibCDirs {98fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *const LibCInstallation) !LibCDirs {
110 var list = try std.array_list.Managed([]const u8).initCapacity(arena, 5);99 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
113 list.appendAssumeCapacity(lci.include_dir.?);101 list.appendAssumeCapacity(lci.include_dir.?);
114102
...@@ -149,21 +137,15 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con...@@ -149,21 +137,15 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con
149 }137 }
150 }138 }
151139
152 var sysroot: ?[]const u8 = null;140 const frameworks: []const []const u8 = if (target.os.tag.isDarwin()) try arena.dupe([]const u8, &.{
153141 try std.fs.path.join(arena, &.{ lci.darwin_sdk_dir.?, "System", "Library", "Frameworks" }),
154 if (target.os.tag.isDarwin()) d: {142 }) else &.{};
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 }
160143
161 return .{144 return .{
162 .libc_include_dir_list = list.items,145 .libc_include_dir_list = list.items,
163 .libc_installation = lci,146 .libc_installation = lci,
164 .libc_framework_dir_list = framework_list.items,147 .libc_framework_dir_list = frameworks,
165 .sysroot = sysroot,148 .sysroot = if (target.os.tag.isDarwin()) lci.darwin_sdk_dir.? else null,
166 .darwin_sdk_layout = if (sysroot == null) null else .sdk,
167 };149 };
168}150}
169151
...@@ -178,7 +160,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st...@@ -178,7 +160,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st
178 .libc_installation = null,160 .libc_installation = null,
179 .libc_framework_dir_list = &.{},161 .libc_framework_dir_list = &.{},
180 .sysroot = null,162 .sysroot = null,
181 .darwin_sdk_layout = .vendored,
182 };163 };
183 }164 }
184165
...@@ -230,7 +211,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st...@@ -230,7 +211,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st
230 .libc_installation = null,211 .libc_installation = null,
231 .libc_framework_dir_list = &.{},212 .libc_framework_dir_list = &.{},
232 .sysroot = null,213 .sysroot = null,
233 .darwin_sdk_layout = .vendored,
234 };214 };
235}215}
236216
lib/std/zig/LibCInstallation.zig+25-28
...@@ -23,6 +23,7 @@ crt_dir: ?[]const u8 = null,...@@ -23,6 +23,7 @@ crt_dir: ?[]const u8 = null,
23msvc_lib_dir: ?[]const u8 = null,23msvc_lib_dir: ?[]const u8 = null,
24kernel32_lib_dir: ?[]const u8 = null,24kernel32_lib_dir: ?[]const u8 = null,
25cc_dir: ?[]const u8 = null,25cc_dir: ?[]const u8 = null,
26darwin_sdk_dir: ?[]const u8 = null,
2627
27pub const FindError = error{28pub const FindError = error{
28 OutOfMemory,29 OutOfMemory,
...@@ -96,6 +97,10 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const...@@ -96,6 +97,10 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
96 log.err("sys_include_dir may not be empty", .{});97 log.err("sys_include_dir may not be empty", .{});
97 return error.ParseError;98 return error.ParseError;
98 }99 }
100 if (self.crt_dir == null) {
101 log.err("crt_dir may not be empty", .{});
102 return error.ParseError;
103 }
99104
100 const os_tag = target.os.tag;105 const os_tag = target.os.tag;
101106
...@@ -103,12 +108,6 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const...@@ -103,12 +108,6 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
103 log.err("cc_dir may not be empty for {s}", .{@tagName(os_tag)});108 log.err("cc_dir may not be empty for {s}", .{@tagName(os_tag)});
104 return error.ParseError;109 return error.ParseError;
105 }110 }
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
112 if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) {111 if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) {
113 log.err("msvc_lib_dir may not be empty for {s}-{s}", .{112 log.err("msvc_lib_dir may not be empty for {s}-{s}", .{
114 @tagName(os_tag),113 @tagName(os_tag),
...@@ -123,19 +122,16 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const...@@ -123,19 +122,16 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const
123 });122 });
124 return error.ParseError;123 return error.ParseError;
125 }124 }
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
127 return self;130 return self;
128}131}
129132
130pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {133pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
131 @setEvalBranchQuota(4000);134 @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
139 try out.print(135 try out.print(
140 \\# The directory that contains `stdlib.h`.136 \\# The directory that contains `stdlib.h`.
141 \\# On POSIX, can be found with: `cc -E -Wp,-v -xc /dev/null`137 \\# 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 {...@@ -153,7 +149,6 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
153 \\149 \\
154 \\# The directory that contains `crt1.o` or `crt2.o`.150 \\# The directory that contains `crt1.o` or `crt2.o`.
155 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.151 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
156 \\# Not needed when targeting macOS.
157 \\crt_dir={s}152 \\crt_dir={s}
158 \\153 \\
159 \\# The directory that contains `vcruntime.lib`.154 \\# The directory that contains `vcruntime.lib`.
...@@ -164,13 +159,18 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {...@@ -164,13 +159,18 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
164 \\# Only needed when targeting MSVC on Windows.159 \\# Only needed when targeting MSVC on Windows.
165 \\kernel32_lib_dir={s}160 \\kernel32_lib_dir={s}
166 \\161 \\
162 \\# The directory that contains `SDKSettings.json`
163 \\# Only needed when targeting Darwin.
164 \\darwin_sdk_dir={s}
165 \\
167 , .{166 , .{
168 include_dir,167 self.include_dir orelse "",
169 sys_include_dir,168 self.sys_include_dir orelse "",
170 crt_dir,169 self.cc_dir orelse "",
171 msvc_lib_dir,170 self.crt_dir orelse "",
172 kernel32_lib_dir,171 self.msvc_lib_dir orelse "",
173 cc_dir,172 self.kernel32_lib_dir orelse "",
173 self.darwin_sdk_dir orelse "",
174 });174 });
175}175}
176176
...@@ -189,16 +189,13 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib...@@ -189,16 +189,13 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib
189 if (is_darwin and args.target.os.tag.isDarwin()) {189 if (is_darwin and args.target.os.tag.isDarwin()) {
190 if (!std.zig.system.darwin.isSdkInstalled(gpa, io))190 if (!std.zig.system.darwin.isSdkInstalled(gpa, io))
191 return error.DarwinSdkNotFound;191 return error.DarwinSdkNotFound;
192 const sdk = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse192 self.darwin_sdk_dir = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse
193 return error.DarwinSdkNotFound;193 return error.DarwinSdkNotFound;
194 defer gpa.free(sdk);
195194
196 self.include_dir = try fs.path.join(gpa, &.{195 const sdk = self.darwin_sdk_dir.?;
197 sdk, "usr/include",196 self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" });
198 });197 self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" });
199 self.sys_include_dir = try fs.path.join(gpa, &.{198 self.crt_dir = try fs.path.join(gpa, &.{ sdk, "usr/lib" });
200 sdk, "usr/include",
201 });
202 return self;199 return self;
203 } else if (is_windows) {200 } else if (is_windows) {
204 const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch, args.environ_map) catch |err| switch (err) {201 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,...@@ -83,7 +83,7 @@ zigc_strat: RtStrat,
83/// Resolved into known paths, any GNU ld scripts already resolved.83/// Resolved into known paths, any GNU ld scripts already resolved.
84link_inputs: []const link.Input,84link_inputs: []const link.Input,
85/// Needed only for passing -F args to clang.85/// Needed only for passing -F args to clang.
86framework_dirs: []const []const u8,86framework_dirs: []const Cache.Directory,
87/// These are only for DLLs dependencies fulfilled by the `.def` files shipped87/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
88/// with Zig. Static libraries are provided as `link.Input` values.88/// with Zig. Static libraries are provided as `link.Input` values.
89windows_libs: std.array_hash_map.String(void),89windows_libs: std.array_hash_map.String(void),
...@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {...@@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum {
1350 version,1350 version,
1351};1351};
13521352
1353pub const Framework = link.File.MachO.Framework;
1354pub const SystemLib = link.SystemLib;1353pub const SystemLib = link.SystemLib;
13551354
1356pub const CacheMode = enum {1355pub const CacheMode = enum {
...@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {...@@ -1487,6 +1486,7 @@ pub const CreateOptions = struct {
1487 /// The ELF implementation no longer uses this data, however the MachO and COFF1486 /// The ELF implementation no longer uses this data, however the MachO and COFF
1488 /// implementations still do.1487 /// implementations still do.
1489 lib_directories: []const Cache.Directory = &.{},1488 lib_directories: []const Cache.Directory = &.{},
1489 framework_directories: []const Cache.Directory = &.{},
1490 rpath_list: []const []const u8 = &[0][]const u8{},1490 rpath_list: []const []const u8 = &[0][]const u8{},
1491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,1491 symbol_wrap_set: std.array_hash_map.String(void) = .empty,
1492 c_source_files: []const CSourceFile = &.{},1492 c_source_files: []const CSourceFile = &.{},
...@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {...@@ -1494,8 +1494,6 @@ pub const CreateOptions = struct {
1494 manifest_file: ?[]const u8 = null,1494 manifest_file: ?[]const u8 = null,
1495 rc_includes: std.zig.RcIncludes = .any,1495 rc_includes: std.zig.RcIncludes = .any,
1496 link_inputs: []const link.Input = &.{},1496 link_inputs: []const link.Input = &.{},
1497 framework_dirs: []const []const u8 = &[0][]const u8{},
1498 frameworks: []const Framework = &.{},
1499 windows_lib_names: []const []const u8 = &.{},1497 windows_lib_names: []const []const u8 = &.{},
1500 /// This means that if the output mode is an executable it will be a1498 /// This means that if the output mode is an executable it will be a
1501 /// Position Independent Executable. If the output mode is not an1499 /// 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,...@@ -2146,7 +2144,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2146 .ubsan_rt_strat = ubsan_rt_strat,2144 .ubsan_rt_strat = ubsan_rt_strat,
2147 .zigc_strat = zigc_strat,2145 .zigc_strat = zigc_strat,
2148 .link_inputs = options.link_inputs,2146 .link_inputs = options.link_inputs,
2149 .framework_dirs = options.framework_dirs,2147 .framework_dirs = options.framework_directories,
2150 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,2148 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
2151 .skip_linker_dependencies = options.skip_linker_dependencies,2149 .skip_linker_dependencies = options.skip_linker_dependencies,
2152 .queued_jobs = .{},2150 .queued_jobs = .{},
...@@ -2208,10 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,...@@ -2208,10 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2208 .z_relro = options.linker_z_relro,2206 .z_relro = options.linker_z_relro,
2209 .z_common_page_size = options.linker_z_common_page_size,2207 .z_common_page_size = options.linker_z_common_page_size,
2210 .z_max_page_size = options.linker_z_max_page_size,2208 .z_max_page_size = options.linker_z_max_page_size,
2211 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
2212 .frameworks = options.frameworks,
2213 .lib_directories = options.lib_directories,2209 .lib_directories = options.lib_directories,
2214 .framework_dirs = options.framework_dirs,
2215 .rpath_list = options.rpath_list,2210 .rpath_list = options.rpath_list,
2216 .symbol_wrap_set = options.symbol_wrap_set,2211 .symbol_wrap_set = options.symbol_wrap_set,
2217 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),2212 .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,...@@ -2429,11 +2424,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2429 // If linking against host libc installation, instead queue up jobs2424 // If linking against host libc installation, instead queue up jobs
2430 // for loading those files in the linker.2425 // for loading those files in the linker.
2431 if (comp.config.link_libc and is_exe_or_dyn_lib) {2426 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,2427 if (comp.libc_installation) |lci| {
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| {
2437 const basenames = LibCInstallation.CrtBasenames.get(.{2428 const basenames = LibCInstallation.CrtBasenames.get(.{
2438 .target = target,2429 .target = target,
2439 .link_libc = comp.config.link_libc,2430 .link_libc = comp.config.link_libc,
...@@ -2456,6 +2447,18 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,...@@ -2456,6 +2447,18 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2456 }2447 }
2457 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.2448 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
2458 comp.oneshot_prelink_tasks.appendAssumeCapacity(.load_host_libc);2449 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.
2459 } else if (target.isMuslLibC()) {2462 } else if (target.isMuslLibC()) {
2460 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);2463 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
24612464
...@@ -3409,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -3409,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
3409 man.hash.add(comp.zigc_strat);3412 man.hash.add(comp.zigc_strat);
3410 man.hash.add(comp.rc_includes);3413 man.hash.add(comp.rc_includes);
3411 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());3414 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 }
3413 man.hash.addListOfBytes(comp.windows_libs.keys());3419 man.hash.addListOfBytes(comp.windows_libs.keys());
34143420
3415 man.hash.addListOfBytes(comp.global_cc_argv);3421 man.hash.addListOfBytes(comp.global_cc_argv);
...@@ -3475,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -3475,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
3475 man.hash.add(opts.growable_table);3481 man.hash.add(opts.growable_table);
34763482
3477 // Mach-O specific stuff3483 // Mach-O specific stuff
3478 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
3479 try man.addInputPathOptional(opts.entitlements, .{});3484 try man.addInputPathOptional(opts.entitlements, .{});
3480 man.hash.addOptional(opts.pagezero_size);3485 man.hash.addOptional(opts.pagezero_size);
3481 man.hash.addOptional(opts.headerpad_size);3486 man.hash.addOptional(opts.headerpad_size);
...@@ -3485,7 +3490,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -3485,7 +3490,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
3485 man.hash.add(opts.discard_local_symbols);3490 man.hash.add(opts.discard_local_symbols);
3486 man.hash.addOptional(opts.compatibility_version);3491 man.hash.addOptional(opts.compatibility_version);
3487 man.hash.addOptionalBytes(opts.install_name);3492 man.hash.addOptionalBytes(opts.install_name);
3488 man.hash.addOptional(opts.darwin_sdk_layout);
34893493
3490 // COFF specific stuff3494 // COFF specific stuff
3491 man.hash.addOptional(opts.subsystem);3495 man.hash.addOptional(opts.subsystem);
...@@ -6455,7 +6459,7 @@ fn addCommonCCArgs(...@@ -6455,7 +6459,7 @@ fn addCommonCCArgs(
64556459
6456 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);6460 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
6457 for (comp.framework_dirs) |framework_dir| {6461 for (comp.framework_dirs) |framework_dir| {
6458 try argv.appendSlice(&.{ "-F", framework_dir });6462 try argv.appendSlice(&.{ "-F", framework_dir.path orelse "." });
6459 }6463 }
6460 }6464 }
6461 }6465 }
src/codegen.zig+12-74
...@@ -754,23 +754,15 @@ fn lowerUavRef(...@@ -754,23 +754,15 @@ fn lowerUavRef(
754 }754 }
755755
756 const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu);756 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(.{
760 .parent = reloc_parent,759 .parent = reloc_parent,
761 .offset = w.end,760 .offset = w.end,
761 .target = try lf.uavSymbol(pt, uav_val, uav_align),
762 .addend = @intCast(offset),762 .addend = @intCast(offset),
763 }) catch |err| switch (err) {763 });
764 error.OutOfMemory => |e| return e,764
765 else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}),765 try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation
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 }
774}766}
775767
776fn lowerNavRef(768fn lowerNavRef(
...@@ -805,68 +797,14 @@ fn lowerNavRef(...@@ -805,68 +797,14 @@ fn lowerNavRef(
805 else => {},797 else => {},
806 }798 }
807799
808 const vaddr = lf.getNavVAddr(pt, nav_index, .{800 try lf.relocSymAddr(.{
809 .parent = reloc_parent,801 .parent = reloc_parent,
810 .offset = w.end,802 .offset = w.end,
803 .target = try lf.navSymbol(nav_index),
811 .addend = @intCast(offset),804 .addend = @intCast(offset),
812 }) catch @panic("TODO rework getNavVAddr");805 });
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}
821806
822pub fn genNavRef(807 try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation
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 }
870}808}
871809
872/// deprecated legacy type810/// deprecated legacy type
...@@ -908,13 +846,13 @@ pub fn genTypedValue(...@@ -908,13 +846,13 @@ pub fn genTypedValue(
908 .none => .none,846 .none => .none,
909 .undef => .undef,847 .undef => .undef,
910 .immediate => |imm| .{ .immediate = imm },848 .immediate => |imm| .{ .immediate = imm },
911 .lea_nav => |nav| .{ .lea_symbol = try genNavRef(lf, pt, nav) },849 .lea_nav => |nav| .{ .lea_symbol = try lf.navSymbol(nav) },
912 .load_uav => |uav| .{ .load_symbol = try lf.lowerUav(850 .load_uav => |uav| .{ .load_symbol = try lf.uavSymbol(
913 pt,851 pt,
914 uav.val,852 uav.val,
915 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),853 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
916 ) },854 ) },
917 .lea_uav => |uav| .{ .lea_symbol = try lf.lowerUav(855 .lea_uav => |uav| .{ .lea_symbol = try lf.uavSymbol(
918 pt,856 pt,
919 uav.val,857 uav.val,
920 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),858 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
src/codegen/aarch64/Mir.zig+2-6
...@@ -93,11 +93,7 @@ pub fn emit(...@@ -93,11 +93,7 @@ pub fn emit(
93 lf,93 lf,
94 zcu,94 zcu,
95 atom_index,95 atom_index,
96 try @import("../../codegen.zig").genNavRef(96 try lf.navSymbol(nav_reloc.nav),
97 lf,
98 pt,
99 nav_reloc.nav,
100 ),
101 mir.body[nav_reloc.reloc.label],97 mir.body[nav_reloc.reloc.label],
102 body_end - Instruction.size * (1 + nav_reloc.reloc.label),98 body_end - Instruction.size * (1 + nav_reloc.reloc.label),
103 nav_reloc.reloc.addend,99 nav_reloc.reloc.addend,
...@@ -107,7 +103,7 @@ pub fn emit(...@@ -107,7 +103,7 @@ pub fn emit(
107 lf,103 lf,
108 zcu,104 zcu,
109 atom_index,105 atom_index,
110 try lf.lowerUav(106 try lf.uavSymbol(
111 pt,107 pt,
112 uav_reloc.uav.val,108 uav_reloc.uav.val,
113 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),109 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),
src/codegen/loongarch/Mir.zig+3-12
...@@ -83,11 +83,7 @@ pub fn emit(...@@ -83,11 +83,7 @@ pub fn emit(
83 lf,83 lf,
84 zcu,84 zcu,
85 atom_index,85 atom_index,
86 try @import("../../codegen.zig").genNavRef(86 try lf.navSymbol(nav_reloc.nav),
87 lf,
88 pt,
89 nav_reloc.nav,
90 ),
91 nav_reloc.reloc.type,87 nav_reloc.reloc.type,
92 body_end - @sizeOf(Instruction) * (1 + nav_reloc.reloc.label),88 body_end - @sizeOf(Instruction) * (1 + nav_reloc.reloc.label),
93 nav_reloc.reloc.addend,89 nav_reloc.reloc.addend,
...@@ -97,7 +93,7 @@ pub fn emit(...@@ -97,7 +93,7 @@ pub fn emit(
97 lf,93 lf,
98 zcu,94 zcu,
99 atom_index,95 atom_index,
100 try lf.lowerUav(96 try lf.uavSymbol(
101 pt,97 pt,
102 uav_reloc.uav.val,98 uav_reloc.uav.val,
103 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),99 ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),
...@@ -142,16 +138,11 @@ pub fn emit(...@@ -142,16 +138,11 @@ pub fn emit(
142 ) catch |err|138 ) catch |err|
143 return zcu.codegenFail(func.owner_nav, "emit reloc failed: {t}", .{err});139 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 );
150 for (mir.internal_relocs) |internal_reloc| emitReloc(141 for (mir.internal_relocs) |internal_reloc| emitReloc(
151 lf,142 lf,
152 zcu,143 zcu,
153 atom_index,144 atom_index,
154 func_nav,145 try lf.navSymbol(func.owner_nav),
155 internal_reloc.reloc.type,146 internal_reloc.reloc.type,
156 body_end - @sizeOf(Instruction) * (1 + internal_reloc.reloc.label),147 body_end - @sizeOf(Instruction) * (1 + internal_reloc.reloc.label),
157 @sizeOf(Instruction) * (@as(i64, @intCast(mir.prologue.len + mir.body.len - internal_reloc.target))),148 @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 {...@@ -8160,7 +8160,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
8160 const lf = func.bin_file;8160 const lf = func.bin_file;
81618161
8162 const result: codegen.MCValue = if (val.isUndef(pt.zcu))8162 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) }
8164 else8164 else
8165 try codegen.genTypedValue(lf, pt, val, func.target);8165 try codegen.genTypedValue(lf, pt, val, func.target);
8166 const mcv: MCValue = switch (result) {8166 const mcv: MCValue = switch (result) {
src/codegen/x86_64/Emit.zig+12-16
...@@ -100,11 +100,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -100,11 +100,7 @@ pub fn emitMir(emit: *Emit) Error!void {
100 .inst => |inst| .{ .inst = inst },100 .inst => |inst| .{ .inst = inst },
101 .table => .table,101 .table => .table,
102 .nav => |nav| {102 .nav => |nav| {
103 const symbol_id = try codegen.genNavRef(103 const symbol_id = try emit.bin_file.navSymbol(nav);
104 emit.bin_file,
105 emit.pt,
106 nav,
107 );
108 const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{104 const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{
109 .symbol = symbol_id,105 .symbol = symbol_id,
110 .is_extern = switch (@"extern".visibility) {106 .is_extern = switch (@"extern".visibility) {
...@@ -124,7 +120,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -124,7 +120,7 @@ pub fn emitMir(emit: *Emit) Error!void {
124 }120 }
125 },121 },
126 .uav => |uav| .{ .symbol = .{122 .uav => |uav| .{ .symbol = .{
127 .symbol = try emit.bin_file.lowerUav(123 .symbol = try emit.bin_file.uavSymbol(
128 emit.pt,124 emit.pt,
129 uav.val,125 uav.val,
130 Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),126 Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
...@@ -142,7 +138,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -142,7 +138,7 @@ pub fn emitMir(emit: *Emit) Error!void {
142 else if (emit.bin_file.cast(.macho)) |macho_file|138 else if (emit.bin_file.cast(.macho)) |macho_file|
143 @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|139 @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
144 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})))140 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|
146 @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym))))142 @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym))))
147 else143 else
148 return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),144 return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
...@@ -157,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -157,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {
157 .type = .FUNC,153 .type = .FUNC,
158 }) else if (emit.bin_file.cast(.macho)) |macho_file|154 }) else if (emit.bin_file.cast(.macho)) |macho_file|
159 @fromBackingInt(@intCast(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)))155 @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(.{
161 .name = extern_func.toSlice(&emit.lower.mir).?,157 .name = extern_func.toSlice(&emit.lower.mir).?,
162 })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),158 })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
163 .is_extern = true,159 .is_extern = true,
...@@ -171,7 +167,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -171,7 +167,7 @@ pub fn emitMir(emit: *Emit) Error!void {
171 switch (lowered_inst.encoding.mnemonic) {167 switch (lowered_inst.encoding.mnemonic) {
172 .call => {168 .call => {
173 reloc.target = .{ .branch = target };169 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) {
175 try emit.encodeInst(try .new(.none, .call, &.{171 try emit.encodeInst(try .new(.none, .call, &.{
176 .{ .mem = .initRip(.ptr, 0) },172 .{ .mem = .initRip(.ptr, 0) },
177 }, emit.lower.target), reloc_info);173 }, emit.lower.target), reloc_info);
...@@ -252,7 +248,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -252,7 +248,7 @@ pub fn emitMir(emit: *Emit) Error!void {
252 }, emit.lower.target), reloc_info),248 }, emit.lower.target), reloc_info),
253 else => unreachable,249 else => unreachable,
254 }250 }
255 } else if (emit.bin_file.cast(.coff2)) |_| {251 } else if (emit.bin_file.cast(.coff)) |_| {
256 if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {252 if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {
257 .lea => try emit.encodeInst(try .new(.none, .mov, &.{253 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
258 lowered_inst.ops[0],254 lowered_inst.ops[0],
...@@ -372,7 +368,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -372,7 +368,7 @@ pub fn emitMir(emit: *Emit) Error!void {
372 }, emit.lower.target), &.{});368 }, emit.lower.target), &.{});
373 },369 },
374 else => unreachable,370 else => unreachable,
375 } else if (emit.bin_file.cast(.coff2)) |coff| {371 } else if (emit.bin_file.cast(.coff)) |coff| {
376 switch (emit.lower.target.cpu.arch) {372 switch (emit.lower.target.cpu.arch) {
377 else => unreachable,373 else => unreachable,
378 .x86 => {374 .x86 => {
...@@ -597,12 +593,12 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -597,12 +593,12 @@ pub fn emitMir(emit: *Emit) Error!void {
597 .nav => |nav| @unionInit(593 .nav => |nav| @unionInit(
598 DwarfLoc,594 DwarfLoc,
599 addr_loc,595 addr_loc,
600 try codegen.genNavRef(emit.bin_file, emit.pt, nav),596 try emit.bin_file.navSymbol(nav),
601 ),597 ),
602 .uav => |uav| @unionInit(598 .uav => |uav| @unionInit(
603 DwarfLoc,599 DwarfLoc,
604 addr_loc,600 addr_loc,
605 try emit.bin_file.lowerUav(601 try emit.bin_file.uavSymbol(
606 emit.pt,602 emit.pt,
607 uav.val,603 uav.val,
608 Type.fromInterned(uav.orig_ty)604 Type.fromInterned(uav.orig_ty)
...@@ -845,7 +841,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI...@@ -845,7 +841,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
845 if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL;841 if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL;
846 break :rt .PC32;842 break :rt .PC32;
847 } },843 } },
848 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(844 ) else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
849 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),845 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
850 end_offset - 4,846 end_offset - 4,
851 @fromBackingInt(@intCast(@backingInt(target.symbol))),847 @fromBackingInt(@intCast(@backingInt(target.symbol))),
...@@ -883,7 +879,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI...@@ -883,7 +879,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
883 .symbolnum = @intCast(@backingInt(target.symbol)),879 .symbolnum = @intCast(@backingInt(target.symbol)),
884 },880 },
885 });881 });
886 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(882 } else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
887 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),883 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
888 end_offset - 4,884 end_offset - 4,
889 @fromBackingInt(@intCast(@backingInt(target.symbol))),885 @fromBackingInt(@intCast(@backingInt(target.symbol))),
...@@ -941,7 +937,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI...@@ -941,7 +937,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
941 .symbolnum = @intCast(@backingInt(target.symbol)),937 .symbolnum = @intCast(@backingInt(target.symbol)),
942 },938 },
943 });939 });
944 } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(940 } else if (emit.bin_file.cast(.coff)) |coff| try coff.addReloc(
945 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),941 @fromBackingInt(@intCast(@backingInt(emit.atom_id))),
946 end_offset - 4,942 end_offset - 4,
947 @fromBackingInt(@intCast(@backingInt(target.symbol))),943 @fromBackingInt(@intCast(@backingInt(target.symbol))),
src/dev.zig+3-3
...@@ -109,10 +109,10 @@ pub const Env = enum {...@@ -109,10 +109,10 @@ pub const Env = enum {
109 .spork8_backend,109 .spork8_backend,
110 .lld_linker,110 .lld_linker,
111 .coff_linker,111 .coff_linker,
112 .coff2_linker,
113 .elf_linker,112 .elf_linker,
114 .elf2_linker,113 .elf2_linker,
115 .macho_linker,114 .macho_linker,
115 .macho2_linker,
116 .c_linker,116 .c_linker,
117 .wasm_linker,117 .wasm_linker,
118 .spirv_linker,118 .spirv_linker,
...@@ -231,7 +231,7 @@ pub const Env = enum {...@@ -231,7 +231,7 @@ pub const Env = enum {
231 .incremental,231 .incremental,
232 .legalize,232 .legalize,
233 .x86_64_backend,233 .x86_64_backend,
234 .coff2_linker,234 .coff_linker,
235 => true,235 => true,
236 else => Env.sema.supports(feature),236 else => Env.sema.supports(feature),
237 },237 },
...@@ -321,10 +321,10 @@ pub const Feature = enum {...@@ -321,10 +321,10 @@ pub const Feature = enum {
321321
322 lld_linker,322 lld_linker,
323 coff_linker,323 coff_linker,
324 coff2_linker,
325 elf_linker,324 elf_linker,
326 elf2_linker,325 elf2_linker,
327 macho_linker,326 macho_linker,
327 macho2_linker,
328 c_linker,328 c_linker,
329 wasm_linker,329 wasm_linker,
330 spirv_linker,330 spirv_linker,
src/link.zig+552-172
...@@ -481,7 +481,6 @@ pub const File = struct {...@@ -481,7 +481,6 @@ pub const File = struct {
481481
482 // TODO: remove this. libraries are resolved by the frontend.482 // TODO: remove this. libraries are resolved by the frontend.
483 lib_directories: []const Directory,483 lib_directories: []const Directory,
484 framework_dirs: []const []const u8,
485 rpath_list: []const []const u8,484 rpath_list: []const []const u8,
486485
487 /// Zig compiler development linker flags.486 /// Zig compiler development linker flags.
...@@ -501,8 +500,6 @@ pub const File = struct {...@@ -501,8 +500,6 @@ pub const File = struct {
501 headerpad_max_install_names: bool,500 headerpad_max_install_names: bool,
502 /// Remove dylibs that are unreachable by the entry point or exported symbols501 /// Remove dylibs that are unreachable by the entry point or exported symbols
503 dead_strip_dylibs: bool,502 dead_strip_dylibs: bool,
504 frameworks: []const MachO.Framework,
505 darwin_sdk_layout: ?MachO.SdkLayout,
506 /// Force load all members of static archives that implement an503 /// Force load all members of static archives that implement an
507 /// Objective-C class or category504 /// Objective-C class or category
508 force_load_objc: bool,505 force_load_objc: bool,
...@@ -587,7 +584,7 @@ pub const File = struct {...@@ -587,7 +584,7 @@ pub const File = struct {
587 pub fn startProgress(base: *File, prog_node: std.Progress.Node) void {584 pub fn startProgress(base: *File, prog_node: std.Progress.Node) void {
588 switch (base.tag) {585 switch (base.tag) {
589 else => {},586 else => {},
590 inline .elf2, .coff2 => |tag| {587 inline .elf2, .coff, .macho2 => |tag| {
591 dev.check(tag.devFeature());588 dev.check(tag.devFeature());
592 return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node);589 return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node);
593 },590 },
...@@ -597,7 +594,7 @@ pub const File = struct {...@@ -597,7 +594,7 @@ pub const File = struct {
597 pub fn endProgress(base: *File) void {594 pub fn endProgress(base: *File) void {
598 switch (base.tag) {595 switch (base.tag) {
599 else => {},596 else => {},
600 inline .elf2, .coff2 => |tag| {597 inline .elf2, .coff, .macho2 => |tag| {
601 dev.check(tag.devFeature());598 dev.check(tag.devFeature());
602 return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress();599 return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress();
603 },600 },
...@@ -651,11 +648,13 @@ pub const File = struct {...@@ -651,11 +648,13 @@ pub const File = struct {
651 }648 }
652 base.file = try emit.root_dir.handle.openFile(io, emit.sub_path, .{ .mode = .read_write });649 base.file = try emit.root_dir.handle.openFile(io, emit.sub_path, .{ .mode = .read_write });
653 },650 },
654 .elf2, .coff2 => if (base.file == null) {651 .elf2, .coff, .macho2 => if (base.file == null) {
655 const mf = if (base.cast(.elf2)) |elf|652 const mf = if (base.cast(.elf2)) |elf|
656 &elf.mf653 &elf.mf
657 else if (base.cast(.coff2)) |coff|654 else if (base.cast(.coff)) |coff|
658 &coff.mf655 &coff.mf
656 else if (base.cast(.macho2)) |macho|
657 &macho.mf
659 else658 else
660 unreachable;659 unreachable;
661 mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{660 mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
...@@ -740,11 +739,13 @@ pub const File = struct {...@@ -740,11 +739,13 @@ pub const File = struct {
740 }739 }
741 }740 }
742 },741 },
743 .elf2, .coff2 => if (base.file) |f| {742 .elf2, .coff, .macho2 => if (base.file) |f| {
744 const mf = if (base.cast(.elf2)) |elf|743 const mf = if (base.cast(.elf2)) |elf|
745 &elf.mf744 &elf.mf
746 else if (base.cast(.coff2)) |coff|745 else if (base.cast(.coff)) |coff|
747 &coff.mf746 &coff.mf
747 else if (base.cast(.macho2)) |macho|
748 &macho.mf
748 else749 else
749 unreachable;750 unreachable;
750 mf.unmap();751 mf.unmap();
...@@ -841,7 +842,7 @@ pub const File = struct {...@@ -841,7 +842,7 @@ pub const File = struct {
841 switch (base.tag) {842 switch (base.tag) {
842 .lld => unreachable,843 .lld => unreachable,
843 else => {},844 else => {},
844 inline .elf, .elf2, .c, .coff2 => |tag| {845 inline .elf, .elf2, .c, .coff => |tag| {
845 dev.check(tag.devFeature());846 dev.check(tag.devFeature());
846 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);847 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);
847 },848 },
...@@ -899,7 +900,8 @@ pub const File = struct {...@@ -899,7 +900,8 @@ pub const File = struct {
899 .lld => unreachable,900 .lld => unreachable,
900 .plan9 => unreachable,901 .plan9 => unreachable,
901 .spirv => {},902 .spirv => {},
902 .coff2 => {},903 .coff => {},
904 .macho2 => {},
903 inline else => |tag| {905 inline else => |tag| {
904 dev.check(tag.devFeature());906 dev.check(tag.devFeature());
905 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line);907 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line);
...@@ -951,7 +953,7 @@ pub const File = struct {...@@ -951,7 +953,7 @@ pub const File = struct {
951 pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool {953 pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool {
952 switch (base.tag) {954 switch (base.tag) {
953 else => return false,955 else => return false,
954 inline .elf2, .coff2 => |tag| {956 inline .elf2, .coff, .macho2 => |tag| {
955 dev.check(tag.devFeature());957 dev.check(tag.devFeature());
956 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);958 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);
957 },959 },
...@@ -959,9 +961,10 @@ pub const File = struct {...@@ -959,9 +961,10 @@ pub const File = struct {
959 }961 }
960962
961 pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void {963 pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void {
964 if (base.comp.zcu.?.llvm_object != null) return;
962 switch (base.tag) {965 switch (base.tag) {
963 else => {},966 else => {},
964 inline .elf2, .coff2 => |tag| {967 inline .elf2, .coff, .macho2 => |tag| {
965 dev.check(tag.devFeature());968 dev.check(tag.devFeature());
966 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);969 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);
967 },970 },
...@@ -1039,6 +1042,7 @@ pub const File = struct {...@@ -1039,6 +1042,7 @@ pub const File = struct {
1039 pub const RelocInfo = struct {1042 pub const RelocInfo = struct {
1040 parent: Parent,1043 parent: Parent,
1041 offset: u64,1044 offset: u64,
1045 target: SymbolId,
1042 addend: u32,1046 addend: u32,
10431047
1044 pub const Parent = union(enum) {1048 pub const Parent = union(enum) {
...@@ -1048,16 +1052,9 @@ pub const File = struct {...@@ -1048,16 +1052,9 @@ pub const File = struct {
1048 };1052 };
1049 };1053 };
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.
1057 /// Never called when LLVM is codegenning the ZCU.1055 /// 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 {1056 pub fn relocSymAddr(base: *File, reloc_info: RelocInfo) Error!void {
1059 assert(pt.zcu.llvm_object == null);1057 assert(base.comp.zcu.?.llvm_object == null);
1060
1061 switch (base.tag) {1058 switch (base.tag) {
1062 .lld => unreachable,1059 .lld => unreachable,
1063 .c => unreachable,1060 .c => unreachable,
...@@ -1067,20 +1064,19 @@ pub const File = struct {...@@ -1067,20 +1064,19 @@ pub const File = struct {
1067 .spork8 => unreachable,1064 .spork8 => unreachable,
1068 inline else => |tag| {1065 inline else => |tag| {
1069 dev.check(tag.devFeature());1066 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);
1071 },1068 },
1072 }1069 }
1073 }1070 }
10741071
1075 /// Never called when LLVM is codegenning the ZCU.1072 /// Never called when LLVM is codegenning the ZCU.
1076 pub fn lowerUav(1073 pub fn uavSymbol(
1077 base: *File,1074 base: *File,
1078 pt: Zcu.PerThread,1075 pt: Zcu.PerThread,
1079 decl_val: InternPool.Index,1076 uav_val: InternPool.Index,
1080 decl_align: InternPool.Alignment,1077 uav_align: InternPool.Alignment,
1081 ) Error!SymbolId {1078 ) Error!SymbolId {
1082 assert(pt.zcu.llvm_object == null);1079 assert(pt.zcu.llvm_object == null);
1083
1084 switch (base.tag) {1080 switch (base.tag) {
1085 .lld => unreachable,1081 .lld => unreachable,
1086 .c => unreachable,1082 .c => unreachable,
...@@ -1090,15 +1086,14 @@ pub const File = struct {...@@ -1090,15 +1086,14 @@ pub const File = struct {
1090 .spork8 => unreachable,1086 .spork8 => unreachable,
1091 inline else => |tag| {1087 inline else => |tag| {
1092 dev.check(tag.devFeature());1088 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);
1094 },1090 },
1095 }1091 }
1096 }1092 }
10971093
1098 /// Never called when LLVM is codegenning the ZCU.1094 /// 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 {
1100 assert(base.comp.zcu.?.llvm_object == null);1096 assert(base.comp.zcu.?.llvm_object == null);
1101
1102 switch (base.tag) {1097 switch (base.tag) {
1103 .lld => unreachable,1098 .lld => unreachable,
1104 .c => unreachable,1099 .c => unreachable,
...@@ -1108,7 +1103,7 @@ pub const File = struct {...@@ -1108,7 +1103,7 @@ pub const File = struct {
1108 .spork8 => unreachable,1103 .spork8 => unreachable,
1109 inline else => |tag| {1104 inline else => |tag| {
1110 dev.check(tag.devFeature());1105 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);
1112 },1107 },
1113 }1108 }
1114 }1109 }
...@@ -1238,15 +1233,68 @@ pub const File = struct {...@@ -1238,15 +1233,68 @@ pub const File = struct {
1238 }1233 }
12391234
1240 pub fn loadInput(base: *File, input: Input) anyerror!void {1235 pub fn loadInput(base: *File, input: Input) anyerror!void {
1241 if (base.tag == .lld) return;1236 if (base.tag != .lld) {
1242 assert(!base.post_prelink);1237 assert(!base.post_prelink);
12431238 }
1244 switch (base.tag) {1239 switch (base.tag) {
1245 inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| {1240 inline else => |tag| {
1246 dev.check(tag.devFeature());1241 dev.check(tag.devFeature());
1247 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);1242 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
1248 },1243 },
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,
1250 }1298 }
1251 }1299 }
12521300
...@@ -1261,7 +1309,7 @@ pub const File = struct {...@@ -1261,7 +1309,7 @@ pub const File = struct {
1261 }1309 }
12621310
1263 switch (base.tag) {1311 switch (base.tag) {
1264 inline .elf2, .coff2, .wasm, .c => |tag| {1312 inline .elf2, .coff, .macho2, .wasm, .c => |tag| {
1265 dev.check(tag.devFeature());1313 dev.check(tag.devFeature());
1266 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);1314 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
1267 },1315 },
...@@ -1302,10 +1350,11 @@ pub const File = struct {...@@ -1302,10 +1350,11 @@ pub const File = struct {
1302 }1350 }
13031351
1304 pub const Tag = enum {1352 pub const Tag = enum {
1305 coff2,1353 coff,
1306 elf,1354 elf,
1307 elf2,1355 elf2,
1308 macho,1356 macho,
1357 macho2,
1309 c,1358 c,
1310 wasm,1359 wasm,
1311 spirv,1360 spirv,
...@@ -1315,10 +1364,11 @@ pub const File = struct {...@@ -1315,10 +1364,11 @@ pub const File = struct {
13151364
1316 pub fn Type(comptime tag: Tag) type {1365 pub fn Type(comptime tag: Tag) type {
1317 return switch (tag) {1366 return switch (tag) {
1318 .coff2 => Coff2,1367 .coff => Coff,
1319 .elf => Elf,1368 .elf => Elf,
1320 .elf2 => Elf2,1369 .elf2 => Elf2,
1321 .macho => MachO,1370 .macho => MachO,
1371 .macho2 => MachO2,
1322 .c => C,1372 .c => C,
1323 .wasm => Wasm,1373 .wasm => Wasm,
1324 .spirv => Spirv,1374 .spirv => Spirv,
...@@ -1330,9 +1380,9 @@ pub const File = struct {...@@ -1330,9 +1380,9 @@ pub const File = struct {
13301380
1331 fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {1381 fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {
1332 return switch (ofmt) {1382 return switch (ofmt) {
1333 .coff => .coff2,1383 .coff => .coff,
1334 .elf => if (use_new_linker) .elf2 else .elf,1384 .elf => if (use_new_linker) .elf2 else .elf,
1335 .macho => .macho,1385 .macho => if (use_new_linker) .macho2 else .macho,
1336 .wasm => .wasm,1386 .wasm => .wasm,
1337 .plan9 => .plan9,1387 .plan9 => .plan9,
1338 .c => .c,1388 .c => .c,
...@@ -1420,11 +1470,12 @@ pub const File = struct {...@@ -1420,11 +1470,12 @@ pub const File = struct {
14201470
1421 pub const Lld = @import("link/Lld.zig");1471 pub const Lld = @import("link/Lld.zig");
1422 pub const C = @import("link/C.zig");1472 pub const C = @import("link/C.zig");
1423 pub const Coff2 = @import("link/Coff.zig");1473 pub const Coff = @import("link/Coff.zig");
1424 pub const Spork8 = @import("link/Spork8.zig");1474 pub const Spork8 = @import("link/Spork8.zig");
1425 pub const Elf = @import("link/Elf.zig");1475 pub const Elf = @import("link/Elf.zig");
1426 pub const Elf2 = @import("link/Elf2.zig");1476 pub const Elf2 = @import("link/Elf2.zig");
1427 pub const MachO = @import("link/MachO.zig");1477 pub const MachO = @import("link/MachO.zig");
1478 pub const MachO2 = @import("link/MachO2.zig");
1428 pub const Spirv = @import("link/Spirv.zig");1479 pub const Spirv = @import("link/Spirv.zig");
1429 pub const Wasm = @import("link/Wasm.zig");1480 pub const Wasm = @import("link/Wasm.zig");
1430 pub const Dwarf = @import("link/Dwarf.zig");1481 pub const Dwarf = @import("link/Dwarf.zig");
...@@ -1449,6 +1500,8 @@ pub const PrelinkTask = union(enum) {...@@ -1449,6 +1500,8 @@ pub const PrelinkTask = union(enum) {
1449 /// Tells the linker to load a shared library, possibly one that is a1500 /// Tells the linker to load a shared library, possibly one that is a
1450 /// GNU ld script.1501 /// GNU ld script.
1451 load_dso: Path,1502 load_dso: Path,
1503 load_tbd: Path,
1504 load_darwin_sdk_settings: Path,
1452};1505};
1453pub const ZcuTask = union(enum) {1506pub const ZcuTask = union(enum) {
1454 /// Sent once per update, as the very first `ZcuTask` in the update. Indicates that all per-file1507 /// 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 {...@@ -1503,6 +1556,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1503 error.AlreadyReported => return, // error reported via diags1556 error.AlreadyReported => return, // error reported via diags
1504 else => |e| switch (input) {1557 else => |e| switch (input) {
1505 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {t}", .{e}),1558 .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}),
1506 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),1560 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),
1507 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}),1561 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}),
1508 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}),1562 .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 {...@@ -1524,34 +1578,72 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1524 assert(mem.startsWith(u8, flag, "-l"));1578 assert(mem.startsWith(u8, flag, "-l"));
1525 const lib_name = flag["-l".len..];1579 const lib_name = flag["-l".len..];
1526 switch (comp.config.link_mode) {1580 switch (comp.config.link_mode) {
1527 .dynamic => {1581 .dynamic => loaded: {
1528 const dso_path = Path.initCwd(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(
1529 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{1630 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(),
1531 }) catch return diags.setAllocFailure(),1632 }) catch return diags.setAllocFailure(),
1532 );1633 );
1533 base.openLoadDso(dso_path, .{1634 if (base.openLoadArchiveQuery(archive_path, .{
1534 .preferred_mode = .dynamic,1635 .preferred_mode = .dynamic,
1535 .search_strategy = .paths_first,1636 .search_strategy = .paths_first,
1536 }) catch |err| switch (err) {1637 })) {
1537 error.FileNotFound => {1638 break :loaded;
1538 // Also try static.1639 } else |archive_err| switch (archive_err) {
1539 const archive_path = Path.initCwd(1640 error.FileNotFound => {},
1540 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{1641 error.AlreadyReported => return,
1541 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),1642 error.Canceled => io.recancel(),
1542 }) catch return diags.setAllocFailure(),1643 else => |e| diags.addParseError(archive_path, "failed to parse archive: {s}", .{@errorName(e)}),
1543 );1644 }
1544 base.openLoadArchiveQuery(archive_path, .{1645
1545 .preferred_mode = .dynamic,1646 diags.addError("failed to find library '{s}'", .{lib_name});
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 };
1555 },1647 },
1556 .static => {1648 .static => {
1557 const path = Path.initCwd(1649 const path = Path.initCwd(
...@@ -1571,6 +1663,19 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1571,6 +1663,19 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1571 }1663 }
1572 }1664 }
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
1574 if (target.os.tag == .windows and target.abi == .msvc) {1679 if (target.os.tag == .windows and target.abi == .msvc) {
1575 const inputs: []const struct {1680 const inputs: []const struct {
1576 dir: enum { crt, msvc_lib, kernel32_lib },1681 dir: enum { crt, msvc_lib, kernel32_lib },
...@@ -1646,6 +1751,37 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1646,6 +1751,37 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1646 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),1751 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1647 };1752 };
1648 },1753 },
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 },
1649 }1785 }
1650}1786}
1651pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void {1787pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void {
...@@ -1820,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {...@@ -1820,6 +1956,7 @@ pub const UnresolvedInput = union(enum) {
1820 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?1956 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1821 /// Who knows! Fuck around and find out.1957 /// Who knows! Fuck around and find out.
1822 ambiguous_name: AmbiguousNameQuery,1958 ambiguous_name: AmbiguousNameQuery,
1959 framework_query: FrameworkQuery,
18231960
1824 pub const NameQuery = struct {1961 pub const NameQuery = struct {
1825 name: []const u8,1962 name: []const u8,
...@@ -1858,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {...@@ -1858,6 +1995,12 @@ pub const UnresolvedInput = union(enum) {
1858 }1995 }
1859 };1996 };
18601997
1998 pub const FrameworkQuery = struct {
1999 name: []const u8,
2000 needed: bool,
2001 weak: bool,
2002 };
2003
1861 pub const SearchStrategy = enum {2004 pub const SearchStrategy = enum {
1862 paths_first,2005 paths_first,
1863 mode_first,2006 mode_first,
...@@ -1872,6 +2015,8 @@ pub const Input = union(enum) {...@@ -1872,6 +2015,8 @@ pub const Input = union(enum) {
1872 /// May not be a GNU ld script. Those are resolved when converting from2015 /// May not be a GNU ld script. Those are resolved when converting from
1873 /// `UnresolvedInput` to `Input` values.2016 /// `UnresolvedInput` to `Input` values.
1874 dso: Dso,2017 dso: Dso,
2018 /// Only possible when targeting Darwin.
2019 tbd: Tbd,
18752020
1876 pub const Object = struct {2021 pub const Object = struct {
1877 path: Path,2022 path: Path,
...@@ -1896,24 +2041,32 @@ pub const Input = union(enum) {...@@ -1896,24 +2041,32 @@ pub const Input = union(enum) {
1896 pub const FallbackSoname = enum { basename, full_path };2041 pub const FallbackSoname = enum { basename, full_path };
1897 };2042 };
18982043
2044 pub const Tbd = struct {
2045 path: Path,
2046 file: Io.File,
2047 needed: bool,
2048 weak: bool,
2049 reexport: bool,
2050 };
2051
1899 pub fn path(input: Input) Path {2052 pub fn path(input: Input) Path {
1900 return switch (input) {2053 return switch (input) {
1901 .object, .archive => |obj| obj.path,2054 .object, .archive => |obj| obj.path,
1902 inline .res, .dso => |x| x.path,2055 inline .res, .dso, .tbd => |x| x.path,
1903 };2056 };
1904 }2057 }
19052058
1906 pub fn pathAndFile(input: Input) struct { Path, Io.File } {2059 pub fn pathAndFile(input: Input) struct { Path, Io.File } {
1907 return switch (input) {2060 return switch (input) {
1908 .object, .archive => |obj| .{ obj.path, obj.file },2061 .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 },
1910 };2063 };
1911 }2064 }
19122065
1913 pub fn taskName(input: Input) []const u8 {2066 pub fn taskName(input: Input) []const u8 {
1914 return switch (input) {2067 return switch (input) {
1915 .object, .archive => |obj| obj.path.basename(),2068 .object, .archive => |obj| obj.path.basename(),
1916 inline .res, .dso => |x| x.path.basename(),2069 inline .res, .dso, .tbd => |x| x.path.basename(),
1917 };2070 };
1918 }2071 }
1919};2072};
...@@ -1946,6 +2099,15 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {...@@ -1946,6 +2099,15 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
1946 man.hash.add(dso.reexport);2099 man.hash.add(dso.reexport);
1947 man.hash.add(dso.fallback_soname);2100 man.hash.add(dso.fallback_soname);
1948 },2101 },
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 },
1949 }2111 }
1950 }2112 }
1951}2113}
...@@ -1961,6 +2123,7 @@ pub fn resolveInputs(...@@ -1961,6 +2123,7 @@ pub fn resolveInputs(
1961 /// Allocated with `gpa`.2123 /// Allocated with `gpa`.
1962 resolved_inputs: *std.ArrayList(Input),2124 resolved_inputs: *std.ArrayList(Input),
1963 lib_directories: []const Cache.Directory,2125 lib_directories: []const Cache.Directory,
2126 framework_directories: []const Cache.Directory,
1964 color: std.zig.Color,2127 color: std.zig.Color,
1965) Allocator.Error!void {2128) Allocator.Error!void {
1966 var checked_paths: std.ArrayList(u8) = .empty;2129 var checked_paths: std.ArrayList(u8) = .empty;
...@@ -1972,6 +2135,7 @@ pub fn resolveInputs(...@@ -1972,6 +2135,7 @@ pub fn resolveInputs(
1972 var archive_dedup: ArchiveDedupMap = .empty;2135 var archive_dedup: ArchiveDedupMap = .empty;
1973 defer archive_dedup.deinit(gpa);2136 defer archive_dedup.deinit(gpa);
19742137
2138 // Allocated with `arena`.
1975 var failed_libs: std.ArrayList(struct {2139 var failed_libs: std.ArrayList(struct {
1976 name: []const u8,2140 name: []const u8,
1977 strategy: UnresolvedInput.SearchStrategy,2141 strategy: UnresolvedInput.SearchStrategy,
...@@ -1979,6 +2143,12 @@ pub fn resolveInputs(...@@ -1979,6 +2143,12 @@ pub fn resolveInputs(
1979 preferred_mode: std.lang.LinkMode,2143 preferred_mode: std.lang.LinkMode,
1980 }) = .empty;2144 }) = .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
1982 // Convert external system libs into a stack so that items can be2152 // Convert external system libs into a stack so that items can be
1983 // pushed to it.2153 // pushed to it.
1984 //2154 //
...@@ -2180,17 +2350,47 @@ pub fn resolveInputs(...@@ -2180,17 +2350,47 @@ pub fn resolveInputs(
2180 }2350 }
2181 continue;2351 continue;
2182 },2352 },
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 },
2183 }2377 }
2184 comptime unreachable;2378 comptime unreachable;
2185 }2379 }
21862380
2187 if (failed_libs.items.len > 0) {2381 if (failed_libs.items.len > 0 or failed_frameworks.items.len > 0) {
2188 for (failed_libs.items) |f| {2382 for (failed_libs.items) |f| {
2189 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;2383 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2190 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{2384 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{
2191 f.preferred_mode, f.name, f.strategy, searched_paths,2385 f.preferred_mode, f.name, f.strategy, searched_paths,
2192 });2386 });
2193 }2387 }
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 }
2194 std.process.exit(1);2394 std.process.exit(1);
2195 }2395 }
2196}2396}
...@@ -2235,16 +2435,14 @@ fn resolveLibInput(...@@ -2235,16 +2435,14 @@ fn resolveLibInput(
2235 else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }),2435 else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }),
2236 };2436 };
2237 errdefer file.close(io);2437 errdefer file.close(io);
2238 return finishResolveLibInput(2438 resolved_inputs.appendAssumeCapacity(.{ .tbd = .{
2239 io,2439 .path = test_path,
2240 resolved_inputs,2440 .file = file,
2241 archive_dedup,2441 .needed = name_query.query.needed,
2242 test_path,2442 .weak = name_query.query.weak,
2243 file,2443 .reexport = name_query.query.reexport,
2244 link_mode,2444 } });
2245 name_query.query,2445 return .ok;
2246 .basename,
2247 );
2248 }2446 }
22492447
2250 {2448 {
...@@ -2282,16 +2480,15 @@ fn resolveLibInput(...@@ -2282,16 +2480,15 @@ fn resolveLibInput(
2282 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),2480 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
2283 };2481 };
2284 errdefer file.close(io);2482 errdefer file.close(io);
2285 return finishResolveLibInput(2483 resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2286 io,2484 .path = test_path,
2287 resolved_inputs,2485 .file = file,
2288 archive_dedup,2486 .needed = name_query.query.needed,
2289 test_path,2487 .weak = name_query.query.weak,
2290 file,2488 .reexport = name_query.query.reexport,
2291 link_mode,2489 .fallback_soname = .basename,
2292 name_query.query,2490 } });
2293 .basename,2491 return .ok;
2294 );
2295 }2492 }
22962493
2297 // In the case of MinGW, the main check will be .lib but we also need to2494 // In the case of MinGW, the main check will be .lib but we also need to
...@@ -2307,16 +2504,13 @@ fn resolveLibInput(...@@ -2307,16 +2504,13 @@ fn resolveLibInput(
2307 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),2504 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
2308 };2505 };
2309 errdefer file.close(io);2506 errdefer file.close(io);
2310 return finishResolveLibInput(2507 addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2311 io,2508 .path = test_path,
2312 resolved_inputs,2509 .file = file,
2313 archive_dedup,2510 .must_link = name_query.query.must_link,
2314 test_path,2511 .hidden = name_query.query.hidden,
2315 file,2512 });
2316 link_mode,2513 return .ok;
2317 name_query.query,
2318 .basename,
2319 );
2320 }2514 }
23212515
2322 // In the case of OpenBSD, dynamic libraries are always versioned, without2516 // In the case of OpenBSD, dynamic libraries are always versioned, without
...@@ -2378,6 +2572,145 @@ fn resolveLibInput(...@@ -2378,6 +2572,145 @@ fn resolveLibInput(
2378 return .no_match;2572 return .no_match;
2379}2573}
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
2381/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so2714/// Deduplicates static archive link inputs based on their path. This is done for efficiency, so
2382/// that linker implementations do not need to open and scan the archive just to determine that they2715/// that linker implementations do not need to open and scan the archive just to determine that they
2383/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions2716/// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions
...@@ -2401,45 +2734,21 @@ const ArchiveDedupAdapter = struct {...@@ -2401,45 +2734,21 @@ const ArchiveDedupAdapter = struct {
2401 }2734 }
2402};2735};
24032736
2404fn finishResolveLibInput(2737fn addResolvedStaticLibInput(
2405 io: Io,2738 io: Io,
2406 resolved_inputs: *std.ArrayList(Input),2739 resolved_inputs: *std.ArrayList(Input),
2407 archive_dedup: *ArchiveDedupMap,2740 archive_dedup: *ArchiveDedupMap,
2408 path: Path,2741 archive: Input.Object,
2409 file: Io.File,2742) void {
2410 link_mode: std.lang.LinkMode,2743 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };
2411 query: UnresolvedInput.Query,2744 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(archive.path, ctx);
2412 fallback_soname: Input.Dso.FallbackSoname,2745 if (gop.found_existing) {
2413) ResolveLibInputResult {2746 // Ignore duplicate archive input
2414 switch (link_mode) {2747 archive.file.close(io);
2415 .static => {2748 } else {
2416 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items };2749 gop.key_ptr.* = @intCast(resolved_inputs.items.len);
2417 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx);2750 resolved_inputs.appendAssumeCapacity(.{ .archive = archive });
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 }),
2441 }2751 }
2442 return .ok;
2443}2752}
24442753
2445fn resolvePathInput(2754fn resolvePathInput(
...@@ -2535,19 +2844,24 @@ fn resolvePathInputLib(...@@ -2535,19 +2844,24 @@ fn resolvePathInputLib(
2535 try archive_dedup.ensureUnusedCapacity(gpa, 1);2844 try archive_dedup.ensureUnusedCapacity(gpa, 1);
25362845
2537 const test_path: Path = pq.path;2846 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
2538 // In the case of shared libraries, they might actually be "linker scripts"2856 // In the case of shared libraries, they might actually be "linker scripts"
2539 // that contain references to other libraries.2857 // 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)) {2858 ld_script: {
2541 .static_library, .shared_library => true,2859 if (!pq.query.allow_so_scripts) break :ld_script;
2542 else => false,2860 if (target.ofmt != .elf) break :ld_script;
2543 }) {2861 switch (Compilation.classifyFileExt(test_path.sub_path)) {
2544 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2862 .static_library, .shared_library => {},
2545 error.FileNotFound => return .no_match,2863 else => break :ld_script,
2546 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{2864 }
2547 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2548 }),
2549 };
2550 errdefer file.close(io);
2551 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));2865 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
2552 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|2866 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
2553 fatal("failed to read {qf}: {t}", .{ test_path, err });2867 fatal("failed to read {qf}: {t}", .{ test_path, err });
...@@ -2557,16 +2871,7 @@ fn resolvePathInputLib(...@@ -2557,16 +2871,7 @@ fn resolvePathInputLib(
2557 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))2871 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
2558 {2872 {
2559 // Appears to be an ELF or archive file.2873 // Appears to be an ELF or archive file.
2560 return finishResolveLibInput(2874 break :ld_script;
2561 io,
2562 resolved_inputs,
2563 archive_dedup,
2564 test_path,
2565 file,
2566 link_mode,
2567 pq.query,
2568 fallback_soname,
2569 );
2570 }2875 }
2571 const stat = file.stat(io) catch |err|2876 const stat = file.stat(io) catch |err|
2572 fatal("failed to stat {f}: {t}", .{ test_path, err });2877 fatal("failed to stat {f}: {t}", .{ test_path, err });
...@@ -2630,21 +2935,23 @@ fn resolvePathInputLib(...@@ -2630,21 +2935,23 @@ fn resolvePathInputLib(
2630 return .ok;2935 return .ok;
2631 }2936 }
26322937
2633 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2938 switch (link_mode) {
2634 error.FileNotFound => return .no_match,2939 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2635 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),2940 .path = test_path,
2636 };2941 .file = file,
2637 errdefer file.close(io);2942 .needed = pq.query.needed,
2638 return finishResolveLibInput(2943 .weak = pq.query.weak,
2639 io,2944 .reexport = pq.query.reexport,
2640 resolved_inputs,2945 .fallback_soname = fallback_soname,
2641 archive_dedup,2946 } }),
2642 test_path,2947 .static => addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{
2643 file,2948 .path = test_path,
2644 link_mode,2949 .file = file,
2645 pq.query,2950 .must_link = pq.query.must_link,
2646 fallback_soname,2951 .hidden = pq.query.hidden,
2647 );2952 }),
2953 }
2954 return .ok;
2648}2955}
26492956
2650pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {2957pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {
...@@ -2699,7 +3006,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool {...@@ -2699,7 +3006,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool {
2699pub fn countObjectInputs(inputs: []const Input) usize {3006pub fn countObjectInputs(inputs: []const Input) usize {
2700 var count: usize = 0;3007 var count: usize = 0;
2701 for (inputs) |input| switch (input) {3008 for (inputs) |input| switch (input) {
2702 .dso => continue,3009 .dso, .tbd => continue,
2703 .res, .object, .archive => count += 1,3010 .res, .object, .archive => count += 1,
2704 };3011 };
2705 return count;3012 return count;
...@@ -2709,7 +3016,80 @@ pub fn countObjectInputs(inputs: []const Input) usize {...@@ -2709,7 +3016,80 @@ pub fn countObjectInputs(inputs: []const Input) usize {
2709pub fn firstObjectInput(inputs: []const Input) ?Input.Object {3016pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
2710 for (inputs) |input| switch (input) {3017 for (inputs) |input| switch (input) {
2711 .object, .archive => |obj| return obj,3018 .object, .archive => |obj| return obj,
2712 .res, .dso => continue,3019 .res, .dso, .tbd => continue,
2713 };3020 };
2714 return null;3021 return null;
2715}3022}
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(...@@ -1607,7 +1607,7 @@ fn create(
1607 errdefer file.close(io);1607 errdefer file.close(io);
1608 coff.* = .{1608 coff.* = .{
1609 .base = .{1609 .base = .{
1610 .tag = .coff2,1610 .tag = .coff,
16111611
1612 .comp = comp,1612 .comp = comp,
1613 .emit = path,1613 .emit = path,
...@@ -2935,18 +2935,19 @@ fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.Na...@@ -2935,18 +2935,19 @@ fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.Na
2935 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();2935 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
2936 return @fromBackingInt(@intCast(sym_gop.index));2936 return @fromBackingInt(@intCast(sym_gop.index));
2937}2937}
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.?;
2939 const ip = &zcu.intern_pool;2940 const ip = &zcu.intern_pool;
2940 const nav = ip.getNav(nav_index);2941 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(.{
2942 .name = @"extern".name.toSlice(ip),2943 .name = @"extern".name.toSlice(ip),
2943 .lib_name = @"extern".lib_name.toSlice(ip),2944 .lib_name = @"extern".lib_name.toSlice(ip),
2944 // TODO: Threadlocal as well?2945 // TODO: Threadlocal as well?
2945 .type = if (ip.isFunctionType(nav.resolved.?.type)) .code else .data,2946 .type = if (ip.isFunctionType(nav.resolved.?.type)) .code else .data,
2946 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,2947 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,
2947 });2948 }));
2948 const nmi = try coff.navMapIndex(zcu, nav_index);2949 const nmi = try coff.navMapIndex(zcu, nav_index);
2949 return nmi.symbol(coff);2950 return @bitCast(nmi.symbol(coff));
2950}2951}
29512952
2952fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {2953fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
...@@ -2956,10 +2957,6 @@ fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {...@@ -2956,10 +2957,6 @@ fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
2956 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();2957 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
2957 return @fromBackingInt(@intCast(sym_gop.index));2958 return @fromBackingInt(@intCast(sym_gop.index));
2958}2959}
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
2964pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {2961pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
2965 const gpa = coff.base.comp.gpa;2962 const gpa = coff.base.comp.gpa;
...@@ -2972,24 +2969,8 @@ pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {...@@ -2972,24 +2969,8 @@ pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
2972 return sym_gop.value_ptr.*;2969 return sym_gop.value_ptr.*;
2973}2970}
29742971
2975pub fn getNavVAddr(2972pub fn relocSymAddr(coff: *Coff, reloc_info: link.File.RelocInfo) link.Error!void {
2976 coff: *Coff,2973 const target_si: Symbol.Index = @bitCast(reloc_info.target);
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 {
2993 try coff.addReloc(2974 try coff.addReloc(
2994 @fromBackingInt(@intCast(@backingInt(reloc_info.parent.atom_index))),2975 @fromBackingInt(@intCast(@backingInt(reloc_info.parent.atom_index))),
2995 reloc_info.offset,2976 reloc_info.offset,
...@@ -3001,10 +2982,6 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol....@@ -3001,10 +2982,6 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
3001 .I386 => .{ .I386 = .DIR32 },2982 .I386 => .{ .I386 = .DIR32 },
3002 },2983 },
3003 );2984 );
3004
3005 var vaddr: u64 = target_si.get(coff).rva;
3006 if (coff.isImage()) vaddr += coff.optionalHeaderField(.image_base);
3007 return vaddr;
3008}2985}
30092986
3010/// Caller guarantees there is capacity for one member and two nodes2987/// 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 {...@@ -3935,6 +3912,7 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3935 coff.loadDll(dso.path, &fr) catch |err|3912 coff.loadDll(dso.path, &fr) catch |err|
3936 return coff.failLoadInput(err, &fr, dso.path);3913 return coff.failLoadInput(err, &fr, dso.path);
3937 },3914 },
3915 .tbd => unreachable,
3938 }3916 }
3939}3917}
39403918
...@@ -5580,13 +5558,15 @@ pub fn updateContainerType(...@@ -5580,13 +5558,15 @@ pub fn updateContainerType(
5580 };5558 };
5581}5559}
55825560
5583pub fn lowerUav(5561pub fn uavSymbol(
5584 coff: *Coff,5562 coff: *Coff,
5585 pt: Zcu.PerThread,5563 pt: Zcu.PerThread,
5586 uav_val: InternPool.Index,5564 uav_val: InternPool.Index,
5587 uav_align: InternPool.Alignment,5565 uav_align: InternPool.Alignment,
5588) link.Error!link.File.SymbolId {5566) link.Error!link.File.SymbolId {
5589 const zcu = pt.zcu;5567 _ = pt;
5568
5569 const zcu = coff.base.comp.zcu.?;
5590 const gpa = zcu.gpa;5570 const gpa = zcu.gpa;
55915571
5592 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);5572 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
...@@ -7471,8 +7451,8 @@ fn updateExportInner(...@@ -7471,8 +7451,8 @@ fn updateExportInner(
74717451
7472 try coff.symbols.ensureUnusedCapacity(gpa, 1);7452 try coff.symbols.ensureUnusedCapacity(gpa, 1);
7473 const exported_si: Symbol.Index = switch (exp.exported) {7453 const exported_si: Symbol.Index = switch (exp.exported) {
7474 .nav => |nav| try coff.navSymbol(zcu, nav),7454 .nav => |nav| @bitCast(try coff.navSymbol(nav)),
7475 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(7455 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.uavSymbol(
7476 pt,7456 pt,
7477 uav,7457 uav,
7478 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),7458 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
src/link/Elf.zig+10-8
...@@ -463,21 +463,21 @@ pub fn deinit(self: *Elf) void {...@@ -463,21 +463,21 @@ pub fn deinit(self: *Elf) void {
463 self.dump_argv_list.deinit(gpa);463 self.dump_argv_list.deinit(gpa);
464}464}
465465
466pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {466pub fn navSymbol(self: *Elf, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId {
467 return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info);467 return self.zigObjectPtr().?.navSymbol(self, nav);
468}468}
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(
471 self: *Elf,475 self: *Elf,
472 pt: Zcu.PerThread,476 pt: Zcu.PerThread,
473 uav: InternPool.Index,477 uav: InternPool.Index,
474 explicit_alignment: InternPool.Alignment,478 explicit_alignment: InternPool.Alignment,
475) !link.File.SymbolId {479) !link.File.SymbolId {
476 return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment);480 return self.zigObjectPtr().?.uavSymbol(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);
481}481}
482482
483/// Returns end pos of collision, if any.483/// Returns end pos of collision, if any.
...@@ -722,6 +722,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {...@@ -722,6 +722,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
722 const argv = &self.dump_argv_list;722 const argv = &self.dump_argv_list;
723 switch (input) {723 switch (input) {
724 .res => unreachable,724 .res => unreachable,
725 .tbd => unreachable,
725 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),726 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
726 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),727 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),
727 }728 }
...@@ -729,6 +730,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {...@@ -729,6 +730,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
729730
730 switch (input) {731 switch (input) {
731 .res => unreachable,732 .res => unreachable,
733 .tbd => unreachable,
732 .object => |obj| try parseObject(self, obj),734 .object => |obj| try parseObject(self, obj),
733 .archive => |obj| if (self.base.isStaticLib()) {735 .archive => |obj| if (self.base.isStaticLib()) {
734 // Ignore static library inputs when generating a static library.736 // 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...@@ -917,53 +917,42 @@ pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
917 return code;917 return code;
918}918}
919919
920pub fn getNavVAddr(920pub fn navSymbol(
921 self: *ZigObject,921 zo: *ZigObject,
922 elf_file: *Elf,922 elf_file: *Elf,
923 pt: Zcu.PerThread,
924 nav_index: InternPool.Nav.Index,923 nav_index: InternPool.Nav.Index,
925 reloc_info: link.File.RelocInfo,924) link.Error!link.File.SymbolId {
926) !u64 {925 const zcu = elf_file.base.comp.zcu.?;
927 const zcu = pt.zcu;
928 const ip = &zcu.intern_pool;926 const ip = &zcu.intern_pool;
929 const nav = ip.getNav(nav_index);927 const nav = ip.getNav(nav_index);
930 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });928 if (nav.getExtern(ip)) |@"extern"| {
931 const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(929 const sym_index = try zo.getGlobalSymbol(
932 elf_file,930 elf_file,
933 nav.name.toSlice(ip),931 nav.name.toSlice(ip),
934 @"extern".lib_name.toSlice(ip),932 @"extern".lib_name.toSlice(ip),
935 ) else try self.getOrCreateMetadataForNav(zcu, nav_index);933 );
936 const this_sym = self.symbol(this_sym_index);934 if (@"extern".linkage == .weak) {
937 const vaddr = this_sym.address(.{}, elf_file);935 zo.symbol(sym_index).flags.weak = true;
938 switch (reloc_info.parent) {936 }
939 .none => unreachable,937 if (nav.resolved.?.@"threadlocal") {
940 .atom_index => |atom_index| {938 zo.symbol(sym_index).flags.is_tls = true;
941 const parent_atom = self.symbol(@backingInt(atom_index)).atom(elf_file).?;939 }
942 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);940 return @fromBackingInt(sym_index);
943 try parent_atom.addReloc(elf_file.base.comp.gpa, .{941 } else {
944 .r_offset = reloc_info.offset,942 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
945 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type,943 if (nav.resolved.?.@"threadlocal") {
946 .r_addend = reloc_info.addend,944 zo.symbol(sym_index).flags.is_tls = true;
947 }, self);945 }
948 },946 return @fromBackingInt(sym_index);
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 }),
954 }947 }
955 return @intCast(vaddr);
956}948}
957949
958pub fn getUavVAddr(950pub fn relocSymAddr(
959 self: *ZigObject,951 self: *ZigObject,
960 elf_file: *Elf,952 elf_file: *Elf,
961 uav: InternPool.Index,
962 reloc_info: link.File.RelocInfo,953 reloc_info: link.File.RelocInfo,
963) !u64 {954) !void {
964 const sym_index = self.uavs.get(uav).?.symbol_index;955 const this_sym_index = @backingInt(reloc_info.target);
965 const sym = self.symbol(sym_index);
966 const vaddr = sym.address(.{}, elf_file);
967 switch (reloc_info.parent) {956 switch (reloc_info.parent) {
968 .none => unreachable,957 .none => unreachable,
969 .atom_index => |atom_index| {958 .atom_index => |atom_index| {
...@@ -971,20 +960,19 @@ pub fn getUavVAddr(...@@ -971,20 +960,19 @@ pub fn getUavVAddr(
971 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);960 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
972 try parent_atom.addReloc(elf_file.base.comp.gpa, .{961 try parent_atom.addReloc(elf_file.base.comp.gpa, .{
973 .r_offset = reloc_info.offset,962 .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,
975 .r_addend = reloc_info.addend,964 .r_addend = reloc_info.addend,
976 }, self);965 }, self);
977 },966 },
978 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{967 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
979 .source_off = @intCast(reloc_info.offset),968 .source_off = @intCast(reloc_info.offset),
980 .target_sym = @fromBackingInt(@intCast(sym_index)),969 .target_sym = @fromBackingInt(@intCast(this_sym_index)),
981 .target_off = reloc_info.addend,970 .target_off = reloc_info.addend,
982 }),971 }),
983 }972 }
984 return @intCast(vaddr);
985}973}
986974
987pub fn lowerUav(975pub fn uavSymbol(
988 self: *ZigObject,976 self: *ZigObject,
989 elf_file: *Elf,977 elf_file: *Elf,
990 pt: Zcu.PerThread,978 pt: Zcu.PerThread,
...@@ -1889,7 +1877,7 @@ pub fn updateExports(...@@ -1889,7 +1877,7 @@ pub fn updateExports(
1889 break :blk self.navs.getPtr(nav).?;1877 break :blk self.navs.getPtr(nav).?;
1890 },1878 },
1891 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1879 .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);
1893 break :blk self.uavs.getPtr(uav).?;1881 break :blk self.uavs.getPtr(uav).?;
1894 },1882 },
1895 };1883 };
src/link/Elf2.zig+6-35
...@@ -4324,36 +4324,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.Fil...@@ -4324,36 +4324,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.Fil
4324 const s: Symbol.Id = .local(nmi.symbol(elf));4324 const s: Symbol.Id = .local(nmi.symbol(elf));
4325 return s.toTypeErased();4325 return s.toTypeErased();
4326}4326}
4327pub fn uavSymbol(4327pub fn relocSymAddr(elf: *Elf, reloc_info: link.File.RelocInfo) link.Error!void {
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 {
4357 try elf.addReloc(4328 try elf.addReloc(
4358 switch (reloc_info.parent) {4329 switch (reloc_info.parent) {
4359 .none => unreachable,4330 .none => unreachable,
...@@ -4361,13 +4332,12 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy...@@ -4361,13 +4332,12 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy
4361 .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni),4332 .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni),
4362 },4333 },
4363 reloc_info.offset,4334 reloc_info.offset,
4364 target,4335 reloc_info.target,
4365 reloc_info.addend,4336 reloc_info.addend,
4366 .absAddr(elf),4337 .absAddr(elf),
4367 );4338 );
4368 return Symbol.Id.fromTypeErased(target).value(elf);
4369}4339}
4370pub fn lowerUav(4340pub fn uavSymbol(
4371 elf: *Elf,4341 elf: *Elf,
4372 pt: Zcu.PerThread,4342 pt: Zcu.PerThread,
4373 uav_val: InternPool.Index,4343 uav_val: InternPool.Index,
...@@ -7176,7 +7146,6 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void...@@ -7176,7 +7146,6 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
7176 },7146 },
7177 };7147 };
7178 },7148 },
7179 .res => unreachable,
7180 .dso => |dso| {7149 .dso => |dso| {
7181 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);7150 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
7182 var fr = dso.file.reader(io, &buf);7151 var fr = dso.file.reader(io, &buf);
...@@ -7193,6 +7162,8 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void...@@ -7193,6 +7162,8 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
7193 },7162 },
7194 };7163 };
7195 },7164 },
7165 .res => unreachable,
7166 .tbd => unreachable,
7196 }7167 }
7197}7168}
7198fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {7169fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
...@@ -12384,7 +12355,7 @@ pub fn printNode(...@@ -12384,7 +12355,7 @@ pub fn printNode(
12384 mf_node.flags.alignment.toByteUnits(),12355 mf_node.flags.alignment.toByteUnits(),
12385 mf_node.flags.position,12356 mf_node.flags.position,
12386 if (mf_node.flags.bubbles_moved) " bubbles_moved" else "",12357 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 "",
12388 if (mf_node.flags.resized) " resized" else "",12359 if (mf_node.flags.resized) " resized" else "",
12389 if (mf_node.flags.enable_next_moved) " enable_next_moved" else "",12360 if (mf_node.flags.enable_next_moved) " enable_next_moved" else "",
12390 if (mf_node.flags.next_moved) " next_moved" else "",12361 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 {...@@ -309,7 +309,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void {
309309
310 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);310 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);
311 for (comp.link_inputs) |input| switch (input) {311 for (comp.link_inputs) |input| switch (input) {
312 .dso, .archive => {}, // static archives should not contain shared libraries or other static archives312 .dso, .tbd, .archive => {}, // static archives should not contain shared libraries or other static archives
313 .res, .object => {313 .res, .object => {
314 const path = try input.path().toStringZ(arena);314 const path = try input.path().toStringZ(arena);
315 object_files.appendAssumeCapacity(path);315 object_files.appendAssumeCapacity(path);
...@@ -561,6 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -561,6 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
561 argv.appendAssumeCapacity(try obj.path.toString(arena));561 argv.appendAssumeCapacity(try obj.path.toString(arena));
562 }562 }
563 },563 },
564 .tbd => unreachable,
564 };565 };
565566
566 for (comp.c_objects.items) |c_object| {567 for (comp.c_objects.items) |c_object| {
...@@ -1102,6 +1103,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1102,6 +1103,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11021103
1103 for (base.comp.link_inputs) |link_input| switch (link_input) {1104 for (base.comp.link_inputs) |link_input| switch (link_input) {
1104 .res => unreachable, // Windows-only1105 .res => unreachable, // Windows-only
1106 .tbd => unreachable, // Darwin-only
1105 .dso => continue,1107 .dso => continue,
1106 .object, .archive => |obj| {1108 .object, .archive => |obj| {
1107 if (obj.must_link and !whole_archive) {1109 if (obj.must_link and !whole_archive) {
...@@ -1163,6 +1165,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1163,6 +1165,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11631165
1164 for (base.comp.link_inputs) |link_input| switch (link_input) {1166 for (base.comp.link_inputs) |link_input| switch (link_input) {
1165 .res => unreachable, // Windows-only1167 .res => unreachable, // Windows-only
1168 .tbd => unreachable, // Darwin-only
1166 .object, .archive => continue,1169 .object, .archive => continue,
1167 .dso => |dso| {1170 .dso => |dso| {
1168 const lib_as_needed = !dso.needed;1171 const lib_as_needed = !dso.needed;
...@@ -1596,6 +1599,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1596,6 +1599,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1596 .dso => |dso| {1599 .dso => |dso| {
1597 try argv.append(try dso.path.toString(arena));1600 try argv.append(try dso.path.toString(arena));
1598 },1601 },
1602 .tbd => unreachable,
1599 .res => unreachable,1603 .res => unreachable,
1600 };1604 };
1601 if (whole_archive) {1605 if (whole_archive) {
src/link/MachO.zig+88-318
...@@ -4,6 +4,8 @@ pub const Relocation = @import("MachO/Relocation.zig");...@@ -4,6 +4,8 @@ pub const Relocation = @import("MachO/Relocation.zig");
44
5base: link.File,5base: link.File,
66
7all_inputs: std.ArrayList(link.Input),
8
7rpath_list: []const []const u8,9rpath_list: []const []const u8,
810
9/// Debug symbols bundle (or dSym).11/// Debug symbols bundle (or dSym).
...@@ -104,9 +106,7 @@ has_tlv: AtomicBool = AtomicBool.init(false),...@@ -104,9 +106,7 @@ has_tlv: AtomicBool = AtomicBool.init(false),
104binds_to_weak: AtomicBool = AtomicBool.init(false),106binds_to_weak: AtomicBool = AtomicBool.init(false),
105weak_defines: AtomicBool = AtomicBool.init(false),107weak_defines: AtomicBool = AtomicBool.init(false),
106108
107/// Options109// Options
108/// SDK layout
109sdk_layout: ?SdkLayout,
110/// Size of the __PAGEZERO segment.110/// Size of the __PAGEZERO segment.
111pagezero_size: ?u64,111pagezero_size: ?u64,
112/// Minimum space for future expansion of the load commands.112/// Minimum space for future expansion of the load commands.
...@@ -119,10 +119,6 @@ dead_strip_dylibs: bool,...@@ -119,10 +119,6 @@ dead_strip_dylibs: bool,
119undefined_treatment: UndefinedTreatment,119undefined_treatment: UndefinedTreatment,
120/// TODO: delete this, libraries need to be resolved by the frontend instead120/// TODO: delete this, libraries need to be resolved by the frontend instead
121lib_directories: []const Directory,121lib_directories: []const Directory,
122/// Resolved list of framework search directories
123framework_dirs: []const []const u8,
124/// List of input frameworks
125frameworks: []const Framework,
126/// Install name for the dylib.122/// Install name for the dylib.
127/// TODO: unify with soname123/// TODO: unify with soname
128install_name: ?[]const u8,124install_name: ?[]const u8,
...@@ -132,7 +128,7 @@ compatibility_version: ?std.SemanticVersion,...@@ -132,7 +128,7 @@ compatibility_version: ?std.SemanticVersion,
132/// Entry name128/// Entry name
133entry_name: ?[]const u8,129entry_name: ?[]const u8,
134platform: Platform,130platform: Platform,
135sdk_version: ?std.SemanticVersion,131sdk_version: ?link.DarwinSdkVersion,
136/// When set to true, the linker will hoist all dylibs including system dependent dylibs.132/// When set to true, the linker will hoist all dylibs including system dependent dylibs.
137no_implicit_dylibs: bool = false,133no_implicit_dylibs: bool = false,
138/// Whether the linker should parse and always force load objects containing ObjC in archives.134/// Whether the linker should parse and always force load objects containing ObjC in archives.
...@@ -190,13 +186,12 @@ pub fn createEmpty(...@@ -190,13 +186,12 @@ pub fn createEmpty(
190 .file = null,186 .file = null,
191 .build_id = options.build_id,187 .build_id = options.build_id,
192 },188 },
189 .all_inputs = .empty,
193 .rpath_list = options.rpath_list,190 .rpath_list = options.rpath_list,
194 .pagezero_size = options.pagezero_size,191 .pagezero_size = options.pagezero_size,
195 .headerpad_size = options.headerpad_size,192 .headerpad_size = options.headerpad_size,
196 .headerpad_max_install_names = options.headerpad_max_install_names,193 .headerpad_max_install_names = options.headerpad_max_install_names,
197 .dead_strip_dylibs = options.dead_strip_dylibs,194 .dead_strip_dylibs = options.dead_strip_dylibs,
198 .sdk_layout = options.darwin_sdk_layout,
199 .frameworks = options.frameworks,
200 .install_name = options.install_name,195 .install_name = options.install_name,
201 .entitlements = options.entitlements,196 .entitlements = options.entitlements,
202 .compatibility_version = options.compatibility_version,197 .compatibility_version = options.compatibility_version,
...@@ -207,11 +202,10 @@ pub fn createEmpty(...@@ -207,11 +202,10 @@ pub fn createEmpty(
207 .named => |name| name,202 .named => |name| name,
208 },203 },
209 .platform = Platform.fromTarget(target),204 .platform = Platform.fromTarget(target),
210 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,205 .sdk_version = null,
211 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",206 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
212 // TODO delete this, directories must instead be resolved by the frontend207 // TODO delete this, directories must instead be resolved by the frontend
213 .lib_directories = options.lib_directories,208 .lib_directories = options.lib_directories,
214 .framework_dirs = options.framework_dirs,
215 .force_load_objc = options.force_load_objc,209 .force_load_objc = options.force_load_objc,
216 .discard_local_symbols = options.discard_local_symbols,210 .discard_local_symbols = options.discard_local_symbols,
217 };211 };
...@@ -271,6 +265,8 @@ pub fn deinit(self: *MachO) void {...@@ -271,6 +265,8 @@ pub fn deinit(self: *MachO) void {
271 const gpa = comp.gpa;265 const gpa = comp.gpa;
272 const io = comp.io;266 const io = comp.io;
273267
268 self.all_inputs.deinit(gpa);
269
274 if (self.d_sym) |*d_sym| {270 if (self.d_sym) |*d_sym| {
275 d_sym.deinit();271 d_sym.deinit();
276 }272 }
...@@ -334,6 +330,11 @@ pub fn deinit(self: *MachO) void {...@@ -334,6 +330,11 @@ pub fn deinit(self: *MachO) void {
334 self.thunks.deinit(gpa);330 self.thunks.deinit(gpa);
335}331}
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
337pub fn flush(338pub fn flush(
338 self: *MachO,339 self: *MachO,
339 arena: Allocator,340 arena: Allocator,
...@@ -343,6 +344,8 @@ pub fn flush(...@@ -343,6 +344,8 @@ pub fn flush(
343 const tracy = trace(@src());344 const tracy = trace(@src());
344 defer tracy.end();345 defer tracy.end();
345346
347 _ = arena;
348
346 const comp = self.base.comp;349 const comp = self.base.comp;
347 const gpa = comp.gpa;350 const gpa = comp.gpa;
348 const io = comp.io;351 const io = comp.io;
...@@ -351,146 +354,40 @@ pub fn flush(...@@ -351,146 +354,40 @@ pub fn flush(
351 const sub_prog_node = prog_node.start("MachO Flush", 0);354 const sub_prog_node = prog_node.start("MachO Flush", 0);
352 defer sub_prog_node.end();355 defer sub_prog_node.end();
353356
354 const zcu_obj_path: ?Path = p: {357 // TODO: due to https://codeberg.org/ziglang/zig/issues/36946, we receive link inputs in a
355 const zcu = comp.zcu orelse break :p null;358 // non-deterministic order. To ensure that this linker behaves deterministically, sort the
356 const llvm_object = zcu.llvm_object orelse break :p null;359 // inputs into a consistent order now.
357 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);360 {
358 };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
360 // --verbose-link381 // --verbose-link
361 if (comp.verbose_link) try self.dumpArgv(comp);382 if (comp.verbose_link) try self.dumpArgv(comp);
362383
363 if (self.getZigObject()) |zo| try zo.flush(self, tid);384 if (self.getZigObject()) |zo| try zo.flush(self, tid);
364 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path);385 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp);
365 if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path);386 if (self.base.isObject()) return relocatable.flushObject(self, comp);
366387
367 var positionals = std.array_list.Managed(link.Input).init(gpa);388 for (self.all_inputs.items) |input| {
368 defer positionals.deinit();389 self.classifyInputFile(input) catch |err|
369390 diags.addParseError(input.path(), "failed to read input file: {t}", .{err});
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)});
494 }391 }
495392
496 try self.parseInputFiles();393 try self.parseInputFiles();
...@@ -666,9 +563,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -666,9 +563,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
666 for (comp.link_inputs) |link_input| switch (link_input) {563 for (comp.link_inputs) |link_input| switch (link_input) {
667 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),564 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
668 .res => |res| try argv.append(try res.path.toString(arena)),565 .res => |res| try argv.append(try res.path.toString(arena)),
669 .dso => |dso| {566 .dso => |dso| try argv.append(try dso.path.toString(arena)),
670 try argv.append(try dso.path.toString(arena));567 .tbd => |tbd| try argv.append(try tbd.path.toString(arena)),
671 },
672 };568 };
673569
674 for (comp.c_objects.items) |c_object| {570 for (comp.c_objects.items) |c_object| {
...@@ -694,12 +590,20 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -694,12 +590,20 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
694590
695 try argv.append("-platform_version");591 try argv.append("-platform_version");
696 try argv.append(@tagName(self.platform.os_tag));592 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
699 if (self.sdk_version) |ver| {599 if (self.sdk_version) |ver| {
700 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));600 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
701 } else {601 } 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 }));
703 }607 }
704608
705 if (comp.sysroot) |syslibroot| {609 if (comp.sysroot) |syslibroot| {
...@@ -754,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -754,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
754 }658 }
755659
756 for (comp.link_inputs) |link_input| switch (link_input) {660 for (comp.link_inputs) |link_input| switch (link_input) {
757 .dso => continue, // handled below661 .dso, .tbd => continue, // handled below
758 .res => unreachable, // windows only662 .res => unreachable, // windows only
759 .object, .archive => |obj| {663 .object, .archive => |obj| {
760 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this664 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
...@@ -788,7 +692,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -788,7 +692,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
788 for (comp.link_inputs) |link_input| switch (link_input) {692 for (comp.link_inputs) |link_input| switch (link_input) {
789 .object, .archive => continue, // handled above693 .object, .archive => continue, // handled above
790 .res => unreachable, // windows only694 .res => unreachable, // windows only
791 .dso => |dso| {695 inline .dso, .tbd => |dso| {
792 try argv.ensureUnusedCapacity(2);696 try argv.ensureUnusedCapacity(2);
793 if (dso.needed) {697 if (dso.needed) {
794 argv.appendAssumeCapacity("-needed-l");698 argv.appendAssumeCapacity("-needed-l");
...@@ -801,20 +705,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -801,20 +705,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
801 },705 },
802 };706 };
803707
804 for (self.framework_dirs) |f_dir| {708 for (comp.framework_dirs) |f_dir| {
805 try argv.append("-F");709 try argv.append("-F");
806 try argv.append(f_dir);710 try argv.append(f_dir.path orelse ".");
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);
818 }711 }
819712
820 if (comp.config.link_libcpp) {713 if (comp.config.link_libcpp) {
...@@ -836,46 +729,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -836,46 +729,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
836 try Compilation.dumpArgv(io, argv.items);729 try Compilation.dumpArgv(io, argv.items);
837}730}
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
879pub fn classifyInputFile(self: *MachO, input: link.Input) !void {732pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
880 const tracy = trace(@src());733 const tracy = trace(@src());
881 defer tracy.end();734 defer tracy.end();
...@@ -888,7 +741,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {...@@ -888,7 +741,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
888 log.debug("classifying input file {f}", .{path});741 log.debug("classifying input file {f}", .{path});
889742
890 const fh = try self.addFileHandle(file);743 const fh = try self.addFileHandle(file);
891 var buffer: [Archive.SARMAG]u8 = undefined;744 var buffer: [macho.ARMAG.len]u8 = undefined;
892745
893 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);746 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
894 const offset = if (fat_arch) |fa| fa.offset else 0;747 const offset = if (fat_arch) |fa| fa.offset else 0;
...@@ -903,7 +756,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {...@@ -903,7 +756,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
903 return;756 return;
904 }757 }
905 if (readArMagic(io, file, offset, &buffer) catch null) |ar_magic| blk: {758 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;
907 try self.addArchive(input.archive, fh, fat_arch);760 try self.addArchive(input.archive, fh, fat_arch);
908 return;761 return;
909 }762 }
...@@ -933,10 +786,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6...@@ -933,10 +786,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6
933 return hdr;786 return hdr;
934}787}
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 {
937 const nread = try file.readPositionalAll(io, buffer, offset);790 const nread = try file.readPositionalAll(io, buffer, offset);
938 if (nread != buffer.len) return error.InputOutput;791 if (nread != buffer.len) return error.InputOutput;
939 return buffer[0..Archive.SARMAG];792 return buffer[0..macho.ARMAG.len];
940}793}
941794
942fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {795fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void {
...@@ -1159,10 +1012,6 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1159,10 +1012,6 @@ fn parseDependentDylibs(self: *MachO) !void {
1159 const comp = self.base.comp;1012 const comp = self.base.comp;
1160 const gpa = comp.gpa;1013 const gpa = comp.gpa;
1161 const io = comp.io;1014 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
1167 var arena_alloc = std.heap.ArenaAllocator.init(gpa);1016 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1168 defer arena_alloc.deinit();1017 defer arena_alloc.deinit();
...@@ -1197,14 +1046,14 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1197,14 +1046,14 @@ fn parseDependentDylibs(self: *MachO) !void {
1197 const stem = fs.path.stem(id.name);1046 const stem = fs.path.stem(id.name);
11981047
1199 // Framework1048 // Framework
1200 for (framework_dirs) |dir| {1049 for (comp.framework_dirs) |dir| {
1201 test_path.clearRetainingCapacity();1050 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;
1203 }1052 }
12041053
1205 // Library1054 // Library
1206 const lib_name = eatPrefix(stem, "lib") orelse stem;1055 const lib_name = eatPrefix(stem, "lib") orelse stem;
1207 for (lib_directories) |lib_directory| {1056 for (self.lib_directories) |lib_directory| {
1208 test_path.clearRetainingCapacity();1057 test_path.clearRetainingCapacity();
1209 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;1058 if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
1210 }1059 }
...@@ -3124,21 +2973,21 @@ pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {...@@ -3124,21 +2973,21 @@ pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {
3124 return self.getZigObject().?.freeNav(nav);2973 return self.getZigObject().?.freeNav(nav);
3125}2974}
31262975
3127pub fn getNavVAddr(self: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {2976pub fn navSymbol(self: *MachO, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId {
3128 return self.getZigObject().?.getNavVAddr(self, pt, nav_index, reloc_info);2977 return self.getZigObject().?.navSymbol(self, nav);
3129}2978}
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(
3132 self: *MachO,2985 self: *MachO,
3133 pt: Zcu.PerThread,2986 pt: Zcu.PerThread,
3134 uav: InternPool.Index,2987 uav: InternPool.Index,
3135 explicit_alignment: InternPool.Alignment,2988 explicit_alignment: InternPool.Alignment,
3136) !link.File.SymbolId {2989) !link.File.SymbolId {
3137 return self.getZigObject().?.lowerUav(self, pt, uav, explicit_alignment);2990 return self.getZigObject().?.uavSymbol(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);
3142}2991}
31432992
3144pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {2993pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
...@@ -4231,7 +4080,7 @@ pub const null_sym = macho.nlist_64{...@@ -4231,7 +4080,7 @@ pub const null_sym = macho.nlist_64{
4231pub const Platform = struct {4080pub const Platform = struct {
4232 os_tag: std.Target.Os.Tag,4081 os_tag: std.Target.Os.Tag,
4233 abi: std.Target.Abi,4082 abi: std.Target.Abi,
4234 version: std.SemanticVersion,4083 version: link.DarwinSdkVersion,
42354084
4236 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to4085 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
4237 /// the extracted minimum platform version.4086 /// the extracted minimum platform version.
...@@ -4258,7 +4107,7 @@ pub const Platform = struct {...@@ -4258,7 +4107,7 @@ pub const Platform = struct {
4258 => .simulator,4107 => .simulator,
4259 else => .none,4108 else => .none,
4260 },4109 },
4261 .version = appleVersionToSemanticVersion(cmd.minos),4110 .version = @fromBackingInt(cmd.minos),
4262 };4111 };
4263 },4112 },
4264 .VERSION_MIN_IPHONEOS,4113 .VERSION_MIN_IPHONEOS,
...@@ -4277,7 +4126,7 @@ pub const Platform = struct {...@@ -4277,7 +4126,7 @@ pub const Platform = struct {
4277 else => unreachable,4126 else => unreachable,
4278 },4127 },
4279 .abi = .none,4128 .abi = .none,
4280 .version = appleVersionToSemanticVersion(cmd.version),4129 .version = @fromBackingInt(cmd.version),
4281 };4130 };
4282 },4131 },
4283 else => unreachable,4132 else => unreachable,
...@@ -4285,17 +4134,18 @@ pub const Platform = struct {...@@ -4285,17 +4134,18 @@ pub const Platform = struct {
4285 }4134 }
42864135
4287 pub fn fromTarget(target: *const std.Target) Platform {4136 pub fn fromTarget(target: *const std.Target) Platform {
4137 const semver = target.os.version_range.semver.min;
4288 return .{4138 return .{
4289 .os_tag = target.os.tag,4139 .os_tag = target.os.tag,
4290 .abi = target.abi,4140 .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 },
4292 };4146 };
4293 }4147 }
42944148
4295 pub fn toAppleVersion(plat: Platform) u32 {
4296 return semanticVersionToAppleVersion(plat.version);
4297 }
4298
4299 pub fn toApplePlatform(plat: Platform) macho.PLATFORM {4149 pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
4300 return switch (plat.os_tag) {4150 return switch (plat.os_tag) {
4301 .driverkit => .DRIVERKIT,4151 .driverkit => .DRIVERKIT,
...@@ -4312,7 +4162,7 @@ pub const Platform = struct {...@@ -4312,7 +4162,7 @@ pub const Platform = struct {
4312 pub fn isBuildVersionCompatible(plat: Platform) bool {4162 pub fn isBuildVersionCompatible(plat: Platform) bool {
4313 inline for (supported_platforms) |sup_plat| {4163 inline for (supported_platforms) |sup_plat| {
4314 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {4164 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);
4316 }4166 }
4317 }4167 }
4318 return false;4168 return false;
...@@ -4321,7 +4171,7 @@ pub const Platform = struct {...@@ -4321,7 +4171,7 @@ pub const Platform = struct {
4321 pub fn isVersionMinCompatible(plat: Platform) bool {4171 pub fn isVersionMinCompatible(plat: Platform) bool {
4322 inline for (supported_platforms) |sup_plat| {4172 inline for (supported_platforms) |sup_plat| {
4323 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {4173 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);
4325 }4175 }
4326 }4176 }
4327 return false;4177 return false;
...@@ -4380,86 +4230,8 @@ const supported_platforms = [_]SupportedPlatforms{...@@ -4380,86 +4230,8 @@ const supported_platforms = [_]SupportedPlatforms{
4380};4230};
4381// zig fmt: on4231// zig fmt: on
43824232
4383pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {4233pub fn setDarwinSdkVersion(self: *MachO, version: link.DarwinSdkVersion) link.Error!void {
4384 const major = version.major;4234 self.sdk_version = version;
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;
4463}4235}
44644236
4465/// When allocating, the ideal_capacity is calculated by4237/// When allocating, the ideal_capacity is calculated by
...@@ -4497,7 +4269,7 @@ const SystemLib = struct {...@@ -4497,7 +4269,7 @@ const SystemLib = struct {
4497 .must_link = obj.must_link,4269 .must_link = obj.must_link,
4498 .hidden = obj.hidden,4270 .hidden = obj.hidden,
4499 },4271 },
4500 .dso => |dso| .{4272 inline .dso, .tbd => |dso| .{
4501 .path = dso.path,4273 .path = dso.path,
4502 .needed = dso.needed,4274 .needed = dso.needed,
4503 .weak = dso.weak,4275 .weak = dso.weak,
...@@ -4507,8 +4279,6 @@ const SystemLib = struct {...@@ -4507,8 +4279,6 @@ const SystemLib = struct {
4507 }4279 }
4508};4280};
45094281
4510pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
4511
4512const UndefinedTreatment = enum {4282const UndefinedTreatment = enum {
4513 @"error",4283 @"error",
4514 warn,4284 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...@@ -17,7 +17,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
17 const offset = if (fat_arch) |ar| ar.offset else 0;17 const offset = if (fat_arch) |ar| ar.offset else 0;
18 const end_pos = if (fat_arch) |ar| offset + ar.size else (try handle.stat(io)).size;18 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;
21 while (true) {21 while (true) {
22 if (pos >= end_pos) break;22 if (pos >= end_pos) break;
23 if (!mem.isAligned(pos, 2)) pos += 1;23 if (!mem.isAligned(pos, 2)) pos += 1;
...@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File...@@ -30,9 +30,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
30 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;30 const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*;
31 pos += @sizeOf(ar_hdr);31 pos += @sizeOf(ar_hdr);
3232
33 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {33 if (!mem.eql(u8, &hdr.ar_fmag, macho.ARFMAG)) {
34 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{34 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),
36 });36 });
37 }37 }
3838
...@@ -116,17 +116,6 @@ pub fn writeHeader(...@@ -116,17 +116,6 @@ pub fn writeHeader(
116 }116 }
117}117}
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
130pub const SYMDEF = "__.SYMDEF";119pub const SYMDEF = "__.SYMDEF";
131pub const SYMDEF64 = "__.SYMDEF_64";120pub const SYMDEF64 = "__.SYMDEF_64";
132pub const SYMDEF_SORTED = "__.SYMDEF SORTED";121pub const SYMDEF_SORTED = "__.SYMDEF SORTED";
...@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {...@@ -146,7 +135,7 @@ pub const ar_hdr = extern struct {
146 /// File size, in ASCII decimal.135 /// File size, in ASCII decimal.
147 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,136 ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*,
148 /// Always contains ARFMAG.137 /// Always contains ARFMAG.
149 ar_fmag: [2]u8 = ARFMAG.*,138 ar_fmag: [2]u8 = macho.ARFMAG.*,
150139
151 fn date(self: ar_hdr) !u64 {140 fn date(self: ar_hdr) !u64 {
152 const value = mem.trimEnd(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});141 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...@@ -611,60 +611,43 @@ pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.E
611 assert(!self.debug_strtab_dirty);611 assert(!self.debug_strtab_dirty);
612}612}
613613
614pub fn getNavVAddr(614pub fn navSymbol(
615 self: *ZigObject,615 zo: *ZigObject,
616 macho_file: *MachO,616 macho_file: *MachO,
617 pt: Zcu.PerThread,
618 nav_index: InternPool.Nav.Index,617 nav_index: InternPool.Nav.Index,
619 reloc_info: link.File.RelocInfo,618) link.Error!link.File.SymbolId {
620) !u64 {619 const zcu = macho_file.base.comp.zcu.?;
621 const zcu = pt.zcu;
622 const ip = &zcu.intern_pool;620 const ip = &zcu.intern_pool;
623 const nav = ip.getNav(nav_index);621 const nav = ip.getNav(nav_index);
624 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });622 if (nav.getExtern(ip)) |@"extern"| {
625 const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(623 const sym_index = try zo.getGlobalSymbol(
626 macho_file,624 macho_file,
627 nav.name.toSlice(ip),625 nav.name.toSlice(ip),
628 @"extern".lib_name.toSlice(ip),626 @"extern".lib_name.toSlice(ip),
629 ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);627 );
630 const sym = self.symbols.items[sym_index];628 if (@"extern".linkage == .weak) {
631 const vaddr = sym.getAddress(.{}, macho_file);629 zo.symbols.items[sym_index].flags.weak = true;
632 switch (reloc_info.parent) {630 }
633 .none => unreachable,631 if (nav.resolved.?.@"threadlocal") {
634 .atom_index => |atom_index| {632 zo.symbols.items[sym_index].flags.tlv = true;
635 const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?;633 }
636 try parent_atom.addReloc(macho_file, .{634 return @fromBackingInt(sym_index);
637 .tag = .@"extern",635 } else {
638 .offset = @intCast(reloc_info.offset),636 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
639 .target = sym_index,637 if (nav.resolved.?.@"threadlocal") {
640 .addend = reloc_info.addend,638 zo.symbols.items[sym_index].flags.tlv = true;
641 .type = .unsigned,639 }
642 .meta = .{640 return @fromBackingInt(sym_index);
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 }),
655 }641 }
656 return vaddr;
657}642}
658643
659pub fn getUavVAddr(644pub fn relocSymAddr(
660 self: *ZigObject,645 self: *ZigObject,
661 macho_file: *MachO,646 macho_file: *MachO,
662 uav: InternPool.Index,
663 reloc_info: link.File.RelocInfo,647 reloc_info: link.File.RelocInfo,
664) !u64 {648) !void {
665 const sym_index = self.uavs.get(uav).?.symbol_index;649 const sym_index = @backingInt(reloc_info.target);
666 const sym = self.symbols.items[sym_index];650 const sym = self.symbols.items[sym_index];
667 const vaddr = sym.getAddress(.{}, macho_file);
668 switch (reloc_info.parent) {651 switch (reloc_info.parent) {
669 .none => unreachable,652 .none => unreachable,
670 .atom_index => |atom_index| {653 .atom_index => |atom_index| {
...@@ -689,10 +672,9 @@ pub fn getUavVAddr(...@@ -689,10 +672,9 @@ pub fn getUavVAddr(
689 .target_off = reloc_info.addend,672 .target_off = reloc_info.addend,
690 }),673 }),
691 }674 }
692 return vaddr;
693}675}
694676
695pub fn lowerUav(677pub fn uavSymbol(
696 self: *ZigObject,678 self: *ZigObject,
697 macho_file: *MachO,679 macho_file: *MachO,
698 pt: Zcu.PerThread,680 pt: Zcu.PerThread,
...@@ -1281,7 +1263,7 @@ pub fn updateExports(...@@ -1281,7 +1263,7 @@ pub fn updateExports(
1281 break :blk self.navs.getPtr(nav).?;1263 break :blk self.navs.getPtr(nav).?;
1282 },1264 },
1283 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1265 .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);
1285 break :blk self.uavs.getPtr(uav).?;1267 break :blk self.uavs.getPtr(uav).?;
1286 },1268 },
1287 };1269 };
src/link/MachO/load_commands.zig+7-12
...@@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator;...@@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator;
9const DebugSymbols = @import("DebugSymbols.zig");9const DebugSymbols = @import("DebugSymbols.zig");
10const Dylib = @import("Dylib.zig");10const Dylib = @import("Dylib.zig");
11const MachO = @import("../MachO.zig");11const MachO = @import("../MachO.zig");
12const link = @import("../../link.zig");
1213
13pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";14pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1415
...@@ -296,7 +297,7 @@ pub fn writeRpathLC(rpath: []const u8, writer: *Writer) !void {...@@ -296,7 +297,7 @@ pub fn writeRpathLC(rpath: []const u8, writer: *Writer) !void {
296 }297 }
297}298}
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 {
300 const cmd: macho.LC = switch (platform.os_tag) {301 const cmd: macho.LC = switch (platform.os_tag) {
301 .macos => .VERSION_MIN_MACOSX,302 .macos => .VERSION_MIN_MACOSX,
302 .ios, .maccatalyst => .VERSION_MIN_IPHONEOS,303 .ios, .maccatalyst => .VERSION_MIN_IPHONEOS,
...@@ -306,24 +307,18 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer...@@ -306,24 +307,18 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer
306 };307 };
307 try writer.writeAll(mem.asBytes(&macho.version_min_command{308 try writer.writeAll(mem.asBytes(&macho.version_min_command{
308 .cmd = cmd,309 .cmd = cmd,
309 .version = platform.toAppleVersion(),310 .version = @backingInt(platform.version),
310 .sdk = if (sdk_version) |ver|311 .sdk = @backingInt(sdk_version orelse platform.version),
311 MachO.semanticVersionToAppleVersion(ver)
312 else
313 platform.toAppleVersion(),
314 }));312 }));
315}313}
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 {
318 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);316 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
319 try writer.writeStruct(@as(macho.build_version_command, .{317 try writer.writeStruct(@as(macho.build_version_command, .{
320 .cmdsize = cmdsize,318 .cmdsize = cmdsize,
321 .platform = platform.toApplePlatform(),319 .platform = platform.toApplePlatform(),
322 .minos = platform.toAppleVersion(),320 .minos = @backingInt(platform.version),
323 .sdk = if (sdk_version) |ver|321 .sdk = @backingInt(sdk_version orelse platform.version),
324 MachO.semanticVersionToAppleVersion(ver)
325 else
326 platform.toAppleVersion(),
327 .ntools = 1,322 .ntools = 1,
328 }), .little);323 }), .little);
329 try writer.writeAll(mem.asBytes(&macho.build_tool_version{324 try writer.writeAll(mem.asBytes(&macho.build_tool_version{
src/link/MachO/relocatable.zig+31-48
...@@ -1,25 +1,25 @@...@@ -1,25 +1,25 @@
1pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void {1pub fn flushObject(macho_file: *MachO, comp: *Compilation) link.Error!void {
2 const gpa = comp.gpa;
3 const io = comp.io;2 const io = comp.io;
4 const diags = &comp.link_diags;3 const diags = &comp.link_diags;
54
6 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.5 one_input: {
7 var positionals = std.array_list.Managed(link.Input).init(gpa);6 if (macho_file.getZigObject() != null) {
8 defer positionals.deinit();7 break :one_input;
9 try positionals.ensureUnusedCapacity(comp.link_inputs.len);8 }
10 positionals.appendSliceAssumeCapacity(comp.link_inputs);9 var only_input: ?link.Input = null;
1110 for (macho_file.all_inputs.items) |input| switch (input) {
12 for (comp.c_objects.items) |c_object| {11 .res => unreachable,
13 try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path));12 .dso, .tbd => {},
14 }13 .object, .archive => if (only_input == null) {
1514 only_input = input;
16 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path));15 } else {
1716 break :one_input;
18 if (macho_file.getZigObject() == null and positionals.items.len == 1) {17 },
18 };
19 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all19 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
20 // debug info segments/sections (this is apparently by design by Apple), we copy20 // debug info segments/sections (this is apparently by design by Apple), we copy
21 // the *only* input file over.21 // the *only* input file over.
22 const path = positionals.items[0].path();22 const path = (only_input orelse break :one_input).path();
23 const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err|23 const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err|
24 return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });24 return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });
25 const stat = in_file.stat(io) catch |err|25 const stat = in_file.stat(io) catch |err|
...@@ -29,10 +29,12 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -29,10 +29,12 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
29 return;29 return;
30 }30 }
3131
32 for (positionals.items) |link_input| {32 for (macho_file.all_inputs.items) |input| switch (input) {
33 macho_file.classifyInputFile(link_input) catch |err|33 .res => unreachable,
34 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});34 .dso, .tbd => {},
35 }35 .object, .archive => macho_file.classifyInputFile(input) catch |err|
36 diags.addParseError(input.path(), "failed to read input file: {t}", .{err}),
37 };
3638
37 if (diags.hasErrors()) return error.AlreadyReported;39 if (diags.hasErrors()) return error.AlreadyReported;
3840
...@@ -75,35 +77,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -75,35 +77,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
75 try writeHeader(macho_file, ncmds, sizeofcmds);77 try writeHeader(macho_file, ncmds, sizeofcmds);
76}78}
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 {
79 const gpa = comp.gpa;81 const gpa = comp.gpa;
80 const io = comp.io;
81 const diags = &macho_file.base.comp.link_diags;82 const diags = &macho_file.base.comp.link_diags;
8283
83 var positionals = std.array_list.Managed(link.Input).init(gpa);84 for (macho_file.all_inputs.items) |input| switch (input) {
84 defer positionals.deinit();85 .res => unreachable,
8586 .dso, .tbd => {},
86 try positionals.ensureUnusedCapacity(comp.link_inputs.len);87 .object, .archive => macho_file.classifyInputFile(input) catch |err|
87 positionals.appendSliceAssumeCapacity(comp.link_inputs);88 diags.addParseError(input.path(), "failed to read input file: {t}", .{err}),
8889 };
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 }
10790
108 if (diags.hasErrors()) return error.AlreadyReported;91 if (diags.hasErrors()) return error.AlreadyReported;
10992
...@@ -168,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -168,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
168151
169 // Update file offsets of contributing objects152 // Update file offsets of contributing objects
170 const total_size: usize = blk: {153 const total_size: usize = blk: {
171 var pos: usize = Archive.SARMAG;154 var pos: usize = macho.ARMAG.len;
172 pos += @sizeOf(Archive.ar_hdr);155 pos += @sizeOf(Archive.ar_hdr);
173 pos += Archive.SYMDEF.len + 1;156 pos += Archive.SYMDEF.len + 1;
174 pos = mem.alignForward(usize, pos, 8);157 pos = mem.alignForward(usize, pos, 8);
...@@ -211,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -211,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
211 var writer: Writer = .fixed(buffer);194 var writer: Writer = .fixed(buffer);
212195
213 // Write magic196 // Write magic
214 writer.writeAll(Archive.ARMAG) catch unreachable;197 writer.writeAll(macho.ARMAG) catch unreachable;
215198
216 // Write symtab199 // Write symtab
217 ar_symtab.write(format, macho_file, &writer) catch |err|200 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 {...@@ -495,12 +495,12 @@ pub const Node = extern struct {
495 }495 }
496496
497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
498 var parent_ni = ni;498 var cur_ni = ni;
499 while (parent_ni != .root) {499 while (cur_ni != .root) {
500 const parent_node = parent_ni.get(mf);500 if (cur_ni.get(mf).flags.moved) return true;
501 if (!parent_node.flags.bubbles_moved) break;501 const parent_ni = cur_ni.parent(mf).unwrap().?;
502 if (parent_node.flags.moved) return true;502 if (!parent_ni.get(mf).flags.bubbles_moved) break;
503 parent_ni = parent_node.parent.unwrap().?;503 cur_ni = parent_ni;
504 }504 }
505 return false;505 return false;
506 }506 }
src/link/Wasm.zig+2
...@@ -3783,6 +3783,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -3783,6 +3783,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
3783 switch (input) {3783 switch (input) {
3784 .res => unreachable,3784 .res => unreachable,
3785 .dso => unreachable,3785 .dso => unreachable,
3786 .tbd => unreachable,
3786 .object, .archive => |obj| {3787 .object, .archive => |obj| {
3787 try argv.append(gpa, try obj.path.toString(comp.arena));3788 try argv.append(gpa, try obj.path.toString(comp.arena));
3788 },3789 },
...@@ -3792,6 +3793,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -3792,6 +3793,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
3792 switch (input) {3793 switch (input) {
3793 .res => unreachable,3794 .res => unreachable,
3794 .dso => unreachable,3795 .dso => unreachable,
3796 .tbd => unreachable,
3795 .object => |obj| try parseObject(wasm, obj),3797 .object => |obj| try parseObject(wasm, obj),
3796 .archive => |obj| try parseArchive(wasm, obj),3798 .archive => |obj| try parseArchive(wasm, obj),
3797 }3799 }
src/main.zig+126-150
...@@ -893,13 +893,6 @@ const ArgsIterator = struct {...@@ -893,13 +893,6 @@ const ArgsIterator = struct {
893 }893 }
894};894};
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
903const CliModule = struct {896const CliModule = struct {
904 root_path: []const u8,897 root_path: []const u8,
905 root_src_path: []const u8,898 root_src_path: []const u8,
...@@ -1117,12 +1110,12 @@ fn buildOutputType(...@@ -1117,12 +1110,12 @@ fn buildOutputType(
11171110
1118 .llvm_m_args = .empty,1111 .llvm_m_args = .empty,
1119 .sysroot = null,1112 .sysroot = null,
1120 .lib_directories = .empty, // populated by createModule()
1121 .lib_dir_args = .empty, // populated from CLI arg parsing1113 .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()
1122 .libc_installation = null,1117 .libc_installation = null,
1123 .want_native_include_dirs = false,1118 .want_native_include_dirs = false,
1124 .frameworks = .empty,
1125 .framework_dirs = .empty,
1126 .rpath_list = .empty,1119 .rpath_list = .empty,
1127 .each_lib_rpath = null,1120 .each_lib_rpath = null,
1128 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),1121 .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),
...@@ -1275,13 +1268,25 @@ fn buildOutputType(...@@ -1275,13 +1268,25 @@ fn buildOutputType(
1275 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {1268 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1276 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());1269 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
1277 } else if (mem.eql(u8, arg, "-F")) {1270 } 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());
1279 } else if (mem.eql(u8, arg, "-framework")) {1272 } 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 } });
1281 } else if (mem.eql(u8, arg, "-weak_framework")) {1278 } 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 } });
1283 } else if (mem.eql(u8, arg, "-needed_framework")) {1284 } 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 } });
1285 } else if (mem.eql(u8, arg, "-install_name")) {1290 } else if (mem.eql(u8, arg, "-install_name")) {
1286 install_name = args_iter.nextOrFatal();1291 install_name = args_iter.nextOrFatal();
1287 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {1292 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
...@@ -1396,11 +1401,11 @@ fn buildOutputType(...@@ -1396,11 +1401,11 @@ fn buildOutputType(
1396 } else if (mem.eql(u8, arg, "-iframework")) {1401 } else if (mem.eql(u8, arg, "-iframework")) {
1397 const path = args_iter.nextOrFatal();1402 const path = args_iter.nextOrFatal();
1398 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);1403 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1399 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F1404 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
1400 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {1405 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
1401 const path = args_iter.nextOrFatal();1406 const path = args_iter.nextOrFatal();
1402 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);1407 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1403 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F1408 try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F
1404 } else if (mem.eql(u8, arg, "--version")) {1409 } else if (mem.eql(u8, arg, "--version")) {
1405 const next_arg = args_iter.nextOrFatal();1410 const next_arg = args_iter.nextOrFatal();
1406 version = std.SemanticVersion.parse(next_arg) catch |err| {1411 version = std.SemanticVersion.parse(next_arg) catch |err| {
...@@ -1828,7 +1833,7 @@ fn buildOutputType(...@@ -1828,7 +1833,7 @@ fn buildOutputType(
1828 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {1833 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
1829 try create_module.lib_dir_args.append(arena, rest);1834 try create_module.lib_dir_args.append(arena, rest);
1830 } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {1835 } 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);
1832 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {1837 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
1833 // We don't know whether this library is part of libc1838 // We don't know whether this library is part of libc
1834 // or libc++ until we resolve the target, so we append1839 // or libc++ until we resolve the target, so we append
...@@ -2532,8 +2537,12 @@ fn buildOutputType(...@@ -2532,8 +2537,12 @@ fn buildOutputType(
2532 disable_c_depfile = true;2537 disable_c_depfile = true;
2533 try cc_argv.appendSlice(arena, it.other_args);2538 try cc_argv.appendSlice(arena, it.other_args);
2534 },2539 },
2535 .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),2540 .framework_dir => try create_module.framework_dir_args.append(arena, it.only_arg),
2536 .framework => try create_module.frameworks.put(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 } }),
2537 .nostdlibinc => create_module.want_native_include_dirs = false,2546 .nostdlibinc => create_module.want_native_include_dirs = false,
2538 .strip => mod_opts.strip = true,2547 .strip => mod_opts.strip = true,
2539 .exec_model => {2548 .exec_model => {
...@@ -2561,7 +2570,11 @@ fn buildOutputType(...@@ -2561,7 +2570,11 @@ fn buildOutputType(
2561 },2570 },
2562 .name_done = false,2571 .name_done = false,
2563 } }),2572 } }),
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 } }),
2565 .headerpad_max_install_names => headerpad_max_install_names = true,2578 .headerpad_max_install_names => headerpad_max_install_names = true,
2566 .compress_debug_sections => {2579 .compress_debug_sections => {
2567 if (it.only_arg.len == 0) {2580 if (it.only_arg.len == 0) {
...@@ -2914,11 +2927,23 @@ fn buildOutputType(...@@ -2914,11 +2927,23 @@ fn buildOutputType(
2914 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });2927 fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err });
2915 };2928 };
2916 } else if (mem.eql(u8, arg, "-framework")) {2929 } 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 } });
2918 } else if (mem.eql(u8, arg, "-weak_framework")) {2935 } 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 } });
2920 } else if (mem.eql(u8, arg, "-needed_framework")) {2941 } 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 } });
2922 } else if (mem.eql(u8, arg, "-needed_library")) {2947 } else if (mem.eql(u8, arg, "-needed_library")) {
2923 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{2948 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2924 .name = linker_args_it.nextOrFatal(),2949 .name = linker_args_it.nextOrFatal(),
...@@ -3400,59 +3425,6 @@ fn buildOutputType(...@@ -3400,59 +3425,6 @@ fn buildOutputType(
3400 }3425 }
3401 }3426 }
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
3456 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {3428 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
3457 const total_obj_count = create_module.c_source_files.items.len +3429 const total_obj_count = create_module.c_source_files.items.len +
3458 @intFromBool(root_src_file != null) +3430 @intFromBool(root_src_file != null) +
...@@ -3689,6 +3661,7 @@ fn buildOutputType(...@@ -3689,6 +3661,7 @@ fn buildOutputType(
3689 .emit_docs = emit_docs_resolved,3661 .emit_docs = emit_docs_resolved,
3690 .emit_implib = emit_implib_resolved,3662 .emit_implib = emit_implib_resolved,
3691 .lib_directories = create_module.lib_directories.items,3663 .lib_directories = create_module.lib_directories.items,
3664 .framework_directories = create_module.framework_directories.items,
3692 .rpath_list = create_module.rpath_list.items,3665 .rpath_list = create_module.rpath_list.items,
3693 .symbol_wrap_set = symbol_wrap_set,3666 .symbol_wrap_set = symbol_wrap_set,
3694 .c_source_files = create_module.c_source_files.items,3667 .c_source_files = create_module.c_source_files.items,
...@@ -3697,8 +3670,6 @@ fn buildOutputType(...@@ -3697,8 +3670,6 @@ fn buildOutputType(
3697 .rc_includes = rc_includes,3670 .rc_includes = rc_includes,
3698 .mingw_unicode_entry_point = mingw_unicode_entry_point,3671 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3699 .link_inputs = create_module.link_inputs.items,3672 .link_inputs = create_module.link_inputs.items,
3700 .framework_dirs = create_module.framework_dirs.items,
3701 .frameworks = resolved_frameworks.items,
3702 .windows_lib_names = create_module.windows_libs.keys(),3673 .windows_lib_names = create_module.windows_libs.keys(),
3703 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,3674 .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt,
3704 .want_ubsan_rt = want_ubsan_rt,3675 .want_ubsan_rt = want_ubsan_rt,
...@@ -4041,13 +4012,13 @@ const CreateModule = struct {...@@ -4041,13 +4012,13 @@ const CreateModule = struct {
4041 /// CPU features.4012 /// CPU features.
4042 llvm_m_args: std.ArrayList([]const u8),4013 llvm_m_args: std.ArrayList([]const u8),
4043 sysroot: ?[]const u8,4014 sysroot: ?[]const u8,
4044 lib_directories: std.ArrayList(Directory),
4045 lib_dir_args: std.ArrayList([]const u8),4015 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),
4046 libc_installation: ?LibCInstallation,4019 libc_installation: ?LibCInstallation,
4047 want_native_include_dirs: bool,4020 want_native_include_dirs: bool,
4048 frameworks: std.array_hash_map.String(Framework),
4049 native_system_include_paths: []const []const u8,4021 native_system_include_paths: []const []const u8,
4050 framework_dirs: std.ArrayList([]const u8),
4051 rpath_list: std.ArrayList([]const u8),4022 rpath_list: std.ArrayList([]const u8),
4052 each_lib_rpath: ?bool,4023 each_lib_rpath: ?bool,
4053 libc_paths_file: ?[]const u8,4024 libc_paths_file: ?[]const u8,
...@@ -4158,7 +4129,7 @@ fn createModule(...@@ -4158,7 +4129,7 @@ fn createModule(
4158 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;4129 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
4159 defer unresolved_link_inputs.deinit(gpa);4130 defer unresolved_link_inputs.deinit(gpa);
4160 try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);4131 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;
4162 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {4133 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
4163 .name_query => |nq| {4134 .name_query => |nq| {
4164 const lib_name = nq.name;4135 const lib_name = nq.name;
...@@ -4199,39 +4170,52 @@ fn createModule(...@@ -4199,39 +4170,52 @@ fn createModule(
4199 }4170 }
42004171
4201 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);4172 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;
4203 },4178 },
4204 else => {4179 else => {
4205 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);4180 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4206 },4181 },
4207 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.4182 }; // 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
4211 // Resolve the library path arguments with respect to sysroot.4186 // Resolve the library path arguments with respect to sysroot.
4212 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);4187 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
4213 if (create_module.sysroot) |root| {4188 for (create_module.lib_dir_args.items) |dir_arg| {
4214 for (create_module.lib_dir_args.items) |lib_dir_arg| {4189 const path: []const u8 = path: {
4215 if (fs.path.isAbsolute(lib_dir_arg)) {4190 if (fs.path.isAbsolute(dir_arg)) {
4216 const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];4191 if (create_module.sysroot) |sysroot| {
4217 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });4192 // Change the path root to the given sysroot.
4218 addLibDirectoryWarn(io, &create_module.lib_directories, full_path);4193 const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..];
4219 } else {4194 break :path try fs.path.join(arena, &.{ sysroot, stripped_dir });
4220 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);4195 }
4221 }4196 }
4222 }4197 break :path dir_arg;
4223 } else {4198 };
4224 for (create_module.lib_dir_args.items) |lib_dir_arg| {4199 appendLibDirOrWarn(io, &create_module.lib_directories, path);
4225 addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg);
4226 }
4227 }4200 }
4228 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.4201 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()) {4203 // Likewise for framework path arguments.
4231 // If we want to link against frameworks, we need system headers.4204 try create_module.framework_directories.ensureUnusedCapacity(arena, create_module.framework_dir_args.items.len);
4232 if (create_module.frameworks.count() > 0)4205 for (create_module.framework_dir_args.items) |dir_arg| {
4233 create_module.want_native_include_dirs = true;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);
4234 }4217 }
4218 create_module.framework_dir_args = undefined; // From here we use framework_directories instead.
42354219
4236 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {4220 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
4237 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);4221 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
...@@ -4253,11 +4237,17 @@ fn createModule(...@@ -4253,11 +4237,17 @@ fn createModule(
42534237
4254 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);4238 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);
4257 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);4240 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
42584241
4259 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);4242 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 }
4261 }4251 }
42624252
4263 if (create_module.libc_paths_file) |paths_file| {4253 if (create_module.libc_paths_file) |paths_file| {
...@@ -4266,7 +4256,7 @@ fn createModule(...@@ -4266,7 +4256,7 @@ fn createModule(
4266 }4256 }
42674257
4268 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and4258 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
4269 any_name_queries_remaining)4259 any_named_library_queries)
4270 {4260 {
4271 if (create_module.libc_installation == null) {4261 if (create_module.libc_installation == null) {
4272 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{4262 create_module.libc_installation = LibCInstallation.findNative(arena, io, .{
...@@ -4277,9 +4267,19 @@ fn createModule(...@@ -4277,9 +4267,19 @@ fn createModule(
4277 fatal("unable to find native libc installation: {t}", .{err});4267 fatal("unable to find native libc installation: {t}", .{err});
4278 };4268 };
4279 }4269 }
4270
4280 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);4271 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
4281 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);4272
4282 addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);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 );
4283 }4283 }
42844284
4285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.4285 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
...@@ -4291,6 +4291,7 @@ fn createModule(...@@ -4291,6 +4291,7 @@ fn createModule(
4291 &unresolved_link_inputs,4291 &unresolved_link_inputs,
4292 &create_module.link_inputs,4292 &create_module.link_inputs,
4293 create_module.lib_directories.items,4293 create_module.lib_directories.items,
4294 create_module.framework_directories.items,
4294 color,4295 color,
4295 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});4296 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});
42964297
...@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {...@@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct {
6085 };6086 };
6086};6087};
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
6115fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {6089fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
6116 return stringToEnum(std.zig.RcIncludes, arg) orelse6090 return stringToEnum(std.zig.RcIncludes, arg) orelse
6117 fatal("unsupported rc includes type: {q}", .{arg});6091 fatal("unsupported rc includes type: {q}", .{arg});
...@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {...@@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
6222 return false;6196 return false;
6223}6197}
62246198
6225fn addLibDirectoryWarn(io: Io, lib_directories: *std.ArrayList(Directory), path: []const u8) void {6199fn appendLibDirOrWarn(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6226 return addLibDirectoryWarn2(io, lib_directories, path, false);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 }
6227}6208}
62286209fn appendLibDirOrWarnAllowMissing(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void {
6229fn addLibDirectoryWarn2(6210 if (Io.Dir.cwd().openDir(io, path, .{})) |handle| {
6230 io: Io,6211 dirs.appendAssumeCapacity(.{
6231 lib_directories: *std.ArrayList(Directory),6212 .path = path,
6232 path: []const u8,6213 .handle = handle,
6233 ignore_not_found: bool,6214 });
6234) void {6215 } else |err| switch (err) {
6235 lib_directories.appendAssumeCapacity(.{6216 error.FileNotFound => {}, // ignore
6236 .handle = Io.Dir.cwd().openDir(io, path, .{}) catch |err| {6217 else => |e| warn("unable to open library directory {q}: {t}", .{ path, e }),
6237 if (err == error.FileNotFound and ignore_not_found) return;6218 }
6238 warn("unable to open library directory {q}: {t}", .{ path, err });
6239 return;
6240 },
6241 .path = path,
6242 });
6243}6219}
62446220
6245const IoImpl = switch (build_options.io_mode) {6221const IoImpl = switch (build_options.io_mode) {
src/target.zig+2-2
...@@ -299,7 +299,7 @@ pub fn preferNewLinkerOverLld(target: *const std.Target) bool {...@@ -299,7 +299,7 @@ pub fn preferNewLinkerOverLld(target: *const std.Target) bool {
299/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.299/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.
300pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {300pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {
301 return switch (ofmt) {301 return switch (ofmt) {
302 .elf => true,302 .elf, .macho => true,
303 else => false,303 else => false,
304 };304 };
305}305}
...@@ -510,7 +510,7 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 {...@@ -510,7 +510,7 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 {
510 },510 },
511 // On SerenityOS libc includes libm, libpthread, libdl, and libssp.511 // On SerenityOS libc includes libm, libpthread, libdl, and libssp.
512 .serenity => &.{"-lc"},512 .serenity => &.{"-lc"},
513 else => &.{},513 else => if (target.os.tag.isDarwin()) &.{"-lSystem"} else &.{},
514 };514 };
515 return result;515 return result;
516}516}