| author | |
| committer | |
| log | bc616127e87a76ef3de02d5aee6c7d6ff6c9e34f |
| tree | 10718e3aa36da9e015f343449eda1c5390c1ee9b |
| parent | fd0baa9834293be085fd15929437f83986fb2243 |
| parent | 6418840f90d71a3c991bbb207ccdde4c9dc21d17 |
| signature | Commit is signed but in an unrecognized format. |
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 | 1145 | }; |
| 1146 | 1146 | |
| 1147 | 1147 | /// the mach magic number |
| 1148 | pub const MH_MAGIC = 0xfeedface; | |
| 1148 | pub const MH_MAGIC: u32 = 0xfeedface; | |
| 1149 | 1149 | |
| 1150 | 1150 | /// NXSwapInt(MH_MAGIC) |
| 1151 | pub const MH_CIGAM = 0xcefaedfe; | |
| 1151 | pub const MH_CIGAM: u32 = 0xcefaedfe; | |
| 1152 | 1152 | |
| 1153 | 1153 | /// the 64-bit mach magic number |
| 1154 | pub const MH_MAGIC_64 = 0xfeedfacf; | |
| 1154 | pub const MH_MAGIC_64: u32 = 0xfeedfacf; | |
| 1155 | 1155 | |
| 1156 | 1156 | /// NXSwapInt(MH_MAGIC_64) |
| 1157 | pub const MH_CIGAM_64 = 0xcffaedfe; | |
| 1157 | pub const MH_CIGAM_64: u32 = 0xcffaedfe; | |
| 1158 | 1158 | |
| 1159 | 1159 | /// relocatable object file |
| 1160 | 1160 | pub const MH_OBJECT = 0x1; |
| ... | ... | @@ -1281,16 +1281,16 @@ pub const MH_DYLIB_IN_CACHE = 0x80000000; |
| 1281 | 1281 | // Constants for the flags field of the fat_header |
| 1282 | 1282 | |
| 1283 | 1283 | /// the fat magic number |
| 1284 | pub const FAT_MAGIC = 0xcafebabe; | |
| 1284 | pub const FAT_MAGIC: u32 = 0xcafebabe; | |
| 1285 | 1285 | |
| 1286 | 1286 | /// NXSwapLong(FAT_MAGIC) |
| 1287 | pub const FAT_CIGAM = 0xbebafeca; | |
| 1287 | pub const FAT_CIGAM: u32 = 0xbebafeca; | |
| 1288 | 1288 | |
| 1289 | 1289 | /// the 64-bit fat magic number |
| 1290 | pub const FAT_MAGIC_64 = 0xcafebabf; | |
| 1290 | pub const FAT_MAGIC_64: u32 = 0xcafebabf; | |
| 1291 | 1291 | |
| 1292 | 1292 | /// NXSwapLong(FAT_MAGIC_64) |
| 1293 | pub const FAT_CIGAM_64 = 0xbfbafeca; | |
| 1293 | pub const FAT_CIGAM_64: u32 = 0xbfbafeca; | |
| 1294 | 1294 | |
| 1295 | 1295 | /// Segment flags |
| 1296 | 1296 | /// 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 | 2212 | has_lsda: u1, |
| 2213 | 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. | |
| 2220 | pub const ARMAG = "!<arch>\n"; | |
| 2221 | /// String in ar_fmag at the end of each header. | |
| 2222 | pub const ARFMAG = "`\n"; |
lib/std/zig/LibCDirs.zig+5-25| ... | ... | @@ -11,15 +11,6 @@ libc_include_dir_list: []const []const u8, |
| 11 | 11 | libc_installation: ?*const LibCInstallation, |
| 12 | 12 | libc_framework_dir_list: []const []const u8, |
| 13 | 13 | sysroot: ?[]const u8, |
| 14 | darwin_sdk_layout: ?DarwinSdkLayout, | |
| 15 | ||
| 16 | /// The filesystem layout of darwin SDK elements. | |
| 17 | pub 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 | }; | |
| 23 | 14 | |
| 24 | 15 | pub fn detect( |
| 25 | 16 | arena: Allocator, |
| ... | ... | @@ -37,7 +28,6 @@ pub fn detect( |
| 37 | 28 | .libc_installation = null, |
| 38 | 29 | .libc_framework_dir_list = &.{}, |
| 39 | 30 | .sysroot = null, |
| 40 | .darwin_sdk_layout = null, | |
| 41 | 31 | }; |
| 42 | 32 | } |
| 43 | 33 | |
| ... | ... | @@ -102,13 +92,11 @@ pub fn detect( |
| 102 | 92 | .libc_installation = null, |
| 103 | 93 | .libc_framework_dir_list = &.{}, |
| 104 | 94 | .sysroot = null, |
| 105 | .darwin_sdk_layout = null, | |
| 106 | 95 | }; |
| 107 | 96 | } |
| 108 | 97 | |
| 109 | 98 | fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *const LibCInstallation) !LibCDirs { |
| 110 | 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); | |
| 112 | 100 | |
| 113 | 101 | list.appendAssumeCapacity(lci.include_dir.?); |
| 114 | 102 | |
| ... | ... | @@ -149,21 +137,15 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con |
| 149 | 137 | } |
| 150 | 138 | } |
| 151 | 139 | |
| 152 | var sysroot: ?[]const u8 = null; | |
| 153 | ||
| 154 | if (target.os.tag.isDarwin()) d: { | |
| 155 | const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d; | |
| 156 | const down2 = std.fs.path.dirname(down1) orelse break :d; | |
| 157 | try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" })); | |
| 158 | sysroot = down2; | |
| 159 | } | |
| 140 | const frameworks: []const []const u8 = if (target.os.tag.isDarwin()) try arena.dupe([]const u8, &.{ | |
| 141 | try std.fs.path.join(arena, &.{ lci.darwin_sdk_dir.?, "System", "Library", "Frameworks" }), | |
| 142 | }) else &.{}; | |
| 160 | 143 | |
| 161 | 144 | return .{ |
| 162 | 145 | .libc_include_dir_list = list.items, |
| 163 | 146 | .libc_installation = lci, |
| 164 | .libc_framework_dir_list = framework_list.items, | |
| 165 | .sysroot = sysroot, | |
| 166 | .darwin_sdk_layout = if (sysroot == null) null else .sdk, | |
| 147 | .libc_framework_dir_list = frameworks, | |
| 148 | .sysroot = if (target.os.tag.isDarwin()) lci.darwin_sdk_dir.? else null, | |
| 167 | 149 | }; |
| 168 | 150 | } |
| 169 | 151 | |
| ... | ... | @@ -178,7 +160,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st |
| 178 | 160 | .libc_installation = null, |
| 179 | 161 | .libc_framework_dir_list = &.{}, |
| 180 | 162 | .sysroot = null, |
| 181 | .darwin_sdk_layout = .vendored, | |
| 182 | 163 | }; |
| 183 | 164 | } |
| 184 | 165 | |
| ... | ... | @@ -230,7 +211,6 @@ pub fn detectFromBuilding(arena: Allocator, zig_lib_dir: Path, target: *const st |
| 230 | 211 | .libc_installation = null, |
| 231 | 212 | .libc_framework_dir_list = &.{}, |
| 232 | 213 | .sysroot = null, |
| 233 | .darwin_sdk_layout = .vendored, | |
| 234 | 214 | }; |
| 235 | 215 | } |
| 236 | 216 |
lib/std/zig/LibCInstallation.zig+25-28| ... | ... | @@ -23,6 +23,7 @@ crt_dir: ?[]const u8 = null, |
| 23 | 23 | msvc_lib_dir: ?[]const u8 = null, |
| 24 | 24 | kernel32_lib_dir: ?[]const u8 = null, |
| 25 | 25 | cc_dir: ?[]const u8 = null, |
| 26 | darwin_sdk_dir: ?[]const u8 = null, | |
| 26 | 27 | |
| 27 | 28 | pub const FindError = error{ |
| 28 | 29 | OutOfMemory, |
| ... | ... | @@ -96,6 +97,10 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const |
| 96 | 97 | log.err("sys_include_dir may not be empty", .{}); |
| 97 | 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 | } | |
| 99 | 104 | |
| 100 | 105 | const os_tag = target.os.tag; |
| 101 | 106 | |
| ... | ... | @@ -103,12 +108,6 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const |
| 103 | 108 | log.err("cc_dir may not be empty for {s}", .{@tagName(os_tag)}); |
| 104 | 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 | 111 | if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) { |
| 113 | 112 | log.err("msvc_lib_dir may not be empty for {s}-{s}", .{ |
| 114 | 113 | @tagName(os_tag), |
| ... | ... | @@ -123,19 +122,16 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const |
| 123 | 122 | }); |
| 124 | 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 | } | |
| 126 | 129 | |
| 127 | 130 | return self; |
| 128 | 131 | } |
| 129 | 132 | |
| 130 | 133 | pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { |
| 131 | 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 | 135 | try out.print( |
| 140 | 136 | \\# The directory that contains `stdlib.h`. |
| 141 | 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 | 149 | \\ |
| 154 | 150 | \\# The directory that contains `crt1.o` or `crt2.o`. |
| 155 | 151 | \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. |
| 156 | \\# Not needed when targeting macOS. | |
| 157 | 152 | \\crt_dir={s} |
| 158 | 153 | \\ |
| 159 | 154 | \\# The directory that contains `vcruntime.lib`. |
| ... | ... | @@ -164,13 +159,18 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { |
| 164 | 159 | \\# Only needed when targeting MSVC on Windows. |
| 165 | 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, | |
| 169 | sys_include_dir, | |
| 170 | crt_dir, | |
| 171 | msvc_lib_dir, | |
| 172 | kernel32_lib_dir, | |
| 173 | cc_dir, | |
| 167 | self.include_dir orelse "", | |
| 168 | self.sys_include_dir orelse "", | |
| 169 | self.cc_dir orelse "", | |
| 170 | self.crt_dir orelse "", | |
| 171 | self.msvc_lib_dir orelse "", | |
| 172 | self.kernel32_lib_dir orelse "", | |
| 173 | self.darwin_sdk_dir orelse "", | |
| 174 | 174 | }); |
| 175 | 175 | } |
| 176 | 176 | |
| ... | ... | @@ -189,16 +189,13 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib |
| 189 | 189 | if (is_darwin and args.target.os.tag.isDarwin()) { |
| 190 | 190 | if (!std.zig.system.darwin.isSdkInstalled(gpa, io)) |
| 191 | 191 | return error.DarwinSdkNotFound; |
| 192 | const sdk = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse | |
| 192 | self.darwin_sdk_dir = std.zig.system.darwin.getSdk(gpa, io, args.target) orelse | |
| 193 | 193 | return error.DarwinSdkNotFound; |
| 194 | defer gpa.free(sdk); | |
| 195 | 194 | |
| 196 | self.include_dir = try fs.path.join(gpa, &.{ | |
| 197 | sdk, "usr/include", | |
| 198 | }); | |
| 199 | self.sys_include_dir = try fs.path.join(gpa, &.{ | |
| 200 | sdk, "usr/include", | |
| 201 | }); | |
| 195 | const sdk = self.darwin_sdk_dir.?; | |
| 196 | self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" }); | |
| 197 | self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" }); | |
| 198 | self.crt_dir = try fs.path.join(gpa, &.{ sdk, "usr/lib" }); | |
| 202 | 199 | return self; |
| 203 | 200 | } else if (is_windows) { |
| 204 | 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 | 83 | /// Resolved into known paths, any GNU ld scripts already resolved. |
| 84 | 84 | link_inputs: []const link.Input, |
| 85 | 85 | /// Needed only for passing -F args to clang. |
| 86 | framework_dirs: []const []const u8, | |
| 86 | framework_dirs: []const Cache.Directory, | |
| 87 | 87 | /// These are only for DLLs dependencies fulfilled by the `.def` files shipped |
| 88 | 88 | /// with Zig. Static libraries are provided as `link.Input` values. |
| 89 | 89 | windows_libs: std.array_hash_map.String(void), |
| ... | ... | @@ -1350,7 +1350,6 @@ pub const ClangPreprocessorMode = enum { |
| 1350 | 1350 | version, |
| 1351 | 1351 | }; |
| 1352 | 1352 | |
| 1353 | pub const Framework = link.File.MachO.Framework; | |
| 1354 | 1353 | pub const SystemLib = link.SystemLib; |
| 1355 | 1354 | |
| 1356 | 1355 | pub const CacheMode = enum { |
| ... | ... | @@ -1487,6 +1486,7 @@ pub const CreateOptions = struct { |
| 1487 | 1486 | /// The ELF implementation no longer uses this data, however the MachO and COFF |
| 1488 | 1487 | /// implementations still do. |
| 1489 | 1488 | lib_directories: []const Cache.Directory = &.{}, |
| 1489 | framework_directories: []const Cache.Directory = &.{}, | |
| 1490 | 1490 | rpath_list: []const []const u8 = &[0][]const u8{}, |
| 1491 | 1491 | symbol_wrap_set: std.array_hash_map.String(void) = .empty, |
| 1492 | 1492 | c_source_files: []const CSourceFile = &.{}, |
| ... | ... | @@ -1494,8 +1494,6 @@ pub const CreateOptions = struct { |
| 1494 | 1494 | manifest_file: ?[]const u8 = null, |
| 1495 | 1495 | rc_includes: std.zig.RcIncludes = .any, |
| 1496 | 1496 | link_inputs: []const link.Input = &.{}, |
| 1497 | framework_dirs: []const []const u8 = &[0][]const u8{}, | |
| 1498 | frameworks: []const Framework = &.{}, | |
| 1499 | 1497 | windows_lib_names: []const []const u8 = &.{}, |
| 1500 | 1498 | /// This means that if the output mode is an executable it will be a |
| 1501 | 1499 | /// 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 | 2144 | .ubsan_rt_strat = ubsan_rt_strat, |
| 2147 | 2145 | .zigc_strat = zigc_strat, |
| 2148 | 2146 | .link_inputs = options.link_inputs, |
| 2149 | .framework_dirs = options.framework_dirs, | |
| 2147 | .framework_dirs = options.framework_directories, | |
| 2150 | 2148 | .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit, |
| 2151 | 2149 | .skip_linker_dependencies = options.skip_linker_dependencies, |
| 2152 | 2150 | .queued_jobs = .{}, |
| ... | ... | @@ -2208,10 +2206,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, |
| 2208 | 2206 | .z_relro = options.linker_z_relro, |
| 2209 | 2207 | .z_common_page_size = options.linker_z_common_page_size, |
| 2210 | 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 | 2209 | .lib_directories = options.lib_directories, |
| 2214 | .framework_dirs = options.framework_dirs, | |
| 2215 | 2210 | .rpath_list = options.rpath_list, |
| 2216 | 2211 | .symbol_wrap_set = options.symbol_wrap_set, |
| 2217 | 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 | 2424 | // If linking against host libc installation, instead queue up jobs |
| 2430 | 2425 | // for loading those files in the linker. |
| 2431 | 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, | |
| 2433 | // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths(). | |
| 2434 | if (target.isDarwinLibC()) { | |
| 2435 | // TODO delete logic from MachO flush() and queue up tasks here instead. | |
| 2436 | } else if (comp.libc_installation) |lci| { | |
| 2427 | if (comp.libc_installation) |lci| { | |
| 2437 | 2428 | const basenames = LibCInstallation.CrtBasenames.get(.{ |
| 2438 | 2429 | .target = target, |
| 2439 | 2430 | .link_libc = comp.config.link_libc, |
| ... | ... | @@ -2456,6 +2447,18 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, |
| 2456 | 2447 | } |
| 2457 | 2448 | // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. |
| 2458 | 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 | 2462 | } else if (target.isMuslLibC()) { |
| 2460 | 2463 | if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); |
| 2461 | 2464 | |
| ... | ... | @@ -3409,7 +3412,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 3409 | 3412 | man.hash.add(comp.zigc_strat); |
| 3410 | 3413 | man.hash.add(comp.rc_includes); |
| 3411 | 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 | 3419 | man.hash.addListOfBytes(comp.windows_libs.keys()); |
| 3414 | 3420 | |
| 3415 | 3421 | man.hash.addListOfBytes(comp.global_cc_argv); |
| ... | ... | @@ -3475,7 +3481,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 3475 | 3481 | man.hash.add(opts.growable_table); |
| 3476 | 3482 | |
| 3477 | 3483 | // Mach-O specific stuff |
| 3478 | try link.File.MachO.hashAddFrameworks(man, opts.frameworks); | |
| 3479 | 3484 | try man.addInputPathOptional(opts.entitlements, .{}); |
| 3480 | 3485 | man.hash.addOptional(opts.pagezero_size); |
| 3481 | 3486 | man.hash.addOptional(opts.headerpad_size); |
| ... | ... | @@ -3485,7 +3490,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 3485 | 3490 | man.hash.add(opts.discard_local_symbols); |
| 3486 | 3491 | man.hash.addOptional(opts.compatibility_version); |
| 3487 | 3492 | man.hash.addOptionalBytes(opts.install_name); |
| 3488 | man.hash.addOptional(opts.darwin_sdk_layout); | |
| 3489 | 3493 | |
| 3490 | 3494 | // COFF specific stuff |
| 3491 | 3495 | man.hash.addOptional(opts.subsystem); |
| ... | ... | @@ -6455,7 +6459,7 @@ fn addCommonCCArgs( |
| 6455 | 6459 | |
| 6456 | 6460 | try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2); |
| 6457 | 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 | 754 | } |
| 755 | 755 | |
| 756 | 756 | const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu); |
| 757 | _ = try lf.lowerUav(pt, uav_val, uav_align); | |
| 758 | 757 | |
| 759 | const vaddr = lf.getUavVAddr(uav_val, .{ | |
| 758 | try lf.relocSymAddr(.{ | |
| 760 | 759 | .parent = reloc_parent, |
| 761 | 760 | .offset = w.end, |
| 761 | .target = try lf.uavSymbol(pt, uav_val, uav_align), | |
| 762 | 762 | .addend = @intCast(offset), |
| 763 | }) catch |err| switch (err) { | |
| 764 | error.OutOfMemory => |e| return e, | |
| 765 | else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}), | |
| 766 | }; | |
| 767 | const endian = target.cpu.arch.endian(); | |
| 768 | switch (ptr_width_bytes) { | |
| 769 | 2 => try w.writeInt(u16, @intCast(vaddr), endian), | |
| 770 | 4 => try w.writeInt(u32, @intCast(vaddr), endian), | |
| 771 | 8 => try w.writeInt(u64, vaddr, endian), | |
| 772 | else => unreachable, | |
| 773 | } | |
| 763 | }); | |
| 764 | ||
| 765 | try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation | |
| 774 | 766 | } |
| 775 | 767 | |
| 776 | 768 | fn lowerNavRef( |
| ... | ... | @@ -805,68 +797,14 @@ fn lowerNavRef( |
| 805 | 797 | else => {}, |
| 806 | 798 | } |
| 807 | 799 | |
| 808 | const vaddr = lf.getNavVAddr(pt, nav_index, .{ | |
| 800 | try lf.relocSymAddr(.{ | |
| 809 | 801 | .parent = reloc_parent, |
| 810 | 802 | .offset = w.end, |
| 803 | .target = try lf.navSymbol(nav_index), | |
| 811 | 804 | .addend = @intCast(offset), |
| 812 | }) catch @panic("TODO rework getNavVAddr"); | |
| 813 | const endian = target.cpu.arch.endian(); | |
| 814 | switch (ptr_width_bytes) { | |
| 815 | 2 => try w.writeInt(u16, @intCast(vaddr), endian), | |
| 816 | 4 => try w.writeInt(u32, @intCast(vaddr), endian), | |
| 817 | 8 => try w.writeInt(u64, vaddr, endian), | |
| 818 | else => unreachable, | |
| 819 | } | |
| 820 | } | |
| 805 | }); | |
| 821 | 806 | |
| 822 | pub fn genNavRef( | |
| 823 | lf: *link.File, | |
| 824 | pt: Zcu.PerThread, | |
| 825 | nav_index: InternPool.Nav.Index, | |
| 826 | ) Error!link.File.SymbolId { | |
| 827 | const zcu = pt.zcu; | |
| 828 | const ip = &zcu.intern_pool; | |
| 829 | const nav = ip.getNav(nav_index); | |
| 830 | log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)}); | |
| 831 | ||
| 832 | const is_threadlocal = nav.resolved.?.@"threadlocal" and zcu.comp.config.any_non_single_threaded; | |
| 833 | if (lf.cast(.elf)) |elf_file| { | |
| 834 | const zo = elf_file.zigObjectPtr().?; | |
| 835 | if (nav.getExtern(ip)) |e| { | |
| 836 | const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); | |
| 837 | switch (e.linkage) { | |
| 838 | .strong => {}, | |
| 839 | .weak => zo.symbol(sym_index).flags.weak = true, | |
| 840 | } | |
| 841 | if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; | |
| 842 | return @fromBackingInt(@intCast(sym_index)); | |
| 843 | } else { | |
| 844 | const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); | |
| 845 | if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; | |
| 846 | return @fromBackingInt(@intCast(sym_index)); | |
| 847 | } | |
| 848 | } else if (lf.cast(.elf2)) |elf| { | |
| 849 | return elf.navSymbol(nav_index); | |
| 850 | } else if (lf.cast(.macho)) |macho_file| { | |
| 851 | const zo = macho_file.getZigObject().?; | |
| 852 | if (nav.getExtern(ip)) |e| { | |
| 853 | const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); | |
| 854 | switch (e.linkage) { | |
| 855 | .strong => {}, | |
| 856 | .weak => zo.symbols.items[sym_index].flags.weak = true, | |
| 857 | } | |
| 858 | if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; | |
| 859 | return @fromBackingInt(@intCast(sym_index)); | |
| 860 | } else { | |
| 861 | const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); | |
| 862 | if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; | |
| 863 | return @fromBackingInt(@intCast(sym_index)); | |
| 864 | } | |
| 865 | } else if (lf.cast(.coff2)) |coff| { | |
| 866 | return @fromBackingInt(@intCast(@backingInt(try coff.navSymbol(zcu, nav_index)))); | |
| 867 | } else { | |
| 868 | std.debug.panic("TODO genNavRef for '{t}'", .{lf.tag}); | |
| 869 | } | |
| 807 | try w.splatByteAll(0, ptr_width_bytes); // overwritten by relocation | |
| 870 | 808 | } |
| 871 | 809 | |
| 872 | 810 | /// deprecated legacy type |
| ... | ... | @@ -908,13 +846,13 @@ pub fn genTypedValue( |
| 908 | 846 | .none => .none, |
| 909 | 847 | .undef => .undef, |
| 910 | 848 | .immediate => |imm| .{ .immediate = imm }, |
| 911 | .lea_nav => |nav| .{ .lea_symbol = try genNavRef(lf, pt, nav) }, | |
| 912 | .load_uav => |uav| .{ .load_symbol = try lf.lowerUav( | |
| 849 | .lea_nav => |nav| .{ .lea_symbol = try lf.navSymbol(nav) }, | |
| 850 | .load_uav => |uav| .{ .load_symbol = try lf.uavSymbol( | |
| 913 | 851 | pt, |
| 914 | 852 | uav.val, |
| 915 | 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 | 856 | pt, |
| 919 | 857 | uav.val, |
| 920 | 858 | Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), |
src/codegen/aarch64/Mir.zig+2-6| ... | ... | @@ -93,11 +93,7 @@ pub fn emit( |
| 93 | 93 | lf, |
| 94 | 94 | zcu, |
| 95 | 95 | atom_index, |
| 96 | try @import("../../codegen.zig").genNavRef( | |
| 97 | lf, | |
| 98 | pt, | |
| 99 | nav_reloc.nav, | |
| 100 | ), | |
| 96 | try lf.navSymbol(nav_reloc.nav), | |
| 101 | 97 | mir.body[nav_reloc.reloc.label], |
| 102 | 98 | body_end - Instruction.size * (1 + nav_reloc.reloc.label), |
| 103 | 99 | nav_reloc.reloc.addend, |
| ... | ... | @@ -107,7 +103,7 @@ pub fn emit( |
| 107 | 103 | lf, |
| 108 | 104 | zcu, |
| 109 | 105 | atom_index, |
| 110 | try lf.lowerUav( | |
| 106 | try lf.uavSymbol( | |
| 111 | 107 | pt, |
| 112 | 108 | uav_reloc.uav.val, |
| 113 | 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 | 83 | lf, |
| 84 | 84 | zcu, |
| 85 | 85 | atom_index, |
| 86 | try @import("../../codegen.zig").genNavRef( | |
| 87 | lf, | |
| 88 | pt, | |
| 89 | nav_reloc.nav, | |
| 90 | ), | |
| 86 | try lf.navSymbol(nav_reloc.nav), | |
| 91 | 87 | nav_reloc.reloc.type, |
| 92 | 88 | body_end - @sizeOf(Instruction) * (1 + nav_reloc.reloc.label), |
| 93 | 89 | nav_reloc.reloc.addend, |
| ... | ... | @@ -97,7 +93,7 @@ pub fn emit( |
| 97 | 93 | lf, |
| 98 | 94 | zcu, |
| 99 | 95 | atom_index, |
| 100 | try lf.lowerUav( | |
| 96 | try lf.uavSymbol( | |
| 101 | 97 | pt, |
| 102 | 98 | uav_reloc.uav.val, |
| 103 | 99 | ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu), |
| ... | ... | @@ -142,16 +138,11 @@ pub fn emit( |
| 142 | 138 | ) catch |err| |
| 143 | 139 | return zcu.codegenFail(func.owner_nav, "emit reloc failed: {t}", .{err}); |
| 144 | 140 | |
| 145 | const func_nav = try @import("../../codegen.zig").genNavRef( | |
| 146 | lf, | |
| 147 | pt, | |
| 148 | func.owner_nav, | |
| 149 | ); | |
| 150 | 141 | for (mir.internal_relocs) |internal_reloc| emitReloc( |
| 151 | 142 | lf, |
| 152 | 143 | zcu, |
| 153 | 144 | atom_index, |
| 154 | func_nav, | |
| 145 | try lf.navSymbol(func.owner_nav), | |
| 155 | 146 | internal_reloc.reloc.type, |
| 156 | 147 | body_end - @sizeOf(Instruction) * (1 + internal_reloc.reloc.label), |
| 157 | 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 | 8160 | const lf = func.bin_file; |
| 8161 | 8161 | |
| 8162 | 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 | 8164 | else |
| 8165 | 8165 | try codegen.genTypedValue(lf, pt, val, func.target); |
| 8166 | 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 | 100 | .inst => |inst| .{ .inst = inst }, |
| 101 | 101 | .table => .table, |
| 102 | 102 | .nav => |nav| { |
| 103 | const symbol_id = try codegen.genNavRef( | |
| 104 | emit.bin_file, | |
| 105 | emit.pt, | |
| 106 | nav, | |
| 107 | ); | |
| 103 | const symbol_id = try emit.bin_file.navSymbol(nav); | |
| 108 | 104 | const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{ |
| 109 | 105 | .symbol = symbol_id, |
| 110 | 106 | .is_extern = switch (@"extern".visibility) { |
| ... | ... | @@ -124,7 +120,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 124 | 120 | } |
| 125 | 121 | }, |
| 126 | 122 | .uav => |uav| .{ .symbol = .{ |
| 127 | .symbol = try emit.bin_file.lowerUav( | |
| 123 | .symbol = try emit.bin_file.uavSymbol( | |
| 128 | 124 | emit.pt, |
| 129 | 125 | uav.val, |
| 130 | 126 | Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), |
| ... | ... | @@ -142,7 +138,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 142 | 138 | else if (emit.bin_file.cast(.macho)) |macho_file| |
| 143 | 139 | @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err| |
| 144 | 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 | 142 | @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym)))) |
| 147 | 143 | else |
| 148 | 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 | 153 | .type = .FUNC, |
| 158 | 154 | }) else if (emit.bin_file.cast(.macho)) |macho_file| |
| 159 | 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 | 157 | .name = extern_func.toSlice(&emit.lower.mir).?, |
| 162 | 158 | })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}), |
| 163 | 159 | .is_extern = true, |
| ... | ... | @@ -171,7 +167,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 171 | 167 | switch (lowered_inst.encoding.mnemonic) { |
| 172 | 168 | .call => { |
| 173 | 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 | 171 | try emit.encodeInst(try .new(.none, .call, &.{ |
| 176 | 172 | .{ .mem = .initRip(.ptr, 0) }, |
| 177 | 173 | }, emit.lower.target), reloc_info); |
| ... | ... | @@ -252,7 +248,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 252 | 248 | }, emit.lower.target), reloc_info), |
| 253 | 249 | else => unreachable, |
| 254 | 250 | } |
| 255 | } else if (emit.bin_file.cast(.coff2)) |_| { | |
| 251 | } else if (emit.bin_file.cast(.coff)) |_| { | |
| 256 | 252 | if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) { |
| 257 | 253 | .lea => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 258 | 254 | lowered_inst.ops[0], |
| ... | ... | @@ -372,7 +368,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 372 | 368 | }, emit.lower.target), &.{}); |
| 373 | 369 | }, |
| 374 | 370 | else => unreachable, |
| 375 | } else if (emit.bin_file.cast(.coff2)) |coff| { | |
| 371 | } else if (emit.bin_file.cast(.coff)) |coff| { | |
| 376 | 372 | switch (emit.lower.target.cpu.arch) { |
| 377 | 373 | else => unreachable, |
| 378 | 374 | .x86 => { |
| ... | ... | @@ -597,12 +593,12 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 597 | 593 | .nav => |nav| @unionInit( |
| 598 | 594 | DwarfLoc, |
| 599 | 595 | addr_loc, |
| 600 | try codegen.genNavRef(emit.bin_file, emit.pt, nav), | |
| 596 | try emit.bin_file.navSymbol(nav), | |
| 601 | 597 | ), |
| 602 | 598 | .uav => |uav| @unionInit( |
| 603 | 599 | DwarfLoc, |
| 604 | 600 | addr_loc, |
| 605 | try emit.bin_file.lowerUav( | |
| 601 | try emit.bin_file.uavSymbol( | |
| 606 | 602 | emit.pt, |
| 607 | 603 | uav.val, |
| 608 | 604 | Type.fromInterned(uav.orig_ty) |
| ... | ... | @@ -845,7 +841,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI |
| 845 | 841 | if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL; |
| 846 | 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 | 845 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 850 | 846 | end_offset - 4, |
| 851 | 847 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
| ... | ... | @@ -883,7 +879,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI |
| 883 | 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 | 883 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 888 | 884 | end_offset - 4, |
| 889 | 885 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
| ... | ... | @@ -941,7 +937,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI |
| 941 | 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 | 941 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 946 | 942 | end_offset - 4, |
| 947 | 943 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
src/dev.zig+3-3| ... | ... | @@ -109,10 +109,10 @@ pub const Env = enum { |
| 109 | 109 | .spork8_backend, |
| 110 | 110 | .lld_linker, |
| 111 | 111 | .coff_linker, |
| 112 | .coff2_linker, | |
| 113 | 112 | .elf_linker, |
| 114 | 113 | .elf2_linker, |
| 115 | 114 | .macho_linker, |
| 115 | .macho2_linker, | |
| 116 | 116 | .c_linker, |
| 117 | 117 | .wasm_linker, |
| 118 | 118 | .spirv_linker, |
| ... | ... | @@ -231,7 +231,7 @@ pub const Env = enum { |
| 231 | 231 | .incremental, |
| 232 | 232 | .legalize, |
| 233 | 233 | .x86_64_backend, |
| 234 | .coff2_linker, | |
| 234 | .coff_linker, | |
| 235 | 235 | => true, |
| 236 | 236 | else => Env.sema.supports(feature), |
| 237 | 237 | }, |
| ... | ... | @@ -321,10 +321,10 @@ pub const Feature = enum { |
| 321 | 321 | |
| 322 | 322 | lld_linker, |
| 323 | 323 | coff_linker, |
| 324 | coff2_linker, | |
| 325 | 324 | elf_linker, |
| 326 | 325 | elf2_linker, |
| 327 | 326 | macho_linker, |
| 327 | macho2_linker, | |
| 328 | 328 | c_linker, |
| 329 | 329 | wasm_linker, |
| 330 | 330 | spirv_linker, |
src/link.zig+552-172| ... | ... | @@ -481,7 +481,6 @@ pub const File = struct { |
| 481 | 481 | |
| 482 | 482 | // TODO: remove this. libraries are resolved by the frontend. |
| 483 | 483 | lib_directories: []const Directory, |
| 484 | framework_dirs: []const []const u8, | |
| 485 | 484 | rpath_list: []const []const u8, |
| 486 | 485 | |
| 487 | 486 | /// Zig compiler development linker flags. |
| ... | ... | @@ -501,8 +500,6 @@ pub const File = struct { |
| 501 | 500 | headerpad_max_install_names: bool, |
| 502 | 501 | /// Remove dylibs that are unreachable by the entry point or exported symbols |
| 503 | 502 | dead_strip_dylibs: bool, |
| 504 | frameworks: []const MachO.Framework, | |
| 505 | darwin_sdk_layout: ?MachO.SdkLayout, | |
| 506 | 503 | /// Force load all members of static archives that implement an |
| 507 | 504 | /// Objective-C class or category |
| 508 | 505 | force_load_objc: bool, |
| ... | ... | @@ -587,7 +584,7 @@ pub const File = struct { |
| 587 | 584 | pub fn startProgress(base: *File, prog_node: std.Progress.Node) void { |
| 588 | 585 | switch (base.tag) { |
| 589 | 586 | else => {}, |
| 590 | inline .elf2, .coff2 => |tag| { | |
| 587 | inline .elf2, .coff, .macho2 => |tag| { | |
| 591 | 588 | dev.check(tag.devFeature()); |
| 592 | 589 | return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node); |
| 593 | 590 | }, |
| ... | ... | @@ -597,7 +594,7 @@ pub const File = struct { |
| 597 | 594 | pub fn endProgress(base: *File) void { |
| 598 | 595 | switch (base.tag) { |
| 599 | 596 | else => {}, |
| 600 | inline .elf2, .coff2 => |tag| { | |
| 597 | inline .elf2, .coff, .macho2 => |tag| { | |
| 601 | 598 | dev.check(tag.devFeature()); |
| 602 | 599 | return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress(); |
| 603 | 600 | }, |
| ... | ... | @@ -651,11 +648,13 @@ pub const File = struct { |
| 651 | 648 | } |
| 652 | 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 | 652 | const mf = if (base.cast(.elf2)) |elf| |
| 656 | 653 | &elf.mf |
| 657 | else if (base.cast(.coff2)) |coff| | |
| 654 | else if (base.cast(.coff)) |coff| | |
| 658 | 655 | &coff.mf |
| 656 | else if (base.cast(.macho2)) |macho| | |
| 657 | &macho.mf | |
| 659 | 658 | else |
| 660 | 659 | unreachable; |
| 661 | 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 | 739 | } |
| 741 | 740 | } |
| 742 | 741 | }, |
| 743 | .elf2, .coff2 => if (base.file) |f| { | |
| 742 | .elf2, .coff, .macho2 => if (base.file) |f| { | |
| 744 | 743 | const mf = if (base.cast(.elf2)) |elf| |
| 745 | 744 | &elf.mf |
| 746 | else if (base.cast(.coff2)) |coff| | |
| 745 | else if (base.cast(.coff)) |coff| | |
| 747 | 746 | &coff.mf |
| 747 | else if (base.cast(.macho2)) |macho| | |
| 748 | &macho.mf | |
| 748 | 749 | else |
| 749 | 750 | unreachable; |
| 750 | 751 | mf.unmap(); |
| ... | ... | @@ -841,7 +842,7 @@ pub const File = struct { |
| 841 | 842 | switch (base.tag) { |
| 842 | 843 | .lld => unreachable, |
| 843 | 844 | else => {}, |
| 844 | inline .elf, .elf2, .c, .coff2 => |tag| { | |
| 845 | inline .elf, .elf2, .c, .coff => |tag| { | |
| 845 | 846 | dev.check(tag.devFeature()); |
| 846 | 847 | return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success); |
| 847 | 848 | }, |
| ... | ... | @@ -899,7 +900,8 @@ pub const File = struct { |
| 899 | 900 | .lld => unreachable, |
| 900 | 901 | .plan9 => unreachable, |
| 901 | 902 | .spirv => {}, |
| 902 | .coff2 => {}, | |
| 903 | .coff => {}, | |
| 904 | .macho2 => {}, | |
| 903 | 905 | inline else => |tag| { |
| 904 | 906 | dev.check(tag.devFeature()); |
| 905 | 907 | return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line); |
| ... | ... | @@ -951,7 +953,7 @@ pub const File = struct { |
| 951 | 953 | pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool { |
| 952 | 954 | switch (base.tag) { |
| 953 | 955 | else => return false, |
| 954 | inline .elf2, .coff2 => |tag| { | |
| 956 | inline .elf2, .coff, .macho2 => |tag| { | |
| 955 | 957 | dev.check(tag.devFeature()); |
| 956 | 958 | return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid); |
| 957 | 959 | }, |
| ... | ... | @@ -959,9 +961,10 @@ pub const File = struct { |
| 959 | 961 | } |
| 960 | 962 | |
| 961 | 963 | pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void { |
| 964 | if (base.comp.zcu.?.llvm_object != null) return; | |
| 962 | 965 | switch (base.tag) { |
| 963 | 966 | else => {}, |
| 964 | inline .elf2, .coff2 => |tag| { | |
| 967 | inline .elf2, .coff, .macho2 => |tag| { | |
| 965 | 968 | dev.check(tag.devFeature()); |
| 966 | 969 | return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt); |
| 967 | 970 | }, |
| ... | ... | @@ -1039,6 +1042,7 @@ pub const File = struct { |
| 1039 | 1042 | pub const RelocInfo = struct { |
| 1040 | 1043 | parent: Parent, |
| 1041 | 1044 | offset: u64, |
| 1045 | target: SymbolId, | |
| 1042 | 1046 | addend: u32, |
| 1043 | 1047 | |
| 1044 | 1048 | pub const Parent = union(enum) { |
| ... | ... | @@ -1048,16 +1052,9 @@ pub const File = struct { |
| 1048 | 1052 | }; |
| 1049 | 1053 | }; |
| 1050 | 1054 | |
| 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 | 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 { | |
| 1059 | assert(pt.zcu.llvm_object == null); | |
| 1060 | ||
| 1056 | pub fn relocSymAddr(base: *File, reloc_info: RelocInfo) Error!void { | |
| 1057 | assert(base.comp.zcu.?.llvm_object == null); | |
| 1061 | 1058 | switch (base.tag) { |
| 1062 | 1059 | .lld => unreachable, |
| 1063 | 1060 | .c => unreachable, |
| ... | ... | @@ -1067,20 +1064,19 @@ pub const File = struct { |
| 1067 | 1064 | .spork8 => unreachable, |
| 1068 | 1065 | inline else => |tag| { |
| 1069 | 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 | } |
| 1074 | 1071 | |
| 1075 | 1072 | /// Never called when LLVM is codegenning the ZCU. |
| 1076 | pub fn lowerUav( | |
| 1073 | pub fn uavSymbol( | |
| 1077 | 1074 | base: *File, |
| 1078 | 1075 | pt: Zcu.PerThread, |
| 1079 | decl_val: InternPool.Index, | |
| 1080 | decl_align: InternPool.Alignment, | |
| 1076 | uav_val: InternPool.Index, | |
| 1077 | uav_align: InternPool.Alignment, | |
| 1081 | 1078 | ) Error!SymbolId { |
| 1082 | 1079 | assert(pt.zcu.llvm_object == null); |
| 1083 | ||
| 1084 | 1080 | switch (base.tag) { |
| 1085 | 1081 | .lld => unreachable, |
| 1086 | 1082 | .c => unreachable, |
| ... | ... | @@ -1090,15 +1086,14 @@ pub const File = struct { |
| 1090 | 1086 | .spork8 => unreachable, |
| 1091 | 1087 | inline else => |tag| { |
| 1092 | 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 | } |
| 1097 | 1093 | |
| 1098 | 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 | 1096 | assert(base.comp.zcu.?.llvm_object == null); |
| 1101 | ||
| 1102 | 1097 | switch (base.tag) { |
| 1103 | 1098 | .lld => unreachable, |
| 1104 | 1099 | .c => unreachable, |
| ... | ... | @@ -1108,7 +1103,7 @@ pub const File = struct { |
| 1108 | 1103 | .spork8 => unreachable, |
| 1109 | 1104 | inline else => |tag| { |
| 1110 | 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 | 1233 | } |
| 1239 | 1234 | |
| 1240 | 1235 | pub fn loadInput(base: *File, input: Input) anyerror!void { |
| 1241 | if (base.tag == .lld) return; | |
| 1242 | assert(!base.post_prelink); | |
| 1243 | ||
| 1236 | if (base.tag != .lld) { | |
| 1237 | assert(!base.post_prelink); | |
| 1238 | } | |
| 1244 | 1239 | switch (base.tag) { |
| 1245 | inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| { | |
| 1240 | inline else => |tag| { | |
| 1246 | 1241 | dev.check(tag.devFeature()); |
| 1247 | 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 | } |
| 1252 | 1300 | |
| ... | ... | @@ -1261,7 +1309,7 @@ pub const File = struct { |
| 1261 | 1309 | } |
| 1262 | 1310 | |
| 1263 | 1311 | switch (base.tag) { |
| 1264 | inline .elf2, .coff2, .wasm, .c => |tag| { | |
| 1312 | inline .elf2, .coff, .macho2, .wasm, .c => |tag| { | |
| 1265 | 1313 | dev.check(tag.devFeature()); |
| 1266 | 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 | 1350 | } |
| 1303 | 1351 | |
| 1304 | 1352 | pub const Tag = enum { |
| 1305 | coff2, | |
| 1353 | coff, | |
| 1306 | 1354 | elf, |
| 1307 | 1355 | elf2, |
| 1308 | 1356 | macho, |
| 1357 | macho2, | |
| 1309 | 1358 | c, |
| 1310 | 1359 | wasm, |
| 1311 | 1360 | spirv, |
| ... | ... | @@ -1315,10 +1364,11 @@ pub const File = struct { |
| 1315 | 1364 | |
| 1316 | 1365 | pub fn Type(comptime tag: Tag) type { |
| 1317 | 1366 | return switch (tag) { |
| 1318 | .coff2 => Coff2, | |
| 1367 | .coff => Coff, | |
| 1319 | 1368 | .elf => Elf, |
| 1320 | 1369 | .elf2 => Elf2, |
| 1321 | 1370 | .macho => MachO, |
| 1371 | .macho2 => MachO2, | |
| 1322 | 1372 | .c => C, |
| 1323 | 1373 | .wasm => Wasm, |
| 1324 | 1374 | .spirv => Spirv, |
| ... | ... | @@ -1330,9 +1380,9 @@ pub const File = struct { |
| 1330 | 1380 | |
| 1331 | 1381 | fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag { |
| 1332 | 1382 | return switch (ofmt) { |
| 1333 | .coff => .coff2, | |
| 1383 | .coff => .coff, | |
| 1334 | 1384 | .elf => if (use_new_linker) .elf2 else .elf, |
| 1335 | .macho => .macho, | |
| 1385 | .macho => if (use_new_linker) .macho2 else .macho, | |
| 1336 | 1386 | .wasm => .wasm, |
| 1337 | 1387 | .plan9 => .plan9, |
| 1338 | 1388 | .c => .c, |
| ... | ... | @@ -1420,11 +1470,12 @@ pub const File = struct { |
| 1420 | 1470 | |
| 1421 | 1471 | pub const Lld = @import("link/Lld.zig"); |
| 1422 | 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 | 1474 | pub const Spork8 = @import("link/Spork8.zig"); |
| 1425 | 1475 | pub const Elf = @import("link/Elf.zig"); |
| 1426 | 1476 | pub const Elf2 = @import("link/Elf2.zig"); |
| 1427 | 1477 | pub const MachO = @import("link/MachO.zig"); |
| 1478 | pub const MachO2 = @import("link/MachO2.zig"); | |
| 1428 | 1479 | pub const Spirv = @import("link/Spirv.zig"); |
| 1429 | 1480 | pub const Wasm = @import("link/Wasm.zig"); |
| 1430 | 1481 | pub const Dwarf = @import("link/Dwarf.zig"); |
| ... | ... | @@ -1449,6 +1500,8 @@ pub const PrelinkTask = union(enum) { |
| 1449 | 1500 | /// Tells the linker to load a shared library, possibly one that is a |
| 1450 | 1501 | /// GNU ld script. |
| 1451 | 1502 | load_dso: Path, |
| 1503 | load_tbd: Path, | |
| 1504 | load_darwin_sdk_settings: Path, | |
| 1452 | 1505 | }; |
| 1453 | 1506 | pub const ZcuTask = union(enum) { |
| 1454 | 1507 | /// 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 | 1556 | error.AlreadyReported => return, // error reported via diags |
| 1504 | 1557 | else => |e| switch (input) { |
| 1505 | 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 | 1560 | .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}), |
| 1507 | 1561 | .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}), |
| 1508 | 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 | 1578 | assert(mem.startsWith(u8, flag, "-l")); |
| 1525 | 1579 | const lib_name = flag["-l".len..]; |
| 1526 | 1580 | switch (comp.config.link_mode) { |
| 1527 | .dynamic => { | |
| 1528 | const dso_path = Path.initCwd( | |
| 1581 | .dynamic => loaded: { | |
| 1582 | if (target.os.tag.isDarwin()) { | |
| 1583 | // Prefer .tbd over .dylib. | |
| 1584 | const tbd_path: Path = .initCwd(std.fmt.allocPrint( | |
| 1585 | comp.arena, | |
| 1586 | "{s}" ++ sep ++ "{s}{s}.tbd", | |
| 1587 | .{ crt_dir, target.libPrefix(), lib_name }, | |
| 1588 | ) catch return diags.setAllocFailure()); | |
| 1589 | if (tbd_path.root_dir.handle.openFile(io, tbd_path.sub_path, .{})) |file| { | |
| 1590 | errdefer file.close(io); | |
| 1591 | base.loadInput(.{ .tbd = .{ | |
| 1592 | .path = tbd_path, | |
| 1593 | .file = file, | |
| 1594 | .needed = false, | |
| 1595 | .weak = false, | |
| 1596 | .reexport = false, | |
| 1597 | } }) catch |err| switch (err) { | |
| 1598 | error.AlreadyReported => return, | |
| 1599 | error.Canceled => io.recancel(), | |
| 1600 | else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}), | |
| 1601 | }; | |
| 1602 | break :loaded; | |
| 1603 | } else |err| switch (err) { | |
| 1604 | error.FileNotFound => {}, | |
| 1605 | error.Canceled => io.recancel(), | |
| 1606 | else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}), | |
| 1607 | } | |
| 1608 | } | |
| 1609 | ||
| 1610 | // Try dynamic library. | |
| 1611 | const dso_path: Path = .initCwd(std.fmt.allocPrint( | |
| 1612 | comp.arena, | |
| 1613 | "{s}" ++ sep ++ "{s}{s}{s}", | |
| 1614 | .{ crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix() }, | |
| 1615 | ) catch return diags.setAllocFailure()); | |
| 1616 | if (base.openLoadDso(dso_path, .{ | |
| 1617 | .preferred_mode = .dynamic, | |
| 1618 | .search_strategy = .paths_first, | |
| 1619 | })) { | |
| 1620 | break :loaded; | |
| 1621 | } else |err| switch (err) { | |
| 1622 | error.FileNotFound => {}, | |
| 1623 | error.AlreadyReported => return, | |
| 1624 | error.Canceled => io.recancel(), | |
| 1625 | else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), | |
| 1626 | } | |
| 1627 | ||
| 1628 | // Also try static. | |
| 1629 | const archive_path = Path.initCwd( | |
| 1529 | 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 | 1632 | }) catch return diags.setAllocFailure(), |
| 1532 | 1633 | ); |
| 1533 | base.openLoadDso(dso_path, .{ | |
| 1634 | if (base.openLoadArchiveQuery(archive_path, .{ | |
| 1534 | 1635 | .preferred_mode = .dynamic, |
| 1535 | 1636 | .search_strategy = .paths_first, |
| 1536 | }) catch |err| switch (err) { | |
| 1537 | error.FileNotFound => { | |
| 1538 | // Also try static. | |
| 1539 | const archive_path = Path.initCwd( | |
| 1540 | std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ | |
| 1541 | crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), | |
| 1542 | }) catch return diags.setAllocFailure(), | |
| 1543 | ); | |
| 1544 | base.openLoadArchiveQuery(archive_path, .{ | |
| 1545 | .preferred_mode = .dynamic, | |
| 1546 | .search_strategy = .paths_first, | |
| 1547 | }) catch |archive_err| switch (archive_err) { | |
| 1548 | error.AlreadyReported => return, // error reported via diags | |
| 1549 | else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }), | |
| 1550 | }; | |
| 1551 | }, | |
| 1552 | error.AlreadyReported => return, // error reported via diags | |
| 1553 | else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), | |
| 1554 | }; | |
| 1637 | })) { | |
| 1638 | break :loaded; | |
| 1639 | } else |archive_err| switch (archive_err) { | |
| 1640 | error.FileNotFound => {}, | |
| 1641 | error.AlreadyReported => return, | |
| 1642 | error.Canceled => io.recancel(), | |
| 1643 | else => |e| diags.addParseError(archive_path, "failed to parse archive: {s}", .{@errorName(e)}), | |
| 1644 | } | |
| 1645 | ||
| 1646 | diags.addError("failed to find library '{s}'", .{lib_name}); | |
| 1555 | 1647 | }, |
| 1556 | 1648 | .static => { |
| 1557 | 1649 | const path = Path.initCwd( |
| ... | ... | @@ -1571,6 +1663,19 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1571 | 1663 | } |
| 1572 | 1664 | } |
| 1573 | 1665 | |
| 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 | 1679 | if (target.os.tag == .windows and target.abi == .msvc) { |
| 1575 | 1680 | const inputs: []const struct { |
| 1576 | 1681 | dir: enum { crt, msvc_lib, kernel32_lib }, |
| ... | ... | @@ -1646,6 +1751,37 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1646 | 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 | } |
| 1651 | 1787 | pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void { |
| ... | ... | @@ -1820,6 +1956,7 @@ pub const UnresolvedInput = union(enum) { |
| 1820 | 1956 | /// Strings that come from GNU ld scripts. Is it a filename? Is it a path? |
| 1821 | 1957 | /// Who knows! Fuck around and find out. |
| 1822 | 1958 | ambiguous_name: AmbiguousNameQuery, |
| 1959 | framework_query: FrameworkQuery, | |
| 1823 | 1960 | |
| 1824 | 1961 | pub const NameQuery = struct { |
| 1825 | 1962 | name: []const u8, |
| ... | ... | @@ -1858,6 +1995,12 @@ pub const UnresolvedInput = union(enum) { |
| 1858 | 1995 | } |
| 1859 | 1996 | }; |
| 1860 | 1997 | |
| 1998 | pub const FrameworkQuery = struct { | |
| 1999 | name: []const u8, | |
| 2000 | needed: bool, | |
| 2001 | weak: bool, | |
| 2002 | }; | |
| 2003 | ||
| 1861 | 2004 | pub const SearchStrategy = enum { |
| 1862 | 2005 | paths_first, |
| 1863 | 2006 | mode_first, |
| ... | ... | @@ -1872,6 +2015,8 @@ pub const Input = union(enum) { |
| 1872 | 2015 | /// May not be a GNU ld script. Those are resolved when converting from |
| 1873 | 2016 | /// `UnresolvedInput` to `Input` values. |
| 1874 | 2017 | dso: Dso, |
| 2018 | /// Only possible when targeting Darwin. | |
| 2019 | tbd: Tbd, | |
| 1875 | 2020 | |
| 1876 | 2021 | pub const Object = struct { |
| 1877 | 2022 | path: Path, |
| ... | ... | @@ -1896,24 +2041,32 @@ pub const Input = union(enum) { |
| 1896 | 2041 | pub const FallbackSoname = enum { basename, full_path }; |
| 1897 | 2042 | }; |
| 1898 | 2043 | |
| 2044 | pub const Tbd = struct { | |
| 2045 | path: Path, | |
| 2046 | file: Io.File, | |
| 2047 | needed: bool, | |
| 2048 | weak: bool, | |
| 2049 | reexport: bool, | |
| 2050 | }; | |
| 2051 | ||
| 1899 | 2052 | pub fn path(input: Input) Path { |
| 1900 | 2053 | return switch (input) { |
| 1901 | 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 | } |
| 1905 | 2058 | |
| 1906 | 2059 | pub fn pathAndFile(input: Input) struct { Path, Io.File } { |
| 1907 | 2060 | return switch (input) { |
| 1908 | 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 | } |
| 1912 | 2065 | |
| 1913 | 2066 | pub fn taskName(input: Input) []const u8 { |
| 1914 | 2067 | return switch (input) { |
| 1915 | 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 | 2099 | man.hash.add(dso.reexport); |
| 1947 | 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 | 2123 | /// Allocated with `gpa`. |
| 1962 | 2124 | resolved_inputs: *std.ArrayList(Input), |
| 1963 | 2125 | lib_directories: []const Cache.Directory, |
| 2126 | framework_directories: []const Cache.Directory, | |
| 1964 | 2127 | color: std.zig.Color, |
| 1965 | 2128 | ) Allocator.Error!void { |
| 1966 | 2129 | var checked_paths: std.ArrayList(u8) = .empty; |
| ... | ... | @@ -1972,6 +2135,7 @@ pub fn resolveInputs( |
| 1972 | 2135 | var archive_dedup: ArchiveDedupMap = .empty; |
| 1973 | 2136 | defer archive_dedup.deinit(gpa); |
| 1974 | 2137 | |
| 2138 | // Allocated with `arena`. | |
| 1975 | 2139 | var failed_libs: std.ArrayList(struct { |
| 1976 | 2140 | name: []const u8, |
| 1977 | 2141 | strategy: UnresolvedInput.SearchStrategy, |
| ... | ... | @@ -1979,6 +2143,12 @@ pub fn resolveInputs( |
| 1979 | 2143 | preferred_mode: std.lang.LinkMode, |
| 1980 | 2144 | }) = .empty; |
| 1981 | 2145 | |
| 2146 | // Allocated with `arena`. | |
| 2147 | var failed_frameworks: std.ArrayList(struct { | |
| 2148 | name: []const u8, | |
| 2149 | checked_paths: []const u8, | |
| 2150 | }) = .empty; | |
| 2151 | ||
| 1982 | 2152 | // Convert external system libs into a stack so that items can be |
| 1983 | 2153 | // pushed to it. |
| 1984 | 2154 | // |
| ... | ... | @@ -2180,17 +2350,47 @@ pub fn resolveInputs( |
| 2180 | 2350 | } |
| 2181 | 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 | 2378 | comptime unreachable; |
| 2185 | 2379 | } |
| 2186 | 2380 | |
| 2187 | if (failed_libs.items.len > 0) { | |
| 2381 | if (failed_libs.items.len > 0 or failed_frameworks.items.len > 0) { | |
| 2188 | 2382 | for (failed_libs.items) |f| { |
| 2189 | 2383 | const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; |
| 2190 | 2384 | std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{ |
| 2191 | 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 | 2394 | std.process.exit(1); |
| 2195 | 2395 | } |
| 2196 | 2396 | } |
| ... | ... | @@ -2235,16 +2435,14 @@ fn resolveLibInput( |
| 2235 | 2435 | else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }), |
| 2236 | 2436 | }; |
| 2237 | 2437 | errdefer file.close(io); |
| 2238 | return finishResolveLibInput( | |
| 2239 | io, | |
| 2240 | resolved_inputs, | |
| 2241 | archive_dedup, | |
| 2242 | test_path, | |
| 2243 | file, | |
| 2244 | link_mode, | |
| 2245 | name_query.query, | |
| 2246 | .basename, | |
| 2247 | ); | |
| 2438 | resolved_inputs.appendAssumeCapacity(.{ .tbd = .{ | |
| 2439 | .path = test_path, | |
| 2440 | .file = file, | |
| 2441 | .needed = name_query.query.needed, | |
| 2442 | .weak = name_query.query.weak, | |
| 2443 | .reexport = name_query.query.reexport, | |
| 2444 | } }); | |
| 2445 | return .ok; | |
| 2248 | 2446 | } |
| 2249 | 2447 | |
| 2250 | 2448 | { |
| ... | ... | @@ -2282,16 +2480,15 @@ fn resolveLibInput( |
| 2282 | 2480 | else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }), |
| 2283 | 2481 | }; |
| 2284 | 2482 | errdefer file.close(io); |
| 2285 | return finishResolveLibInput( | |
| 2286 | io, | |
| 2287 | resolved_inputs, | |
| 2288 | archive_dedup, | |
| 2289 | test_path, | |
| 2290 | file, | |
| 2291 | link_mode, | |
| 2292 | name_query.query, | |
| 2293 | .basename, | |
| 2294 | ); | |
| 2483 | resolved_inputs.appendAssumeCapacity(.{ .dso = .{ | |
| 2484 | .path = test_path, | |
| 2485 | .file = file, | |
| 2486 | .needed = name_query.query.needed, | |
| 2487 | .weak = name_query.query.weak, | |
| 2488 | .reexport = name_query.query.reexport, | |
| 2489 | .fallback_soname = .basename, | |
| 2490 | } }); | |
| 2491 | return .ok; | |
| 2295 | 2492 | } |
| 2296 | 2493 | |
| 2297 | 2494 | // In the case of MinGW, the main check will be .lib but we also need to |
| ... | ... | @@ -2307,16 +2504,13 @@ fn resolveLibInput( |
| 2307 | 2504 | else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }), |
| 2308 | 2505 | }; |
| 2309 | 2506 | errdefer file.close(io); |
| 2310 | return finishResolveLibInput( | |
| 2311 | io, | |
| 2312 | resolved_inputs, | |
| 2313 | archive_dedup, | |
| 2314 | test_path, | |
| 2315 | file, | |
| 2316 | link_mode, | |
| 2317 | name_query.query, | |
| 2318 | .basename, | |
| 2319 | ); | |
| 2507 | addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{ | |
| 2508 | .path = test_path, | |
| 2509 | .file = file, | |
| 2510 | .must_link = name_query.query.must_link, | |
| 2511 | .hidden = name_query.query.hidden, | |
| 2512 | }); | |
| 2513 | return .ok; | |
| 2320 | 2514 | } |
| 2321 | 2515 | |
| 2322 | 2516 | // In the case of OpenBSD, dynamic libraries are always versioned, without |
| ... | ... | @@ -2378,6 +2572,145 @@ fn resolveLibInput( |
| 2378 | 2572 | return .no_match; |
| 2379 | 2573 | } |
| 2380 | 2574 | |
| 2575 | fn 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 | 2714 | /// Deduplicates static archive link inputs based on their path. This is done for efficiency, so |
| 2382 | 2715 | /// that linker implementations do not need to open and scan the archive just to determine that they |
| 2383 | 2716 | /// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions |
| ... | ... | @@ -2401,45 +2734,21 @@ const ArchiveDedupAdapter = struct { |
| 2401 | 2734 | } |
| 2402 | 2735 | }; |
| 2403 | 2736 | |
| 2404 | fn finishResolveLibInput( | |
| 2737 | fn addResolvedStaticLibInput( | |
| 2405 | 2738 | io: Io, |
| 2406 | 2739 | resolved_inputs: *std.ArrayList(Input), |
| 2407 | 2740 | archive_dedup: *ArchiveDedupMap, |
| 2408 | path: Path, | |
| 2409 | file: Io.File, | |
| 2410 | link_mode: std.lang.LinkMode, | |
| 2411 | query: UnresolvedInput.Query, | |
| 2412 | fallback_soname: Input.Dso.FallbackSoname, | |
| 2413 | ) ResolveLibInputResult { | |
| 2414 | switch (link_mode) { | |
| 2415 | .static => { | |
| 2416 | const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items }; | |
| 2417 | const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx); | |
| 2418 | if (gop.found_existing) { | |
| 2419 | // Ignore duplicate archive input | |
| 2420 | file.close(io); | |
| 2421 | return .ok; | |
| 2422 | } | |
| 2423 | gop.key_ptr.* = @intCast(resolved_inputs.items.len); | |
| 2424 | resolved_inputs.appendAssumeCapacity(.{ .archive = .{ | |
| 2425 | .path = path, | |
| 2426 | .file = file, | |
| 2427 | .must_link = query.must_link, | |
| 2428 | .hidden = query.hidden, | |
| 2429 | } }); | |
| 2430 | }, | |
| 2431 | .dynamic => resolved_inputs.appendAssumeCapacity(.{ | |
| 2432 | .dso = .{ | |
| 2433 | .path = path, | |
| 2434 | .file = file, | |
| 2435 | .needed = query.needed, | |
| 2436 | .weak = query.weak, | |
| 2437 | .reexport = query.reexport, | |
| 2438 | .fallback_soname = fallback_soname, | |
| 2439 | }, | |
| 2440 | }), | |
| 2741 | archive: Input.Object, | |
| 2742 | ) void { | |
| 2743 | const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items }; | |
| 2744 | const gop = archive_dedup.getOrPutAssumeCapacityAdapted(archive.path, ctx); | |
| 2745 | if (gop.found_existing) { | |
| 2746 | // Ignore duplicate archive input | |
| 2747 | archive.file.close(io); | |
| 2748 | } else { | |
| 2749 | gop.key_ptr.* = @intCast(resolved_inputs.items.len); | |
| 2750 | resolved_inputs.appendAssumeCapacity(.{ .archive = archive }); | |
| 2441 | 2751 | } |
| 2442 | return .ok; | |
| 2443 | 2752 | } |
| 2444 | 2753 | |
| 2445 | 2754 | fn resolvePathInput( |
| ... | ... | @@ -2535,19 +2844,24 @@ fn resolvePathInputLib( |
| 2535 | 2844 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| 2536 | 2845 | |
| 2537 | 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 | 2856 | // In the case of shared libraries, they might actually be "linker scripts" |
| 2539 | 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)) { | |
| 2541 | .static_library, .shared_library => true, | |
| 2542 | else => false, | |
| 2543 | }) { | |
| 2544 | var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { | |
| 2545 | error.FileNotFound => return .no_match, | |
| 2546 | else => |e| fatal("unable to search for {t} library {qf}: {t}", .{ | |
| 2547 | link_mode, std.fmt.alt(test_path, .formatEscapeChar), e, | |
| 2548 | }), | |
| 2549 | }; | |
| 2550 | errdefer file.close(io); | |
| 2858 | ld_script: { | |
| 2859 | if (!pq.query.allow_so_scripts) break :ld_script; | |
| 2860 | if (target.ofmt != .elf) break :ld_script; | |
| 2861 | switch (Compilation.classifyFileExt(test_path.sub_path)) { | |
| 2862 | .static_library, .shared_library => {}, | |
| 2863 | else => break :ld_script, | |
| 2864 | } | |
| 2551 | 2865 | try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); |
| 2552 | 2866 | const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err| |
| 2553 | 2867 | fatal("failed to read {qf}: {t}", .{ test_path, err }); |
| ... | ... | @@ -2557,16 +2871,7 @@ fn resolvePathInputLib( |
| 2557 | 2871 | mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) |
| 2558 | 2872 | { |
| 2559 | 2873 | // Appears to be an ELF or archive file. |
| 2560 | return finishResolveLibInput( | |
| 2561 | io, | |
| 2562 | resolved_inputs, | |
| 2563 | archive_dedup, | |
| 2564 | test_path, | |
| 2565 | file, | |
| 2566 | link_mode, | |
| 2567 | pq.query, | |
| 2568 | fallback_soname, | |
| 2569 | ); | |
| 2874 | break :ld_script; | |
| 2570 | 2875 | } |
| 2571 | 2876 | const stat = file.stat(io) catch |err| |
| 2572 | 2877 | fatal("failed to stat {f}: {t}", .{ test_path, err }); |
| ... | ... | @@ -2630,21 +2935,23 @@ fn resolvePathInputLib( |
| 2630 | 2935 | return .ok; |
| 2631 | 2936 | } |
| 2632 | 2937 | |
| 2633 | var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { | |
| 2634 | error.FileNotFound => return .no_match, | |
| 2635 | else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }), | |
| 2636 | }; | |
| 2637 | errdefer file.close(io); | |
| 2638 | return finishResolveLibInput( | |
| 2639 | io, | |
| 2640 | resolved_inputs, | |
| 2641 | archive_dedup, | |
| 2642 | test_path, | |
| 2643 | file, | |
| 2644 | link_mode, | |
| 2645 | pq.query, | |
| 2646 | fallback_soname, | |
| 2647 | ); | |
| 2938 | switch (link_mode) { | |
| 2939 | .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ | |
| 2940 | .path = test_path, | |
| 2941 | .file = file, | |
| 2942 | .needed = pq.query.needed, | |
| 2943 | .weak = pq.query.weak, | |
| 2944 | .reexport = pq.query.reexport, | |
| 2945 | .fallback_soname = fallback_soname, | |
| 2946 | } }), | |
| 2947 | .static => addResolvedStaticLibInput(io, resolved_inputs, archive_dedup, .{ | |
| 2948 | .path = test_path, | |
| 2949 | .file = file, | |
| 2950 | .must_link = pq.query.must_link, | |
| 2951 | .hidden = pq.query.hidden, | |
| 2952 | }), | |
| 2953 | } | |
| 2954 | return .ok; | |
| 2648 | 2955 | } |
| 2649 | 2956 | |
| 2650 | 2957 | pub 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 | 3006 | pub fn countObjectInputs(inputs: []const Input) usize { |
| 2700 | 3007 | var count: usize = 0; |
| 2701 | 3008 | for (inputs) |input| switch (input) { |
| 2702 | .dso => continue, | |
| 3009 | .dso, .tbd => continue, | |
| 2703 | 3010 | .res, .object, .archive => count += 1, |
| 2704 | 3011 | }; |
| 2705 | 3012 | return count; |
| ... | ... | @@ -2709,7 +3016,80 @@ pub fn countObjectInputs(inputs: []const Input) usize { |
| 2709 | 3016 | pub fn firstObjectInput(inputs: []const Input) ?Input.Object { |
| 2710 | 3017 | for (inputs) |input| switch (input) { |
| 2711 | 3018 | .object, .archive => |obj| return obj, |
| 2712 | .res, .dso => continue, | |
| 3019 | .res, .dso, .tbd => continue, | |
| 2713 | 3020 | }; |
| 2714 | 3021 | return null; |
| 2715 | 3022 | } |
| 3023 | ||
| 3024 | pub 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 | 1607 | errdefer file.close(io); |
| 1608 | 1608 | coff.* = .{ |
| 1609 | 1609 | .base = .{ |
| 1610 | .tag = .coff2, | |
| 1610 | .tag = .coff, | |
| 1611 | 1611 | |
| 1612 | 1612 | .comp = comp, |
| 1613 | 1613 | .emit = path, |
| ... | ... | @@ -2935,18 +2935,19 @@ fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.Na |
| 2935 | 2935 | if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); |
| 2936 | 2936 | return @fromBackingInt(@intCast(sym_gop.index)); |
| 2937 | 2937 | } |
| 2938 | pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index { | |
| 2938 | pub fn navSymbol(coff: *Coff, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId { | |
| 2939 | const zcu = coff.base.comp.zcu.?; | |
| 2939 | 2940 | const ip = &zcu.intern_pool; |
| 2940 | 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 | 2943 | .name = @"extern".name.toSlice(ip), |
| 2943 | 2944 | .lib_name = @"extern".lib_name.toSlice(ip), |
| 2944 | 2945 | // TODO: Threadlocal as well? |
| 2945 | 2946 | .type = if (ip.isFunctionType(nav.resolved.?.type)) .code else .data, |
| 2946 | 2947 | .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default, |
| 2947 | }); | |
| 2948 | })); | |
| 2948 | 2949 | const nmi = try coff.navMapIndex(zcu, nav_index); |
| 2949 | return nmi.symbol(coff); | |
| 2950 | return @bitCast(nmi.symbol(coff)); | |
| 2950 | 2951 | } |
| 2951 | 2952 | |
| 2952 | 2953 | 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 | 2957 | if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); |
| 2957 | 2958 | return @fromBackingInt(@intCast(sym_gop.index)); |
| 2958 | 2959 | } |
| 2959 | pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index { | |
| 2960 | const umi = try coff.uavMapIndex(uav_val); | |
| 2961 | return umi.symbol(coff); | |
| 2962 | } | |
| 2963 | 2960 | |
| 2964 | 2961 | pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index { |
| 2965 | 2962 | const gpa = coff.base.comp.gpa; |
| ... | ... | @@ -2972,24 +2969,8 @@ pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index { |
| 2972 | 2969 | return sym_gop.value_ptr.*; |
| 2973 | 2970 | } |
| 2974 | 2971 | |
| 2975 | pub fn getNavVAddr( | |
| 2976 | coff: *Coff, | |
| 2977 | pt: Zcu.PerThread, | |
| 2978 | nav: InternPool.Nav.Index, | |
| 2979 | reloc_info: link.File.RelocInfo, | |
| 2980 | ) link.Error!u64 { | |
| 2981 | return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav)); | |
| 2982 | } | |
| 2983 | ||
| 2984 | pub 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 | ||
| 2992 | pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) link.Error!u64 { | |
| 2972 | pub fn relocSymAddr(coff: *Coff, reloc_info: link.File.RelocInfo) link.Error!void { | |
| 2973 | const target_si: Symbol.Index = @bitCast(reloc_info.target); | |
| 2993 | 2974 | try coff.addReloc( |
| 2994 | 2975 | @fromBackingInt(@intCast(@backingInt(reloc_info.parent.atom_index))), |
| 2995 | 2976 | reloc_info.offset, |
| ... | ... | @@ -3001,10 +2982,6 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol. |
| 3001 | 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 | } |
| 3009 | 2986 | |
| 3010 | 2987 | /// 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 | 3912 | coff.loadDll(dso.path, &fr) catch |err| |
| 3936 | 3913 | return coff.failLoadInput(err, &fr, dso.path); |
| 3937 | 3914 | }, |
| 3915 | .tbd => unreachable, | |
| 3938 | 3916 | } |
| 3939 | 3917 | } |
| 3940 | 3918 | |
| ... | ... | @@ -5580,13 +5558,15 @@ pub fn updateContainerType( |
| 5580 | 5558 | }; |
| 5581 | 5559 | } |
| 5582 | 5560 | |
| 5583 | pub fn lowerUav( | |
| 5561 | pub fn uavSymbol( | |
| 5584 | 5562 | coff: *Coff, |
| 5585 | 5563 | pt: Zcu.PerThread, |
| 5586 | 5564 | uav_val: InternPool.Index, |
| 5587 | 5565 | uav_align: InternPool.Alignment, |
| 5588 | 5566 | ) link.Error!link.File.SymbolId { |
| 5589 | const zcu = pt.zcu; | |
| 5567 | _ = pt; | |
| 5568 | ||
| 5569 | const zcu = coff.base.comp.zcu.?; | |
| 5590 | 5570 | const gpa = zcu.gpa; |
| 5591 | 5571 | |
| 5592 | 5572 | try coff.pending_uavs.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -7471,8 +7451,8 @@ fn updateExportInner( |
| 7471 | 7451 | |
| 7472 | 7452 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| 7473 | 7453 | const exported_si: Symbol.Index = switch (exp.exported) { |
| 7474 | .nav => |nav| try coff.navSymbol(zcu, nav), | |
| 7475 | .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav( | |
| 7454 | .nav => |nav| @bitCast(try coff.navSymbol(nav)), | |
| 7455 | .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.uavSymbol( | |
| 7476 | 7456 | pt, |
| 7477 | 7457 | uav, |
| 7478 | 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 | 463 | self.dump_argv_list.deinit(gpa); |
| 464 | 464 | } |
| 465 | 465 | |
| 466 | pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { | |
| 467 | return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info); | |
| 466 | pub fn navSymbol(self: *Elf, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId { | |
| 467 | return self.zigObjectPtr().?.navSymbol(self, nav); | |
| 468 | 468 | } |
| 469 | 469 | |
| 470 | pub fn lowerUav( | |
| 470 | pub fn relocSymAddr(self: *Elf, reloc_info: link.File.RelocInfo) !void { | |
| 471 | return self.zigObjectPtr().?.relocSymAddr(self, reloc_info); | |
| 472 | } | |
| 473 | ||
| 474 | pub fn uavSymbol( | |
| 471 | 475 | self: *Elf, |
| 472 | 476 | pt: Zcu.PerThread, |
| 473 | 477 | uav: InternPool.Index, |
| 474 | 478 | explicit_alignment: InternPool.Alignment, |
| 475 | 479 | ) !link.File.SymbolId { |
| 476 | return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment); | |
| 477 | } | |
| 478 | ||
| 479 | pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { | |
| 480 | return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info); | |
| 480 | return self.zigObjectPtr().?.uavSymbol(self, pt, uav, explicit_alignment); | |
| 481 | 481 | } |
| 482 | 482 | |
| 483 | 483 | /// Returns end pos of collision, if any. |
| ... | ... | @@ -722,6 +722,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { |
| 722 | 722 | const argv = &self.dump_argv_list; |
| 723 | 723 | switch (input) { |
| 724 | 724 | .res => unreachable, |
| 725 | .tbd => unreachable, | |
| 725 | 726 | .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)), |
| 726 | 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 | 730 | |
| 730 | 731 | switch (input) { |
| 731 | 732 | .res => unreachable, |
| 733 | .tbd => unreachable, | |
| 732 | 734 | .object => |obj| try parseObject(self, obj), |
| 733 | 735 | .archive => |obj| if (self.base.isStaticLib()) { |
| 734 | 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 | 917 | return code; |
| 918 | 918 | } |
| 919 | 919 | |
| 920 | pub fn getNavVAddr( | |
| 921 | self: *ZigObject, | |
| 920 | pub fn navSymbol( | |
| 921 | zo: *ZigObject, | |
| 922 | 922 | elf_file: *Elf, |
| 923 | pt: Zcu.PerThread, | |
| 924 | 923 | nav_index: InternPool.Nav.Index, |
| 925 | reloc_info: link.File.RelocInfo, | |
| 926 | ) !u64 { | |
| 927 | const zcu = pt.zcu; | |
| 924 | ) link.Error!link.File.SymbolId { | |
| 925 | const zcu = elf_file.base.comp.zcu.?; | |
| 928 | 926 | const ip = &zcu.intern_pool; |
| 929 | 927 | const nav = ip.getNav(nav_index); |
| 930 | log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index }); | |
| 931 | const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol( | |
| 932 | elf_file, | |
| 933 | nav.name.toSlice(ip), | |
| 934 | @"extern".lib_name.toSlice(ip), | |
| 935 | ) else try self.getOrCreateMetadataForNav(zcu, nav_index); | |
| 936 | const this_sym = self.symbol(this_sym_index); | |
| 937 | const vaddr = this_sym.address(.{}, elf_file); | |
| 938 | switch (reloc_info.parent) { | |
| 939 | .none => unreachable, | |
| 940 | .atom_index => |atom_index| { | |
| 941 | const parent_atom = self.symbol(@backingInt(atom_index)).atom(elf_file).?; | |
| 942 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); | |
| 943 | try parent_atom.addReloc(elf_file.base.comp.gpa, .{ | |
| 944 | .r_offset = reloc_info.offset, | |
| 945 | .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type, | |
| 946 | .r_addend = reloc_info.addend, | |
| 947 | }, self); | |
| 948 | }, | |
| 949 | .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ | |
| 950 | .source_off = @intCast(reloc_info.offset), | |
| 951 | .target_sym = @fromBackingInt(@intCast(this_sym_index)), | |
| 952 | .target_off = reloc_info.addend, | |
| 953 | }), | |
| 928 | if (nav.getExtern(ip)) |@"extern"| { | |
| 929 | const sym_index = try zo.getGlobalSymbol( | |
| 930 | elf_file, | |
| 931 | nav.name.toSlice(ip), | |
| 932 | @"extern".lib_name.toSlice(ip), | |
| 933 | ); | |
| 934 | if (@"extern".linkage == .weak) { | |
| 935 | zo.symbol(sym_index).flags.weak = true; | |
| 936 | } | |
| 937 | if (nav.resolved.?.@"threadlocal") { | |
| 938 | zo.symbol(sym_index).flags.is_tls = true; | |
| 939 | } | |
| 940 | return @fromBackingInt(sym_index); | |
| 941 | } else { | |
| 942 | const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); | |
| 943 | if (nav.resolved.?.@"threadlocal") { | |
| 944 | zo.symbol(sym_index).flags.is_tls = true; | |
| 945 | } | |
| 946 | return @fromBackingInt(sym_index); | |
| 954 | 947 | } |
| 955 | return @intCast(vaddr); | |
| 956 | 948 | } |
| 957 | 949 | |
| 958 | pub fn getUavVAddr( | |
| 950 | pub fn relocSymAddr( | |
| 959 | 951 | self: *ZigObject, |
| 960 | 952 | elf_file: *Elf, |
| 961 | uav: InternPool.Index, | |
| 962 | 953 | reloc_info: link.File.RelocInfo, |
| 963 | ) !u64 { | |
| 964 | const sym_index = self.uavs.get(uav).?.symbol_index; | |
| 965 | const sym = self.symbol(sym_index); | |
| 966 | const vaddr = sym.address(.{}, elf_file); | |
| 954 | ) !void { | |
| 955 | const this_sym_index = @backingInt(reloc_info.target); | |
| 967 | 956 | switch (reloc_info.parent) { |
| 968 | 957 | .none => unreachable, |
| 969 | 958 | .atom_index => |atom_index| { |
| ... | ... | @@ -971,20 +960,19 @@ pub fn getUavVAddr( |
| 971 | 960 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); |
| 972 | 961 | try parent_atom.addReloc(elf_file.base.comp.gpa, .{ |
| 973 | 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 | 964 | .r_addend = reloc_info.addend, |
| 976 | 965 | }, self); |
| 977 | 966 | }, |
| 978 | 967 | .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ |
| 979 | 968 | .source_off = @intCast(reloc_info.offset), |
| 980 | .target_sym = @fromBackingInt(@intCast(sym_index)), | |
| 969 | .target_sym = @fromBackingInt(@intCast(this_sym_index)), | |
| 981 | 970 | .target_off = reloc_info.addend, |
| 982 | 971 | }), |
| 983 | 972 | } |
| 984 | return @intCast(vaddr); | |
| 985 | 973 | } |
| 986 | 974 | |
| 987 | pub fn lowerUav( | |
| 975 | pub fn uavSymbol( | |
| 988 | 976 | self: *ZigObject, |
| 989 | 977 | elf_file: *Elf, |
| 990 | 978 | pt: Zcu.PerThread, |
| ... | ... | @@ -1889,7 +1877,7 @@ pub fn updateExports( |
| 1889 | 1877 | break :blk self.navs.getPtr(nav).?; |
| 1890 | 1878 | }, |
| 1891 | 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 | 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 | 4324 | const s: Symbol.Id = .local(nmi.symbol(elf)); |
| 4325 | 4325 | return s.toTypeErased(); |
| 4326 | 4326 | } |
| 4327 | pub fn uavSymbol( | |
| 4328 | elf: *Elf, | |
| 4329 | uav_val: InternPool.Index, | |
| 4330 | uav_align: InternPool.Alignment, | |
| 4331 | ) link.Error!link.File.SymbolId { | |
| 4332 | const diags = &elf.base.comp.link_diags; | |
| 4333 | const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) { | |
| 4334 | else => |e| return e, | |
| 4335 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), | |
| 4336 | }; | |
| 4337 | const s: Symbol.Id = .local(umi.symbol(elf)); | |
| 4338 | return s.toTypeErased(); | |
| 4339 | } | |
| 4340 | pub 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 | } | |
| 4349 | pub 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 | } | |
| 4356 | pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 { | |
| 4327 | pub fn relocSymAddr(elf: *Elf, reloc_info: link.File.RelocInfo) link.Error!void { | |
| 4357 | 4328 | try elf.addReloc( |
| 4358 | 4329 | switch (reloc_info.parent) { |
| 4359 | 4330 | .none => unreachable, |
| ... | ... | @@ -4361,13 +4332,12 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy |
| 4361 | 4332 | .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni), |
| 4362 | 4333 | }, |
| 4363 | 4334 | reloc_info.offset, |
| 4364 | target, | |
| 4335 | reloc_info.target, | |
| 4365 | 4336 | reloc_info.addend, |
| 4366 | 4337 | .absAddr(elf), |
| 4367 | 4338 | ); |
| 4368 | return Symbol.Id.fromTypeErased(target).value(elf); | |
| 4369 | 4339 | } |
| 4370 | pub fn lowerUav( | |
| 4340 | pub fn uavSymbol( | |
| 4371 | 4341 | elf: *Elf, |
| 4372 | 4342 | pt: Zcu.PerThread, |
| 4373 | 4343 | uav_val: InternPool.Index, |
| ... | ... | @@ -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 | 7149 | .dso => |dso| { |
| 7181 | 7150 | try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1); |
| 7182 | 7151 | var fr = dso.file.reader(io, &buf); |
| ... | ... | @@ -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 | } |
| 7198 | 7169 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { |
| ... | ... | @@ -12384,7 +12355,7 @@ pub fn printNode( |
| 12384 | 12355 | mf_node.flags.alignment.toByteUnits(), |
| 12385 | 12356 | mf_node.flags.position, |
| 12386 | 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 | 12359 | if (mf_node.flags.resized) " resized" else "", |
| 12389 | 12360 | if (mf_node.flags.enable_next_moved) " enable_next_moved" else "", |
| 12390 | 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 | 309 | |
| 310 | 310 | try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len); |
| 311 | 311 | for (comp.link_inputs) |input| switch (input) { |
| 312 | .dso, .archive => {}, // static archives should not contain shared libraries or other static archives | |
| 312 | .dso, .tbd, .archive => {}, // static archives should not contain shared libraries or other static archives | |
| 313 | 313 | .res, .object => { |
| 314 | 314 | const path = try input.path().toStringZ(arena); |
| 315 | 315 | object_files.appendAssumeCapacity(path); |
| ... | ... | @@ -561,6 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 561 | 561 | argv.appendAssumeCapacity(try obj.path.toString(arena)); |
| 562 | 562 | } |
| 563 | 563 | }, |
| 564 | .tbd => unreachable, | |
| 564 | 565 | }; |
| 565 | 566 | |
| 566 | 567 | for (comp.c_objects.items) |c_object| { |
| ... | ... | @@ -1102,6 +1103,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 1102 | 1103 | |
| 1103 | 1104 | for (base.comp.link_inputs) |link_input| switch (link_input) { |
| 1104 | 1105 | .res => unreachable, // Windows-only |
| 1106 | .tbd => unreachable, // Darwin-only | |
| 1105 | 1107 | .dso => continue, |
| 1106 | 1108 | .object, .archive => |obj| { |
| 1107 | 1109 | if (obj.must_link and !whole_archive) { |
| ... | ... | @@ -1163,6 +1165,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 1163 | 1165 | |
| 1164 | 1166 | for (base.comp.link_inputs) |link_input| switch (link_input) { |
| 1165 | 1167 | .res => unreachable, // Windows-only |
| 1168 | .tbd => unreachable, // Darwin-only | |
| 1166 | 1169 | .object, .archive => continue, |
| 1167 | 1170 | .dso => |dso| { |
| 1168 | 1171 | const lib_as_needed = !dso.needed; |
| ... | ... | @@ -1596,6 +1599,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { |
| 1596 | 1599 | .dso => |dso| { |
| 1597 | 1600 | try argv.append(try dso.path.toString(arena)); |
| 1598 | 1601 | }, |
| 1602 | .tbd => unreachable, | |
| 1599 | 1603 | .res => unreachable, |
| 1600 | 1604 | }; |
| 1601 | 1605 | if (whole_archive) { |
src/link/MachO.zig+88-318| ... | ... | @@ -4,6 +4,8 @@ pub const Relocation = @import("MachO/Relocation.zig"); |
| 4 | 4 | |
| 5 | 5 | base: link.File, |
| 6 | 6 | |
| 7 | all_inputs: std.ArrayList(link.Input), | |
| 8 | ||
| 7 | 9 | rpath_list: []const []const u8, |
| 8 | 10 | |
| 9 | 11 | /// Debug symbols bundle (or dSym). |
| ... | ... | @@ -104,9 +106,7 @@ has_tlv: AtomicBool = AtomicBool.init(false), |
| 104 | 106 | binds_to_weak: AtomicBool = AtomicBool.init(false), |
| 105 | 107 | weak_defines: AtomicBool = AtomicBool.init(false), |
| 106 | 108 | |
| 107 | /// Options | |
| 108 | /// SDK layout | |
| 109 | sdk_layout: ?SdkLayout, | |
| 109 | // Options | |
| 110 | 110 | /// Size of the __PAGEZERO segment. |
| 111 | 111 | pagezero_size: ?u64, |
| 112 | 112 | /// Minimum space for future expansion of the load commands. |
| ... | ... | @@ -119,10 +119,6 @@ dead_strip_dylibs: bool, |
| 119 | 119 | undefined_treatment: UndefinedTreatment, |
| 120 | 120 | /// TODO: delete this, libraries need to be resolved by the frontend instead |
| 121 | 121 | lib_directories: []const Directory, |
| 122 | /// Resolved list of framework search directories | |
| 123 | framework_dirs: []const []const u8, | |
| 124 | /// List of input frameworks | |
| 125 | frameworks: []const Framework, | |
| 126 | 122 | /// Install name for the dylib. |
| 127 | 123 | /// TODO: unify with soname |
| 128 | 124 | install_name: ?[]const u8, |
| ... | ... | @@ -132,7 +128,7 @@ compatibility_version: ?std.SemanticVersion, |
| 132 | 128 | /// Entry name |
| 133 | 129 | entry_name: ?[]const u8, |
| 134 | 130 | platform: Platform, |
| 135 | sdk_version: ?std.SemanticVersion, | |
| 131 | sdk_version: ?link.DarwinSdkVersion, | |
| 136 | 132 | /// When set to true, the linker will hoist all dylibs including system dependent dylibs. |
| 137 | 133 | no_implicit_dylibs: bool = false, |
| 138 | 134 | /// Whether the linker should parse and always force load objects containing ObjC in archives. |
| ... | ... | @@ -190,13 +186,12 @@ pub fn createEmpty( |
| 190 | 186 | .file = null, |
| 191 | 187 | .build_id = options.build_id, |
| 192 | 188 | }, |
| 189 | .all_inputs = .empty, | |
| 193 | 190 | .rpath_list = options.rpath_list, |
| 194 | 191 | .pagezero_size = options.pagezero_size, |
| 195 | 192 | .headerpad_size = options.headerpad_size, |
| 196 | 193 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 197 | 194 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 198 | .sdk_layout = options.darwin_sdk_layout, | |
| 199 | .frameworks = options.frameworks, | |
| 200 | 195 | .install_name = options.install_name, |
| 201 | 196 | .entitlements = options.entitlements, |
| 202 | 197 | .compatibility_version = options.compatibility_version, |
| ... | ... | @@ -207,11 +202,10 @@ pub fn createEmpty( |
| 207 | 202 | .named => |name| name, |
| 208 | 203 | }, |
| 209 | 204 | .platform = Platform.fromTarget(target), |
| 210 | .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null, | |
| 205 | .sdk_version = null, | |
| 211 | 206 | .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error", |
| 212 | 207 | // TODO delete this, directories must instead be resolved by the frontend |
| 213 | 208 | .lib_directories = options.lib_directories, |
| 214 | .framework_dirs = options.framework_dirs, | |
| 215 | 209 | .force_load_objc = options.force_load_objc, |
| 216 | 210 | .discard_local_symbols = options.discard_local_symbols, |
| 217 | 211 | }; |
| ... | ... | @@ -271,6 +265,8 @@ pub fn deinit(self: *MachO) void { |
| 271 | 265 | const gpa = comp.gpa; |
| 272 | 266 | const io = comp.io; |
| 273 | 267 | |
| 268 | self.all_inputs.deinit(gpa); | |
| 269 | ||
| 274 | 270 | if (self.d_sym) |*d_sym| { |
| 275 | 271 | d_sym.deinit(); |
| 276 | 272 | } |
| ... | ... | @@ -334,6 +330,11 @@ pub fn deinit(self: *MachO) void { |
| 334 | 330 | self.thunks.deinit(gpa); |
| 335 | 331 | } |
| 336 | 332 | |
| 333 | pub 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 | ||
| 337 | 338 | pub fn flush( |
| 338 | 339 | self: *MachO, |
| 339 | 340 | arena: Allocator, |
| ... | ... | @@ -343,6 +344,8 @@ pub fn flush( |
| 343 | 344 | const tracy = trace(@src()); |
| 344 | 345 | defer tracy.end(); |
| 345 | 346 | |
| 347 | _ = arena; | |
| 348 | ||
| 346 | 349 | const comp = self.base.comp; |
| 347 | 350 | const gpa = comp.gpa; |
| 348 | 351 | const io = comp.io; |
| ... | ... | @@ -351,146 +354,40 @@ pub fn flush( |
| 351 | 354 | const sub_prog_node = prog_node.start("MachO Flush", 0); |
| 352 | 355 | defer sub_prog_node.end(); |
| 353 | 356 | |
| 354 | const zcu_obj_path: ?Path = p: { | |
| 355 | const zcu = comp.zcu orelse break :p null; | |
| 356 | const llvm_object = zcu.llvm_object orelse break :p null; | |
| 357 | break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); | |
| 358 | }; | |
| 357 | // TODO: due to https://codeberg.org/ziglang/zig/issues/36946, we receive link inputs in a | |
| 358 | // non-deterministic order. To ensure that this linker behaves deterministically, sort the | |
| 359 | // inputs into a consistent order now. | |
| 360 | { | |
| 361 | const InputSortContext = struct { | |
| 362 | fn lessThan(ctx: @This(), lhs: link.Input, rhs: link.Input) bool { | |
| 363 | _ = ctx; | |
| 364 | // We can't sort by full path because that could vary between runs where we still | |
| 365 | // want reproducibility (e.g. the path is affected by cwd). So instead, let's sort | |
| 366 | // by basename---that's good enough for our purposes since we never link two | |
| 367 | // vendored libraries/objects with the same name. | |
| 368 | const lhs_name = fs.path.basename(lhs.path().sub_path); | |
| 369 | const rhs_name = fs.path.basename(rhs.path().sub_path); | |
| 370 | return std.mem.order(u8, lhs_name, rhs_name).compare(.lt); | |
| 371 | } | |
| 372 | }; | |
| 373 | std.mem.sort( | |
| 374 | link.Input, | |
| 375 | self.all_inputs.items, | |
| 376 | @as(InputSortContext, .{}), | |
| 377 | InputSortContext.lessThan, | |
| 378 | ); | |
| 379 | } | |
| 359 | 380 | |
| 360 | 381 | // --verbose-link |
| 361 | 382 | if (comp.verbose_link) try self.dumpArgv(comp); |
| 362 | 383 | |
| 363 | 384 | if (self.getZigObject()) |zo| try zo.flush(self, tid); |
| 364 | if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path); | |
| 365 | if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path); | |
| 385 | if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp); | |
| 386 | if (self.base.isObject()) return relocatable.flushObject(self, comp); | |
| 366 | 387 | |
| 367 | var positionals = std.array_list.Managed(link.Input).init(gpa); | |
| 368 | defer positionals.deinit(); | |
| 369 | ||
| 370 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 371 | ||
| 372 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 373 | .dso => continue, // handled below | |
| 374 | .object, .archive => positionals.appendAssumeCapacity(link_input), | |
| 375 | .res => unreachable, | |
| 376 | }; | |
| 377 | ||
| 378 | // This is a set of object files emitted by clang in a single `build-exe` invocation. | |
| 379 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up | |
| 380 | // in this set. | |
| 381 | try positionals.ensureUnusedCapacity(comp.c_objects.items.len); | |
| 382 | for (comp.c_objects.items) |c_object| { | |
| 383 | positionals.appendAssumeCapacity(try link.openObjectInput(io, diags, c_object.status.success.object_path)); | |
| 384 | } | |
| 385 | ||
| 386 | if (zcu_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); | |
| 387 | ||
| 388 | if (comp.config.any_sanitize_thread) { | |
| 389 | try positionals.append(try link.openObjectInput(io, diags, comp.tsan_lib.?.full_object_path)); | |
| 390 | } | |
| 391 | ||
| 392 | if (comp.config.any_fuzz) { | |
| 393 | try positionals.append(try link.openArchiveInput(io, diags, comp.fuzzer_lib.?.full_object_path, false, false)); | |
| 394 | } | |
| 395 | ||
| 396 | if (comp.ubsan_rt_lib) |crt_file| { | |
| 397 | const path = crt_file.full_object_path; | |
| 398 | self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| | |
| 399 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 400 | } else if (comp.ubsan_rt_obj) |crt_file| { | |
| 401 | const path = crt_file.full_object_path; | |
| 402 | self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err| | |
| 403 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 404 | } | |
| 405 | ||
| 406 | for (positionals.items) |link_input| { | |
| 407 | self.classifyInputFile(link_input) catch |err| | |
| 408 | diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); | |
| 409 | } | |
| 410 | ||
| 411 | var system_libs = std.array_list.Managed(SystemLib).init(gpa); | |
| 412 | defer system_libs.deinit(); | |
| 413 | ||
| 414 | // frameworks | |
| 415 | try system_libs.ensureUnusedCapacity(self.frameworks.len); | |
| 416 | for (self.frameworks) |info| { | |
| 417 | system_libs.appendAssumeCapacity(.{ | |
| 418 | .needed = info.needed, | |
| 419 | .weak = info.weak, | |
| 420 | .path = info.path, | |
| 421 | }); | |
| 422 | } | |
| 423 | ||
| 424 | // libc++ dep | |
| 425 | if (comp.config.link_libcpp) { | |
| 426 | try system_libs.ensureUnusedCapacity(2); | |
| 427 | system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); | |
| 428 | system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); | |
| 429 | } | |
| 430 | ||
| 431 | const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or | |
| 432 | (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); | |
| 433 | ||
| 434 | if (comp.config.link_libc and is_exe_or_dyn_lib) { | |
| 435 | if (comp.zigc_static_lib) |zigc| { | |
| 436 | const path = zigc.full_object_path; | |
| 437 | self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| | |
| 438 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 439 | } | |
| 440 | } | |
| 441 | ||
| 442 | // libc/libSystem dep | |
| 443 | self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { | |
| 444 | error.MissingLibSystem => {}, // already reported | |
| 445 | else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}), | |
| 446 | }; | |
| 447 | ||
| 448 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 449 | .object, .archive => continue, | |
| 450 | .res => unreachable, | |
| 451 | .dso => { | |
| 452 | self.classifyInputFile(link_input) catch |err| | |
| 453 | diags.addParseError(link_input.path(), "failed to parse input file: {t}", .{err}); | |
| 454 | }, | |
| 455 | }; | |
| 456 | ||
| 457 | for (system_libs.items) |lib| { | |
| 458 | switch (Compilation.classifyFileExt(lib.path.sub_path)) { | |
| 459 | .shared_library => { | |
| 460 | const dso_input = try link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport); | |
| 461 | self.classifyInputFile(dso_input) catch |err| | |
| 462 | diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 463 | }, | |
| 464 | .static_library => { | |
| 465 | const archive_input = try link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden); | |
| 466 | self.classifyInputFile(archive_input) catch |err| | |
| 467 | diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 468 | }, | |
| 469 | else => { | |
| 470 | dso: { | |
| 471 | const dso_input = link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport) catch break :dso; | |
| 472 | self.classifyInputFile(dso_input) catch break :dso; | |
| 473 | continue; | |
| 474 | } | |
| 475 | ar: { | |
| 476 | const archive_input = link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden) catch break :ar; | |
| 477 | self.classifyInputFile(archive_input) catch break :ar; | |
| 478 | continue; | |
| 479 | } | |
| 480 | diags.addParseError(lib.path, "unknown file extension", .{}); | |
| 481 | }, | |
| 482 | } | |
| 483 | } | |
| 484 | ||
| 485 | // Finally, link against compiler_rt. | |
| 486 | if (comp.compiler_rt_lib) |crt_file| { | |
| 487 | const path = crt_file.full_object_path; | |
| 488 | self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| | |
| 489 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 490 | } else if (comp.compiler_rt_obj) |crt_file| { | |
| 491 | const path = crt_file.full_object_path; | |
| 492 | self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err| | |
| 493 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 388 | for (self.all_inputs.items) |input| { | |
| 389 | self.classifyInputFile(input) catch |err| | |
| 390 | diags.addParseError(input.path(), "failed to read input file: {t}", .{err}); | |
| 494 | 391 | } |
| 495 | 392 | |
| 496 | 393 | try self.parseInputFiles(); |
| ... | ... | @@ -666,9 +563,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 666 | 563 | for (comp.link_inputs) |link_input| switch (link_input) { |
| 667 | 564 | .object, .archive => |obj| try argv.append(try obj.path.toString(arena)), |
| 668 | 565 | .res => |res| try argv.append(try res.path.toString(arena)), |
| 669 | .dso => |dso| { | |
| 670 | try argv.append(try dso.path.toString(arena)); | |
| 671 | }, | |
| 566 | .dso => |dso| try argv.append(try dso.path.toString(arena)), | |
| 567 | .tbd => |tbd| try argv.append(try tbd.path.toString(arena)), | |
| 672 | 568 | }; |
| 673 | 569 | |
| 674 | 570 | for (comp.c_objects.items) |c_object| { |
| ... | ... | @@ -694,12 +590,20 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 694 | 590 | |
| 695 | 591 | try argv.append("-platform_version"); |
| 696 | 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 | })); | |
| 698 | 598 | |
| 699 | 599 | if (self.sdk_version) |ver| { |
| 700 | 600 | try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); |
| 701 | 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 | } |
| 704 | 608 | |
| 705 | 609 | if (comp.sysroot) |syslibroot| { |
| ... | ... | @@ -754,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 754 | 658 | } |
| 755 | 659 | |
| 756 | 660 | for (comp.link_inputs) |link_input| switch (link_input) { |
| 757 | .dso => continue, // handled below | |
| 661 | .dso, .tbd => continue, // handled below | |
| 758 | 662 | .res => unreachable, // windows only |
| 759 | 663 | .object, .archive => |obj| { |
| 760 | 664 | if (obj.must_link) try argv.append("-force_load"); // TODO: verify this |
| ... | ... | @@ -788,7 +692,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 788 | 692 | for (comp.link_inputs) |link_input| switch (link_input) { |
| 789 | 693 | .object, .archive => continue, // handled above |
| 790 | 694 | .res => unreachable, // windows only |
| 791 | .dso => |dso| { | |
| 695 | inline .dso, .tbd => |dso| { | |
| 792 | 696 | try argv.ensureUnusedCapacity(2); |
| 793 | 697 | if (dso.needed) { |
| 794 | 698 | argv.appendAssumeCapacity("-needed-l"); |
| ... | ... | @@ -801,20 +705,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 801 | 705 | }, |
| 802 | 706 | }; |
| 803 | 707 | |
| 804 | for (self.framework_dirs) |f_dir| { | |
| 708 | for (comp.framework_dirs) |f_dir| { | |
| 805 | 709 | try argv.append("-F"); |
| 806 | try argv.append(f_dir); | |
| 807 | } | |
| 808 | ||
| 809 | for (self.frameworks) |framework| { | |
| 810 | const name = framework.path.stem(); | |
| 811 | const arg = if (framework.needed) | |
| 812 | try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) | |
| 813 | else if (framework.weak) | |
| 814 | try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name}) | |
| 815 | else | |
| 816 | try std.fmt.allocPrint(arena, "-framework {s}", .{name}); | |
| 817 | try argv.append(arg); | |
| 710 | try argv.append(f_dir.path orelse "."); | |
| 818 | 711 | } |
| 819 | 712 | |
| 820 | 713 | if (comp.config.link_libcpp) { |
| ... | ... | @@ -836,46 +729,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 836 | 729 | try Compilation.dumpArgv(io, argv.items); |
| 837 | 730 | } |
| 838 | 731 | |
| 839 | /// TODO delete this, libsystem must be resolved when setting up the compilation pipeline | |
| 840 | pub 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 | ||
| 879 | 732 | pub fn classifyInputFile(self: *MachO, input: link.Input) !void { |
| 880 | 733 | const tracy = trace(@src()); |
| 881 | 734 | defer tracy.end(); |
| ... | ... | @@ -888,7 +741,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void { |
| 888 | 741 | log.debug("classifying input file {f}", .{path}); |
| 889 | 742 | |
| 890 | 743 | const fh = try self.addFileHandle(file); |
| 891 | var buffer: [Archive.SARMAG]u8 = undefined; | |
| 744 | var buffer: [macho.ARMAG.len]u8 = undefined; | |
| 892 | 745 | |
| 893 | 746 | const fat_arch: ?fat.Arch = try self.parseFatFile(file, path); |
| 894 | 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 | 756 | return; |
| 904 | 757 | } |
| 905 | 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 | 760 | try self.addArchive(input.archive, fh, fat_arch); |
| 908 | 761 | return; |
| 909 | 762 | } |
| ... | ... | @@ -933,10 +786,10 @@ pub fn readMachHeader(io: Io, file: Io.File, offset: usize) !macho.mach_header_6 |
| 933 | 786 | return hdr; |
| 934 | 787 | } |
| 935 | 788 | |
| 936 | pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 { | |
| 789 | pub fn readArMagic(io: Io, file: Io.File, offset: usize, buffer: *[macho.ARMAG.len]u8) ![]const u8 { | |
| 937 | 790 | const nread = try file.readPositionalAll(io, buffer, offset); |
| 938 | 791 | if (nread != buffer.len) return error.InputOutput; |
| 939 | return buffer[0..Archive.SARMAG]; | |
| 792 | return buffer[0..macho.ARMAG.len]; | |
| 940 | 793 | } |
| 941 | 794 | |
| 942 | 795 | fn addObject(self: *MachO, path: Path, handle_index: File.HandleIndex, offset: u64) !void { |
| ... | ... | @@ -1159,10 +1012,6 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1159 | 1012 | const comp = self.base.comp; |
| 1160 | 1013 | const gpa = comp.gpa; |
| 1161 | 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; | |
| 1166 | 1015 | |
| 1167 | 1016 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1168 | 1017 | defer arena_alloc.deinit(); |
| ... | ... | @@ -1197,14 +1046,14 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1197 | 1046 | const stem = fs.path.stem(id.name); |
| 1198 | 1047 | |
| 1199 | 1048 | // Framework |
| 1200 | for (framework_dirs) |dir| { | |
| 1049 | for (comp.framework_dirs) |dir| { | |
| 1201 | 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 | } |
| 1204 | 1053 | |
| 1205 | 1054 | // Library |
| 1206 | 1055 | const lib_name = eatPrefix(stem, "lib") orelse stem; |
| 1207 | for (lib_directories) |lib_directory| { | |
| 1056 | for (self.lib_directories) |lib_directory| { | |
| 1208 | 1057 | test_path.clearRetainingCapacity(); |
| 1209 | 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 | 2973 | return self.getZigObject().?.freeNav(nav); |
| 3125 | 2974 | } |
| 3126 | 2975 | |
| 3127 | pub fn getNavVAddr(self: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { | |
| 3128 | return self.getZigObject().?.getNavVAddr(self, pt, nav_index, reloc_info); | |
| 2976 | pub fn navSymbol(self: *MachO, nav: InternPool.Nav.Index) link.Error!link.File.SymbolId { | |
| 2977 | return self.getZigObject().?.navSymbol(self, nav); | |
| 3129 | 2978 | } |
| 3130 | 2979 | |
| 3131 | pub fn lowerUav( | |
| 2980 | pub fn relocSymAddr(self: *MachO, reloc_info: link.File.RelocInfo) link.Error!void { | |
| 2981 | return self.getZigObject().?.relocSymAddr(self, reloc_info); | |
| 2982 | } | |
| 2983 | ||
| 2984 | pub fn uavSymbol( | |
| 3132 | 2985 | self: *MachO, |
| 3133 | 2986 | pt: Zcu.PerThread, |
| 3134 | 2987 | uav: InternPool.Index, |
| 3135 | 2988 | explicit_alignment: InternPool.Alignment, |
| 3136 | 2989 | ) !link.File.SymbolId { |
| 3137 | return self.getZigObject().?.lowerUav(self, pt, uav, explicit_alignment); | |
| 3138 | } | |
| 3139 | ||
| 3140 | pub fn getUavVAddr(self: *MachO, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { | |
| 3141 | return self.getZigObject().?.getUavVAddr(self, uav, reloc_info); | |
| 2990 | return self.getZigObject().?.uavSymbol(self, pt, uav, explicit_alignment); | |
| 3142 | 2991 | } |
| 3143 | 2992 | |
| 3144 | 2993 | pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 { |
| ... | ... | @@ -4231,7 +4080,7 @@ pub const null_sym = macho.nlist_64{ |
| 4231 | 4080 | pub const Platform = struct { |
| 4232 | 4081 | os_tag: std.Target.Os.Tag, |
| 4233 | 4082 | abi: std.Target.Abi, |
| 4234 | version: std.SemanticVersion, | |
| 4083 | version: link.DarwinSdkVersion, | |
| 4235 | 4084 | |
| 4236 | 4085 | /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to |
| 4237 | 4086 | /// the extracted minimum platform version. |
| ... | ... | @@ -4258,7 +4107,7 @@ pub const Platform = struct { |
| 4258 | 4107 | => .simulator, |
| 4259 | 4108 | else => .none, |
| 4260 | 4109 | }, |
| 4261 | .version = appleVersionToSemanticVersion(cmd.minos), | |
| 4110 | .version = @fromBackingInt(cmd.minos), | |
| 4262 | 4111 | }; |
| 4263 | 4112 | }, |
| 4264 | 4113 | .VERSION_MIN_IPHONEOS, |
| ... | ... | @@ -4277,7 +4126,7 @@ pub const Platform = struct { |
| 4277 | 4126 | else => unreachable, |
| 4278 | 4127 | }, |
| 4279 | 4128 | .abi = .none, |
| 4280 | .version = appleVersionToSemanticVersion(cmd.version), | |
| 4129 | .version = @fromBackingInt(cmd.version), | |
| 4281 | 4130 | }; |
| 4282 | 4131 | }, |
| 4283 | 4132 | else => unreachable, |
| ... | ... | @@ -4285,17 +4134,18 @@ pub const Platform = struct { |
| 4285 | 4134 | } |
| 4286 | 4135 | |
| 4287 | 4136 | pub fn fromTarget(target: *const std.Target) Platform { |
| 4137 | const semver = target.os.version_range.semver.min; | |
| 4288 | 4138 | return .{ |
| 4289 | 4139 | .os_tag = target.os.tag, |
| 4290 | 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 | } |
| 4294 | 4148 | |
| 4295 | pub fn toAppleVersion(plat: Platform) u32 { | |
| 4296 | return semanticVersionToAppleVersion(plat.version); | |
| 4297 | } | |
| 4298 | ||
| 4299 | 4149 | pub fn toApplePlatform(plat: Platform) macho.PLATFORM { |
| 4300 | 4150 | return switch (plat.os_tag) { |
| 4301 | 4151 | .driverkit => .DRIVERKIT, |
| ... | ... | @@ -4312,7 +4162,7 @@ pub const Platform = struct { |
| 4312 | 4162 | pub fn isBuildVersionCompatible(plat: Platform) bool { |
| 4313 | 4163 | inline for (supported_platforms) |sup_plat| { |
| 4314 | 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 | 4168 | return false; |
| ... | ... | @@ -4321,7 +4171,7 @@ pub const Platform = struct { |
| 4321 | 4171 | pub fn isVersionMinCompatible(plat: Platform) bool { |
| 4322 | 4172 | inline for (supported_platforms) |sup_plat| { |
| 4323 | 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 | 4177 | return false; |
| ... | ... | @@ -4380,86 +4230,8 @@ const supported_platforms = [_]SupportedPlatforms{ |
| 4380 | 4230 | }; |
| 4381 | 4231 | // zig fmt: on |
| 4382 | 4232 | |
| 4383 | pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { | |
| 4384 | const major = version.major; | |
| 4385 | const minor = version.minor; | |
| 4386 | const patch = version.patch; | |
| 4387 | return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch)); | |
| 4388 | } | |
| 4389 | ||
| 4390 | pub 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 | ||
| 4398 | fn 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. | |
| 4434 | fn 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. | |
| 4444 | fn 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; | |
| 4233 | pub fn setDarwinSdkVersion(self: *MachO, version: link.DarwinSdkVersion) link.Error!void { | |
| 4234 | self.sdk_version = version; | |
| 4463 | 4235 | } |
| 4464 | 4236 | |
| 4465 | 4237 | /// When allocating, the ideal_capacity is calculated by |
| ... | ... | @@ -4497,7 +4269,7 @@ const SystemLib = struct { |
| 4497 | 4269 | .must_link = obj.must_link, |
| 4498 | 4270 | .hidden = obj.hidden, |
| 4499 | 4271 | }, |
| 4500 | .dso => |dso| .{ | |
| 4272 | inline .dso, .tbd => |dso| .{ | |
| 4501 | 4273 | .path = dso.path, |
| 4502 | 4274 | .needed = dso.needed, |
| 4503 | 4275 | .weak = dso.weak, |
| ... | ... | @@ -4507,8 +4279,6 @@ const SystemLib = struct { |
| 4507 | 4279 | } |
| 4508 | 4280 | }; |
| 4509 | 4281 | |
| 4510 | pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout; | |
| 4511 | ||
| 4512 | 4282 | const UndefinedTreatment = enum { |
| 4513 | 4283 | @"error", |
| 4514 | 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 | 17 | const offset = if (fat_arch) |ar| ar.offset else 0; |
| 18 | 18 | const end_pos = if (fat_arch) |ar| offset + ar.size else (try handle.stat(io)).size; |
| 19 | 19 | |
| 20 | var pos: usize = offset + SARMAG; | |
| 20 | var pos: usize = offset + macho.ARMAG.len; | |
| 21 | 21 | while (true) { |
| 22 | 22 | if (pos >= end_pos) break; |
| 23 | 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 | 30 | const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*; |
| 31 | 31 | pos += @sizeOf(ar_hdr); |
| 32 | 32 | |
| 33 | if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { | |
| 33 | if (!mem.eql(u8, &hdr.ar_fmag, macho.ARFMAG)) { | |
| 34 | 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 | } |
| 38 | 38 | |
| ... | ... | @@ -116,17 +116,6 @@ pub fn writeHeader( |
| 116 | 116 | } |
| 117 | 117 | } |
| 118 | 118 | |
| 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. | |
| 123 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | |
| 124 | /// Size of that string. | |
| 125 | pub const SARMAG: u4 = 8; | |
| 126 | ||
| 127 | /// String in ar_fmag at the end of each header. | |
| 128 | const ARFMAG: *const [2:0]u8 = "`\n"; | |
| 129 | ||
| 130 | 119 | pub const SYMDEF = "__.SYMDEF"; |
| 131 | 120 | pub const SYMDEF64 = "__.SYMDEF_64"; |
| 132 | 121 | pub const SYMDEF_SORTED = "__.SYMDEF SORTED"; |
| ... | ... | @@ -146,7 +135,7 @@ pub const ar_hdr = extern struct { |
| 146 | 135 | /// File size, in ASCII decimal. |
| 147 | 136 | ar_size: [10]u8 = "0\x20\x20\x20\x20\x20\x20\x20\x20\x20".*, |
| 148 | 137 | /// Always contains ARFMAG. |
| 149 | ar_fmag: [2]u8 = ARFMAG.*, | |
| 138 | ar_fmag: [2]u8 = macho.ARFMAG.*, | |
| 150 | 139 | |
| 151 | 140 | fn date(self: ar_hdr) !u64 { |
| 152 | 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 | 611 | assert(!self.debug_strtab_dirty); |
| 612 | 612 | } |
| 613 | 613 | |
| 614 | pub fn getNavVAddr( | |
| 615 | self: *ZigObject, | |
| 614 | pub fn navSymbol( | |
| 615 | zo: *ZigObject, | |
| 616 | 616 | macho_file: *MachO, |
| 617 | pt: Zcu.PerThread, | |
| 618 | 617 | nav_index: InternPool.Nav.Index, |
| 619 | reloc_info: link.File.RelocInfo, | |
| 620 | ) !u64 { | |
| 621 | const zcu = pt.zcu; | |
| 618 | ) link.Error!link.File.SymbolId { | |
| 619 | const zcu = macho_file.base.comp.zcu.?; | |
| 622 | 620 | const ip = &zcu.intern_pool; |
| 623 | 621 | const nav = ip.getNav(nav_index); |
| 624 | log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index }); | |
| 625 | const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol( | |
| 626 | macho_file, | |
| 627 | nav.name.toSlice(ip), | |
| 628 | @"extern".lib_name.toSlice(ip), | |
| 629 | ) else try self.getOrCreateMetadataForNav(macho_file, nav_index); | |
| 630 | const sym = self.symbols.items[sym_index]; | |
| 631 | const vaddr = sym.getAddress(.{}, macho_file); | |
| 632 | switch (reloc_info.parent) { | |
| 633 | .none => unreachable, | |
| 634 | .atom_index => |atom_index| { | |
| 635 | const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?; | |
| 636 | try parent_atom.addReloc(macho_file, .{ | |
| 637 | .tag = .@"extern", | |
| 638 | .offset = @intCast(reloc_info.offset), | |
| 639 | .target = sym_index, | |
| 640 | .addend = reloc_info.addend, | |
| 641 | .type = .unsigned, | |
| 642 | .meta = .{ | |
| 643 | .pcrel = false, | |
| 644 | .has_subtractor = false, | |
| 645 | .length = 3, | |
| 646 | .symbolnum = @intCast(sym.nlist_idx), | |
| 647 | }, | |
| 648 | }); | |
| 649 | }, | |
| 650 | .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ | |
| 651 | .source_off = @intCast(reloc_info.offset), | |
| 652 | .target_sym = @fromBackingInt(@intCast(sym_index)), | |
| 653 | .target_off = reloc_info.addend, | |
| 654 | }), | |
| 622 | if (nav.getExtern(ip)) |@"extern"| { | |
| 623 | const sym_index = try zo.getGlobalSymbol( | |
| 624 | macho_file, | |
| 625 | nav.name.toSlice(ip), | |
| 626 | @"extern".lib_name.toSlice(ip), | |
| 627 | ); | |
| 628 | if (@"extern".linkage == .weak) { | |
| 629 | zo.symbols.items[sym_index].flags.weak = true; | |
| 630 | } | |
| 631 | if (nav.resolved.?.@"threadlocal") { | |
| 632 | zo.symbols.items[sym_index].flags.tlv = true; | |
| 633 | } | |
| 634 | return @fromBackingInt(sym_index); | |
| 635 | } else { | |
| 636 | const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); | |
| 637 | if (nav.resolved.?.@"threadlocal") { | |
| 638 | zo.symbols.items[sym_index].flags.tlv = true; | |
| 639 | } | |
| 640 | return @fromBackingInt(sym_index); | |
| 655 | 641 | } |
| 656 | return vaddr; | |
| 657 | 642 | } |
| 658 | 643 | |
| 659 | pub fn getUavVAddr( | |
| 644 | pub fn relocSymAddr( | |
| 660 | 645 | self: *ZigObject, |
| 661 | 646 | macho_file: *MachO, |
| 662 | uav: InternPool.Index, | |
| 663 | 647 | reloc_info: link.File.RelocInfo, |
| 664 | ) !u64 { | |
| 665 | const sym_index = self.uavs.get(uav).?.symbol_index; | |
| 648 | ) !void { | |
| 649 | const sym_index = @backingInt(reloc_info.target); | |
| 666 | 650 | const sym = self.symbols.items[sym_index]; |
| 667 | const vaddr = sym.getAddress(.{}, macho_file); | |
| 668 | 651 | switch (reloc_info.parent) { |
| 669 | 652 | .none => unreachable, |
| 670 | 653 | .atom_index => |atom_index| { |
| ... | ... | @@ -689,10 +672,9 @@ pub fn getUavVAddr( |
| 689 | 672 | .target_off = reloc_info.addend, |
| 690 | 673 | }), |
| 691 | 674 | } |
| 692 | return vaddr; | |
| 693 | 675 | } |
| 694 | 676 | |
| 695 | pub fn lowerUav( | |
| 677 | pub fn uavSymbol( | |
| 696 | 678 | self: *ZigObject, |
| 697 | 679 | macho_file: *MachO, |
| 698 | 680 | pt: Zcu.PerThread, |
| ... | ... | @@ -1281,7 +1263,7 @@ pub fn updateExports( |
| 1281 | 1263 | break :blk self.navs.getPtr(nav).?; |
| 1282 | 1264 | }, |
| 1283 | 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 | 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 | 9 | const DebugSymbols = @import("DebugSymbols.zig"); |
| 10 | 10 | const Dylib = @import("Dylib.zig"); |
| 11 | 11 | const MachO = @import("../MachO.zig"); |
| 12 | const link = @import("../../link.zig"); | |
| 12 | 13 | |
| 13 | 14 | pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; |
| 14 | 15 | |
| ... | ... | @@ -296,7 +297,7 @@ pub fn writeRpathLC(rpath: []const u8, writer: *Writer) !void { |
| 296 | 297 | } |
| 297 | 298 | } |
| 298 | 299 | |
| 299 | pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: *Writer) !void { | |
| 300 | pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?link.DarwinSdkVersion, writer: *Writer) !void { | |
| 300 | 301 | const cmd: macho.LC = switch (platform.os_tag) { |
| 301 | 302 | .macos => .VERSION_MIN_MACOSX, |
| 302 | 303 | .ios, .maccatalyst => .VERSION_MIN_IPHONEOS, |
| ... | ... | @@ -306,24 +307,18 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer |
| 306 | 307 | }; |
| 307 | 308 | try writer.writeAll(mem.asBytes(&macho.version_min_command{ |
| 308 | 309 | .cmd = cmd, |
| 309 | .version = platform.toAppleVersion(), | |
| 310 | .sdk = if (sdk_version) |ver| | |
| 311 | MachO.semanticVersionToAppleVersion(ver) | |
| 312 | else | |
| 313 | platform.toAppleVersion(), | |
| 310 | .version = @backingInt(platform.version), | |
| 311 | .sdk = @backingInt(sdk_version orelse platform.version), | |
| 314 | 312 | })); |
| 315 | 313 | } |
| 316 | 314 | |
| 317 | pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: *Writer) !void { | |
| 315 | pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?link.DarwinSdkVersion, writer: *Writer) !void { | |
| 318 | 316 | const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); |
| 319 | 317 | try writer.writeStruct(@as(macho.build_version_command, .{ |
| 320 | 318 | .cmdsize = cmdsize, |
| 321 | 319 | .platform = platform.toApplePlatform(), |
| 322 | .minos = platform.toAppleVersion(), | |
| 323 | .sdk = if (sdk_version) |ver| | |
| 324 | MachO.semanticVersionToAppleVersion(ver) | |
| 325 | else | |
| 326 | platform.toAppleVersion(), | |
| 320 | .minos = @backingInt(platform.version), | |
| 321 | .sdk = @backingInt(sdk_version orelse platform.version), | |
| 327 | 322 | .ntools = 1, |
| 328 | 323 | }), .little); |
| 329 | 324 | try writer.writeAll(mem.asBytes(&macho.build_tool_version{ |
src/link/MachO/relocatable.zig+31-48| ... | ... | @@ -1,25 +1,25 @@ |
| 1 | pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { | |
| 2 | const gpa = comp.gpa; | |
| 1 | pub fn flushObject(macho_file: *MachO, comp: *Compilation) link.Error!void { | |
| 3 | 2 | const io = comp.io; |
| 4 | 3 | const diags = &comp.link_diags; |
| 5 | 4 | |
| 6 | // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. | |
| 7 | var positionals = std.array_list.Managed(link.Input).init(gpa); | |
| 8 | defer positionals.deinit(); | |
| 9 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 10 | positionals.appendSliceAssumeCapacity(comp.link_inputs); | |
| 11 | ||
| 12 | for (comp.c_objects.items) |c_object| { | |
| 13 | try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path)); | |
| 14 | } | |
| 15 | ||
| 16 | if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); | |
| 17 | ||
| 18 | if (macho_file.getZigObject() == null and positionals.items.len == 1) { | |
| 5 | one_input: { | |
| 6 | if (macho_file.getZigObject() != null) { | |
| 7 | break :one_input; | |
| 8 | } | |
| 9 | var only_input: ?link.Input = null; | |
| 10 | for (macho_file.all_inputs.items) |input| switch (input) { | |
| 11 | .res => unreachable, | |
| 12 | .dso, .tbd => {}, | |
| 13 | .object, .archive => if (only_input == null) { | |
| 14 | only_input = input; | |
| 15 | } else { | |
| 16 | break :one_input; | |
| 17 | }, | |
| 18 | }; | |
| 19 | 19 | // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all |
| 20 | 20 | // debug info segments/sections (this is apparently by design by Apple), we copy |
| 21 | 21 | // the *only* input file over. |
| 22 | const path = positionals.items[0].path(); | |
| 22 | const path = (only_input orelse break :one_input).path(); | |
| 23 | 23 | const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| |
| 24 | 24 | return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) }); |
| 25 | 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 | 29 | return; |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | for (positionals.items) |link_input| { | |
| 33 | macho_file.classifyInputFile(link_input) catch |err| | |
| 34 | diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); | |
| 35 | } | |
| 32 | for (macho_file.all_inputs.items) |input| switch (input) { | |
| 33 | .res => unreachable, | |
| 34 | .dso, .tbd => {}, | |
| 35 | .object, .archive => macho_file.classifyInputFile(input) catch |err| | |
| 36 | diags.addParseError(input.path(), "failed to read input file: {t}", .{err}), | |
| 37 | }; | |
| 36 | 38 | |
| 37 | 39 | if (diags.hasErrors()) return error.AlreadyReported; |
| 38 | 40 | |
| ... | ... | @@ -75,35 +77,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat |
| 75 | 77 | try writeHeader(macho_file, ncmds, sizeofcmds); |
| 76 | 78 | } |
| 77 | 79 | |
| 78 | pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { | |
| 80 | pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void { | |
| 79 | 81 | const gpa = comp.gpa; |
| 80 | const io = comp.io; | |
| 81 | 82 | const diags = &macho_file.base.comp.link_diags; |
| 82 | 83 | |
| 83 | var positionals = std.array_list.Managed(link.Input).init(gpa); | |
| 84 | defer positionals.deinit(); | |
| 85 | ||
| 86 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 87 | positionals.appendSliceAssumeCapacity(comp.link_inputs); | |
| 88 | ||
| 89 | for (comp.c_objects.items) |c_object| { | |
| 90 | try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path)); | |
| 91 | } | |
| 92 | ||
| 93 | if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); | |
| 94 | ||
| 95 | if (comp.compiler_rt_strat == .obj) { | |
| 96 | try positionals.append(try link.openObjectInput(io, diags, comp.compiler_rt_obj.?.full_object_path)); | |
| 97 | } | |
| 98 | ||
| 99 | if (comp.ubsan_rt_strat == .obj) { | |
| 100 | try positionals.append(try link.openObjectInput(io, diags, comp.ubsan_rt_obj.?.full_object_path)); | |
| 101 | } | |
| 102 | ||
| 103 | for (positionals.items) |link_input| { | |
| 104 | macho_file.classifyInputFile(link_input) catch |err| | |
| 105 | diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); | |
| 106 | } | |
| 84 | for (macho_file.all_inputs.items) |input| switch (input) { | |
| 85 | .res => unreachable, | |
| 86 | .dso, .tbd => {}, | |
| 87 | .object, .archive => macho_file.classifyInputFile(input) catch |err| | |
| 88 | diags.addParseError(input.path(), "failed to read input file: {t}", .{err}), | |
| 89 | }; | |
| 107 | 90 | |
| 108 | 91 | if (diags.hasErrors()) return error.AlreadyReported; |
| 109 | 92 | |
| ... | ... | @@ -168,7 +151,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 168 | 151 | |
| 169 | 152 | // Update file offsets of contributing objects |
| 170 | 153 | const total_size: usize = blk: { |
| 171 | var pos: usize = Archive.SARMAG; | |
| 154 | var pos: usize = macho.ARMAG.len; | |
| 172 | 155 | pos += @sizeOf(Archive.ar_hdr); |
| 173 | 156 | pos += Archive.SYMDEF.len + 1; |
| 174 | 157 | pos = mem.alignForward(usize, pos, 8); |
| ... | ... | @@ -211,7 +194,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 211 | 194 | var writer: Writer = .fixed(buffer); |
| 212 | 195 | |
| 213 | 196 | // Write magic |
| 214 | writer.writeAll(Archive.ARMAG) catch unreachable; | |
| 197 | writer.writeAll(macho.ARMAG) catch unreachable; | |
| 215 | 198 | |
| 216 | 199 | // Write symtab |
| 217 | 200 | ar_symtab.write(format, macho_file, &writer) catch |err| |
src/link/MachO2.zig created+1357| ... | ... | @@ -0,0 +1,1357 @@ |
| 1 | const MachO = @This(); | |
| 2 | ||
| 3 | base: link.File, | |
| 4 | options: link.File.OpenOptions, | |
| 5 | ||
| 6 | const_prog_node: std.Progress.Node, | |
| 7 | synth_prog_node: std.Progress.Node, | |
| 8 | input_prog_node: std.Progress.Node, | |
| 9 | ||
| 10 | mf: MappedFile, | |
| 11 | nodes: std.MultiArrayList(Node), | |
| 12 | ||
| 13 | node: struct { | |
| 14 | macho: MappedFile.Node.Index, | |
| 15 | header: MappedFile.Node.Index, | |
| 16 | symtab: MappedFile.Node.Index, | |
| 17 | strtab: MappedFile.Node.Index, | |
| 18 | }, | |
| 19 | ||
| 20 | lc: 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. | |
| 27 | segments: std.MultiArrayList(struct { | |
| 28 | lc: LoadCommand, | |
| 29 | node: MappedFile.Node.Index, | |
| 30 | }), | |
| 31 | sections: 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. | |
| 38 | segments_by_name: std.array_hash_map.Custom(void, void, void, true), | |
| 39 | /// Accessed with `Section.NameAdapter` using `Section.NameAdapter.SegmentAndSection`. | |
| 40 | sections_by_name: std.array_hash_map.Custom(void, void, void, true), | |
| 41 | ||
| 42 | /// Accessed with `String.Adapter` using `[]const u8` keys. | |
| 43 | strtab: std.array_hash_map.Custom(String, void, void, true), | |
| 44 | ||
| 45 | symtab: std.ArrayList(Symbol), | |
| 46 | ||
| 47 | sections_changed_index: std.array_hash_map.Auto(Section, void), | |
| 48 | ||
| 49 | const 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`. | |
| 53 | const 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. | |
| 74 | const 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`. | |
| 119 | const 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 | ||
| 178 | const 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 | ||
| 205 | const 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 | }; | |
| 216 | fn getNode(macho: *const MachO, ni: MappedFile.Node.Index) Node { | |
| 217 | return macho.nodes.get(@backingInt(ni)); | |
| 218 | } | |
| 219 | ||
| 220 | pub 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 | } | |
| 228 | pub 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 | } | |
| 236 | fn 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 | } | |
| 296 | fn 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. | |
| 476 | fn 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 | ||
| 525 | fn 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 | ||
| 600 | const 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 | ||
| 621 | fn 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 | ||
| 648 | fn 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 | ||
| 657 | fn 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. | |
| 689 | fn 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. | |
| 701 | fn 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 | ||
| 760 | fn 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 | ||
| 901 | fn 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 | ||
| 910 | fn 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 | ||
| 923 | fn targetEndian(macho: *const MachO) std.lang.Endian { | |
| 924 | _ = macho; | |
| 925 | return .little; | |
| 926 | } | |
| 927 | fn targetPageAlign(macho: *const MachO) MappedFile.Alignment { | |
| 928 | _ = macho; | |
| 929 | return .fromByteUnits(0x4000); | |
| 930 | } | |
| 931 | fn 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 | } | |
| 944 | fn 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 | ||
| 962 | pub 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 | ||
| 976 | pub 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 | } | |
| 983 | pub 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 | ||
| 994 | pub fn navSymbol(macho: *MachO, nav_id: InternPool.Nav.Index) link.Error!link.File.SymbolId { | |
| 995 | _ = macho; | |
| 996 | _ = nav_id; | |
| 997 | @panic("TODO"); | |
| 998 | } | |
| 999 | pub 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 | } | |
| 1011 | pub 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 | ||
| 1020 | pub 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 | } | |
| 1027 | fn 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 | } | |
| 1061 | pub 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 | } | |
| 1091 | pub fn prelink(macho: *MachO, prog_node: std.Progress.Node) link.Error!void { | |
| 1092 | _ = macho; | |
| 1093 | _ = prog_node; | |
| 1094 | } | |
| 1095 | pub 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 | } | |
| 1126 | pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void { | |
| 1127 | _ = macho; | |
| 1128 | _ = pt; | |
| 1129 | @panic("TODO"); | |
| 1130 | } | |
| 1131 | pub 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 | } | |
| 1166 | fn 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 | } | |
| 1182 | fn 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 | } | |
| 1209 | fn 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 | ||
| 1227 | pub 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 | } | |
| 1239 | pub 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 | } | |
| 1245 | pub 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 | ||
| 1256 | fn 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 | ||
| 1268 | pub 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 | } | |
| 1275 | fn 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 | ||
| 1346 | const std = @import("std"); | |
| 1347 | const assert = std.debug.assert; | |
| 1348 | const log = std.log.scoped(.link); | |
| 1349 | const Allocator = std.mem.Allocator; | |
| 1350 | const Io = std.Io; | |
| 1351 | ||
| 1352 | const link = @import("../link.zig"); | |
| 1353 | const codegen = @import("../codegen.zig"); | |
| 1354 | const Compilation = @import("../Compilation.zig"); | |
| 1355 | const InternPool = @import("../InternPool.zig"); | |
| 1356 | const MappedFile = @import("MappedFile.zig"); | |
| 1357 | const Zcu = @import("../Zcu.zig"); |
src/link/MappedFile.zig+6-6| ... | ... | @@ -495,12 +495,12 @@ pub const Node = extern struct { |
| 495 | 495 | } |
| 496 | 496 | |
| 497 | 497 | pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool { |
| 498 | var parent_ni = ni; | |
| 499 | while (parent_ni != .root) { | |
| 500 | const parent_node = parent_ni.get(mf); | |
| 501 | if (!parent_node.flags.bubbles_moved) break; | |
| 502 | if (parent_node.flags.moved) return true; | |
| 503 | parent_ni = parent_node.parent.unwrap().?; | |
| 498 | var cur_ni = ni; | |
| 499 | while (cur_ni != .root) { | |
| 500 | if (cur_ni.get(mf).flags.moved) return true; | |
| 501 | const parent_ni = cur_ni.parent(mf).unwrap().?; | |
| 502 | if (!parent_ni.get(mf).flags.bubbles_moved) break; | |
| 503 | cur_ni = parent_ni; | |
| 504 | 504 | } |
| 505 | 505 | return false; |
| 506 | 506 | } |
src/link/Wasm.zig+2| ... | ... | @@ -3783,6 +3783,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| 3783 | 3783 | switch (input) { |
| 3784 | 3784 | .res => unreachable, |
| 3785 | 3785 | .dso => unreachable, |
| 3786 | .tbd => unreachable, | |
| 3786 | 3787 | .object, .archive => |obj| { |
| 3787 | 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 | 3793 | switch (input) { |
| 3793 | 3794 | .res => unreachable, |
| 3794 | 3795 | .dso => unreachable, |
| 3796 | .tbd => unreachable, | |
| 3795 | 3797 | .object => |obj| try parseObject(wasm, obj), |
| 3796 | 3798 | .archive => |obj| try parseArchive(wasm, obj), |
| 3797 | 3799 | } |
src/main.zig+126-150| ... | ... | @@ -893,13 +893,6 @@ const ArgsIterator = struct { |
| 893 | 893 | } |
| 894 | 894 | }; |
| 895 | 895 | |
| 896 | /// Similar to `link.Framework` except it doesn't store yet unresolved | |
| 897 | /// path to the framework. | |
| 898 | const Framework = struct { | |
| 899 | needed: bool = false, | |
| 900 | weak: bool = false, | |
| 901 | }; | |
| 902 | ||
| 903 | 896 | const CliModule = struct { |
| 904 | 897 | root_path: []const u8, |
| 905 | 898 | root_src_path: []const u8, |
| ... | ... | @@ -1117,12 +1110,12 @@ fn buildOutputType( |
| 1117 | 1110 | |
| 1118 | 1111 | .llvm_m_args = .empty, |
| 1119 | 1112 | .sysroot = null, |
| 1120 | .lib_directories = .empty, // populated by createModule() | |
| 1121 | 1113 | .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 | 1117 | .libc_installation = null, |
| 1123 | 1118 | .want_native_include_dirs = false, |
| 1124 | .frameworks = .empty, | |
| 1125 | .framework_dirs = .empty, | |
| 1126 | 1119 | .rpath_list = .empty, |
| 1127 | 1120 | .each_lib_rpath = null, |
| 1128 | 1121 | .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map), |
| ... | ... | @@ -1275,13 +1268,25 @@ fn buildOutputType( |
| 1275 | 1268 | } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { |
| 1276 | 1269 | try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal()); |
| 1277 | 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 | 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 | 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 | 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 | 1290 | } else if (mem.eql(u8, arg, "-install_name")) { |
| 1286 | 1291 | install_name = args_iter.nextOrFatal(); |
| 1287 | 1292 | } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| { |
| ... | ... | @@ -1396,11 +1401,11 @@ fn buildOutputType( |
| 1396 | 1401 | } else if (mem.eql(u8, arg, "-iframework")) { |
| 1397 | 1402 | const path = args_iter.nextOrFatal(); |
| 1398 | 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 -F | |
| 1404 | try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F | |
| 1400 | 1405 | } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) { |
| 1401 | 1406 | const path = args_iter.nextOrFatal(); |
| 1402 | 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 -F | |
| 1408 | try create_module.framework_dir_args.append(arena, path); // Forward to the backend as -F | |
| 1404 | 1409 | } else if (mem.eql(u8, arg, "--version")) { |
| 1405 | 1410 | const next_arg = args_iter.nextOrFatal(); |
| 1406 | 1411 | version = std.SemanticVersion.parse(next_arg) catch |err| { |
| ... | ... | @@ -1828,7 +1833,7 @@ fn buildOutputType( |
| 1828 | 1833 | } else if (mem.cutPrefix(u8, arg, "-L")) |rest| { |
| 1829 | 1834 | try create_module.lib_dir_args.append(arena, rest); |
| 1830 | 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 | 1837 | } else if (mem.cutPrefix(u8, arg, "-l")) |name| { |
| 1833 | 1838 | // We don't know whether this library is part of libc |
| 1834 | 1839 | // or libc++ until we resolve the target, so we append |
| ... | ... | @@ -2532,8 +2537,12 @@ fn buildOutputType( |
| 2532 | 2537 | disable_c_depfile = true; |
| 2533 | 2538 | try cc_argv.appendSlice(arena, it.other_args); |
| 2534 | 2539 | }, |
| 2535 | .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg), | |
| 2536 | .framework => try create_module.frameworks.put(arena, it.only_arg, .{}), | |
| 2540 | .framework_dir => try create_module.framework_dir_args.append(arena, it.only_arg), | |
| 2541 | .framework => try create_module.cli_link_inputs.append(arena, .{ .framework_query = .{ | |
| 2542 | .name = it.only_arg, | |
| 2543 | .needed = false, | |
| 2544 | .weak = false, | |
| 2545 | } }), | |
| 2537 | 2546 | .nostdlibinc => create_module.want_native_include_dirs = false, |
| 2538 | 2547 | .strip => mod_opts.strip = true, |
| 2539 | 2548 | .exec_model => { |
| ... | ... | @@ -2561,7 +2570,11 @@ fn buildOutputType( |
| 2561 | 2570 | }, |
| 2562 | 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 | 2578 | .headerpad_max_install_names => headerpad_max_install_names = true, |
| 2566 | 2579 | .compress_debug_sections => { |
| 2567 | 2580 | if (it.only_arg.len == 0) { |
| ... | ... | @@ -2914,11 +2927,23 @@ fn buildOutputType( |
| 2914 | 2927 | fatal("unable to parse minor subsystem version {q}: {t}", .{ minor, err }); |
| 2915 | 2928 | }; |
| 2916 | 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 | 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 | 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 | 2947 | } else if (mem.eql(u8, arg, "-needed_library")) { |
| 2923 | 2948 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| 2924 | 2949 | .name = linker_args_it.nextOrFatal(), |
| ... | ... | @@ -3400,59 +3425,6 @@ fn buildOutputType( |
| 3400 | 3425 | } |
| 3401 | 3426 | } |
| 3402 | 3427 | |
| 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 | 3428 | if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) { |
| 3457 | 3429 | const total_obj_count = create_module.c_source_files.items.len + |
| 3458 | 3430 | @intFromBool(root_src_file != null) + |
| ... | ... | @@ -3689,6 +3661,7 @@ fn buildOutputType( |
| 3689 | 3661 | .emit_docs = emit_docs_resolved, |
| 3690 | 3662 | .emit_implib = emit_implib_resolved, |
| 3691 | 3663 | .lib_directories = create_module.lib_directories.items, |
| 3664 | .framework_directories = create_module.framework_directories.items, | |
| 3692 | 3665 | .rpath_list = create_module.rpath_list.items, |
| 3693 | 3666 | .symbol_wrap_set = symbol_wrap_set, |
| 3694 | 3667 | .c_source_files = create_module.c_source_files.items, |
| ... | ... | @@ -3697,8 +3670,6 @@ fn buildOutputType( |
| 3697 | 3670 | .rc_includes = rc_includes, |
| 3698 | 3671 | .mingw_unicode_entry_point = mingw_unicode_entry_point, |
| 3699 | 3672 | .link_inputs = create_module.link_inputs.items, |
| 3700 | .framework_dirs = create_module.framework_dirs.items, | |
| 3701 | .frameworks = resolved_frameworks.items, | |
| 3702 | 3673 | .windows_lib_names = create_module.windows_libs.keys(), |
| 3703 | 3674 | .want_compiler_rt = if (zig_cc_explicitly_link_compiler_rt) true else want_compiler_rt, |
| 3704 | 3675 | .want_ubsan_rt = want_ubsan_rt, |
| ... | ... | @@ -4041,13 +4012,13 @@ const CreateModule = struct { |
| 4041 | 4012 | /// CPU features. |
| 4042 | 4013 | llvm_m_args: std.ArrayList([]const u8), |
| 4043 | 4014 | sysroot: ?[]const u8, |
| 4044 | lib_directories: std.ArrayList(Directory), | |
| 4045 | 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 | 4019 | libc_installation: ?LibCInstallation, |
| 4047 | 4020 | want_native_include_dirs: bool, |
| 4048 | frameworks: std.array_hash_map.String(Framework), | |
| 4049 | 4021 | native_system_include_paths: []const []const u8, |
| 4050 | framework_dirs: std.ArrayList([]const u8), | |
| 4051 | 4022 | rpath_list: std.ArrayList([]const u8), |
| 4052 | 4023 | each_lib_rpath: ?bool, |
| 4053 | 4024 | libc_paths_file: ?[]const u8, |
| ... | ... | @@ -4158,7 +4129,7 @@ fn createModule( |
| 4158 | 4129 | var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty; |
| 4159 | 4130 | defer unresolved_link_inputs.deinit(gpa); |
| 4160 | 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 | 4133 | for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) { |
| 4163 | 4134 | .name_query => |nq| { |
| 4164 | 4135 | const lib_name = nq.name; |
| ... | ... | @@ -4199,39 +4170,52 @@ fn createModule( |
| 4199 | 4170 | } |
| 4200 | 4171 | |
| 4201 | 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 | 4179 | else => { |
| 4205 | 4180 | unresolved_link_inputs.appendAssumeCapacity(cli_link_input); |
| 4206 | 4181 | }, |
| 4207 | 4182 | }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs. |
| 4208 | 4183 | |
| 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; | |
| 4210 | 4185 | |
| 4211 | 4186 | // Resolve the library path arguments with respect to sysroot. |
| 4212 | 4187 | try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len); |
| 4213 | if (create_module.sysroot) |root| { | |
| 4214 | for (create_module.lib_dir_args.items) |lib_dir_arg| { | |
| 4215 | if (fs.path.isAbsolute(lib_dir_arg)) { | |
| 4216 | const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..]; | |
| 4217 | const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir }); | |
| 4218 | addLibDirectoryWarn(io, &create_module.lib_directories, full_path); | |
| 4219 | } else { | |
| 4220 | addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg); | |
| 4188 | for (create_module.lib_dir_args.items) |dir_arg| { | |
| 4189 | const path: []const u8 = path: { | |
| 4190 | if (fs.path.isAbsolute(dir_arg)) { | |
| 4191 | if (create_module.sysroot) |sysroot| { | |
| 4192 | // Change the path root to the given sysroot. | |
| 4193 | const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..]; | |
| 4194 | break :path try fs.path.join(arena, &.{ sysroot, stripped_dir }); | |
| 4195 | } | |
| 4221 | 4196 | } |
| 4222 | } | |
| 4223 | } else { | |
| 4224 | for (create_module.lib_dir_args.items) |lib_dir_arg| { | |
| 4225 | addLibDirectoryWarn(io, &create_module.lib_directories, lib_dir_arg); | |
| 4226 | } | |
| 4197 | break :path dir_arg; | |
| 4198 | }; | |
| 4199 | appendLibDirOrWarn(io, &create_module.lib_directories, path); | |
| 4227 | 4200 | } |
| 4228 | 4201 | create_module.lib_dir_args = undefined; // From here we use lib_directories instead. |
| 4229 | 4202 | |
| 4230 | if (resolved_target.is_native_os and target.os.tag.isDarwin()) { | |
| 4231 | // If we want to link against frameworks, we need system headers. | |
| 4232 | if (create_module.frameworks.count() > 0) | |
| 4233 | create_module.want_native_include_dirs = true; | |
| 4203 | // Likewise for framework path arguments. | |
| 4204 | try create_module.framework_directories.ensureUnusedCapacity(arena, create_module.framework_dir_args.items.len); | |
| 4205 | for (create_module.framework_dir_args.items) |dir_arg| { | |
| 4206 | const path: []const u8 = path: { | |
| 4207 | if (fs.path.isAbsolute(dir_arg)) { | |
| 4208 | if (create_module.sysroot) |sysroot| { | |
| 4209 | // Change the path root to the given sysroot. | |
| 4210 | const stripped_dir = dir_arg[fs.path.parsePath(dir_arg).root.len..]; | |
| 4211 | break :path try fs.path.join(arena, &.{ sysroot, stripped_dir }); | |
| 4212 | } | |
| 4213 | } | |
| 4214 | break :path dir_arg; | |
| 4215 | }; | |
| 4216 | appendLibDirOrWarn(io, &create_module.framework_directories, path); | |
| 4234 | 4217 | } |
| 4218 | create_module.framework_dir_args = undefined; // From here we use framework_directories instead. | |
| 4235 | 4219 | |
| 4236 | 4220 | if (create_module.each_lib_rpath orelse resolved_target.is_native_os) { |
| 4237 | 4221 | try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len); |
| ... | ... | @@ -4253,11 +4237,17 @@ fn createModule( |
| 4253 | 4237 | |
| 4254 | 4238 | create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena); |
| 4255 | 4239 | |
| 4256 | try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items); | |
| 4257 | 4240 | try create_module.rpath_list.appendSlice(arena, paths.rpaths.items); |
| 4258 | 4241 | |
| 4259 | 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 | } |
| 4262 | 4252 | |
| 4263 | 4253 | if (create_module.libc_paths_file) |paths_file| { |
| ... | ... | @@ -4266,7 +4256,7 @@ fn createModule( |
| 4266 | 4256 | } |
| 4267 | 4257 | |
| 4268 | 4258 | 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 | 4261 | if (create_module.libc_installation == null) { |
| 4272 | 4262 | create_module.libc_installation = LibCInstallation.findNative(arena, io, .{ |
| ... | ... | @@ -4277,9 +4267,19 @@ fn createModule( |
| 4277 | 4267 | fatal("unable to find native libc installation: {t}", .{err}); |
| 4278 | 4268 | }; |
| 4279 | 4269 | } |
| 4270 | ||
| 4280 | 4271 | try create_module.lib_directories.ensureUnusedCapacity(arena, 2); |
| 4281 | addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?); | |
| 4282 | addLibDirectoryWarn(io, &create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?); | |
| 4272 | ||
| 4273 | appendLibDirOrWarn( | |
| 4274 | io, | |
| 4275 | &create_module.lib_directories, | |
| 4276 | create_module.libc_installation.?.msvc_lib_dir.?, | |
| 4277 | ); | |
| 4278 | appendLibDirOrWarn( | |
| 4279 | io, | |
| 4280 | &create_module.lib_directories, | |
| 4281 | create_module.libc_installation.?.kernel32_lib_dir.?, | |
| 4282 | ); | |
| 4283 | 4283 | } |
| 4284 | 4284 | |
| 4285 | 4285 | // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`. |
| ... | ... | @@ -4291,6 +4291,7 @@ fn createModule( |
| 4291 | 4291 | &unresolved_link_inputs, |
| 4292 | 4292 | &create_module.link_inputs, |
| 4293 | 4293 | create_module.lib_directories.items, |
| 4294 | create_module.framework_directories.items, | |
| 4294 | 4295 | color, |
| 4295 | 4296 | ) catch |err| fatal("failed to resolve link inputs: {t}", .{err}); |
| 4296 | 4297 | |
| ... | ... | @@ -6085,33 +6086,6 @@ const ClangSearchSanitizer = struct { |
| 6085 | 6086 | }; |
| 6086 | 6087 | }; |
| 6087 | 6088 | |
| 6088 | fn 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 | ||
| 6115 | 6089 | fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes { |
| 6116 | 6090 | return stringToEnum(std.zig.RcIncludes, arg) orelse |
| 6117 | 6091 | fatal("unsupported rc includes type: {q}", .{arg}); |
| ... | ... | @@ -6222,24 +6196,26 @@ fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool { |
| 6222 | 6196 | return false; |
| 6223 | 6197 | } |
| 6224 | 6198 | |
| 6225 | fn addLibDirectoryWarn(io: Io, lib_directories: *std.ArrayList(Directory), path: []const u8) void { | |
| 6226 | return addLibDirectoryWarn2(io, lib_directories, path, false); | |
| 6199 | fn appendLibDirOrWarn(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void { | |
| 6200 | if (Io.Dir.cwd().openDir(io, path, .{})) |handle| { | |
| 6201 | dirs.appendAssumeCapacity(.{ | |
| 6202 | .path = path, | |
| 6203 | .handle = handle, | |
| 6204 | }); | |
| 6205 | } else |err| { | |
| 6206 | warn("unable to open library directory {q}: {t}", .{ path, err }); | |
| 6207 | } | |
| 6227 | 6208 | } |
| 6228 | ||
| 6229 | fn addLibDirectoryWarn2( | |
| 6230 | io: Io, | |
| 6231 | lib_directories: *std.ArrayList(Directory), | |
| 6232 | path: []const u8, | |
| 6233 | ignore_not_found: bool, | |
| 6234 | ) void { | |
| 6235 | lib_directories.appendAssumeCapacity(.{ | |
| 6236 | .handle = Io.Dir.cwd().openDir(io, path, .{}) catch |err| { | |
| 6237 | if (err == error.FileNotFound and ignore_not_found) return; | |
| 6238 | warn("unable to open library directory {q}: {t}", .{ path, err }); | |
| 6239 | return; | |
| 6240 | }, | |
| 6241 | .path = path, | |
| 6242 | }); | |
| 6209 | fn appendLibDirOrWarnAllowMissing(io: Io, dirs: *std.ArrayList(Directory), path: []const u8) void { | |
| 6210 | if (Io.Dir.cwd().openDir(io, path, .{})) |handle| { | |
| 6211 | dirs.appendAssumeCapacity(.{ | |
| 6212 | .path = path, | |
| 6213 | .handle = handle, | |
| 6214 | }); | |
| 6215 | } else |err| switch (err) { | |
| 6216 | error.FileNotFound => {}, // ignore | |
| 6217 | else => |e| warn("unable to open library directory {q}: {t}", .{ path, e }), | |
| 6218 | } | |
| 6243 | 6219 | } |
| 6244 | 6220 | |
| 6245 | 6221 | const IoImpl = switch (build_options.io_mode) { |
src/target.zig+2-2| ... | ... | @@ -299,7 +299,7 @@ pub fn preferNewLinkerOverLld(target: *const std.Target) bool { |
| 299 | 299 | /// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful. |
| 300 | 300 | pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool { |
| 301 | 301 | return switch (ofmt) { |
| 302 | .elf => true, | |
| 302 | .elf, .macho => true, | |
| 303 | 303 | else => false, |
| 304 | 304 | }; |
| 305 | 305 | } |
| ... | ... | @@ -510,7 +510,7 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 { |
| 510 | 510 | }, |
| 511 | 511 | // On SerenityOS libc includes libm, libpthread, libdl, and libssp. |
| 512 | 512 | .serenity => &.{"-lc"}, |
| 513 | else => &.{}, | |
| 513 | else => if (target.os.tag.isDarwin()) &.{"-lSystem"} else &.{}, | |
| 514 | 514 | }; |
| 515 | 515 | return result; |
| 516 | 516 | } |