diff options
| -rw-r--r-- | lib/std/build.zig | 12 | ||||
| -rw-r--r-- | lib/std/macho.zig | 15 | ||||
| -rw-r--r-- | lib/std/zig/system.zig | 10 | ||||
| -rw-r--r-- | lib/std/zig/system/darwin.zig | 44 | ||||
| -rw-r--r-- | lib/std/zig/system/darwin/macos.zig (renamed from lib/std/zig/system/macos.zig) | 22 | ||||
| -rw-r--r-- | src/Compilation.zig | 3 | ||||
| -rw-r--r-- | src/link/MachO.zig | 153 | ||||
| -rw-r--r-- | src/link/MachO/Archive.zig | 13 | ||||
| -rw-r--r-- | src/link/MachO/Dylib.zig | 97 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 13 | ||||
| -rw-r--r-- | src/link/MachO/commands.zig | 9 | ||||
| -rw-r--r-- | src/link/MachO/fat.zig | 10 | ||||
| -rw-r--r-- | src/main.zig | 25 |
13 files changed, 277 insertions, 149 deletions
diff --git a/lib/std/build.zig b/lib/std/build.zig index 17cad016e8..efb305d4a3 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2672,7 +2672,11 @@ pub const LibExeObjStep = struct { try zig_args.append(self.builder.pathFromRoot(include_path)); }, .raw_path_system => |include_path| { - try zig_args.append("-isystem"); + if (builder.sysroot != null) { + try zig_args.append("-iwithsysroot"); + } else { + try zig_args.append("-isystem"); + } try zig_args.append(self.builder.pathFromRoot(include_path)); }, .other_step => |other| if (other.emit_h) { @@ -2700,6 +2704,12 @@ pub const LibExeObjStep = struct { if (self.target.isDarwin()) { for (self.framework_dirs.items) |dir| { + if (builder.sysroot != null) { + try zig_args.append("-iframeworkwithsysroot"); + } else { + try zig_args.append("-iframework"); + } + try zig_args.append(dir); try zig_args.append("-F"); try zig_args.append(dir); } diff --git a/lib/std/macho.zig b/lib/std/macho.zig index cb030e941e..14be755a70 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -116,6 +116,21 @@ pub const build_tool_version = extern struct { version: u32, }; +pub const PLATFORM_MACOS: u32 = 0x1; +pub const PLATFORM_IOS: u32 = 0x2; +pub const PLATFORM_TVOS: u32 = 0x3; +pub const PLATFORM_WATCHOS: u32 = 0x4; +pub const PLATFORM_BRIDGEOS: u32 = 0x5; +pub const PLATFORM_MACCATALYST: u32 = 0x6; +pub const PLATFORM_IOSSIMULATOR: u32 = 0x7; +pub const PLATFORM_TVOSSIMULATOR: u32 = 0x8; +pub const PLATFORM_WATCHOSSIMULATOR: u32 = 0x9; +pub const PLATFORM_DRIVERKIT: u32 = 0x10; + +pub const TOOL_CLANG: u32 = 0x1; +pub const TOOL_SWIFT: u32 = 0x2; +pub const TOOL_LD: u32 = 0x3; + /// The entry_point_command is a replacement for thread_command. /// It is used for main executables to specify the location (file offset) /// of main(). If -stack_size was used at link time, the stacksize diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 4d671efe94..c5c62911cc 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -13,12 +13,10 @@ const assert = std.debug.assert; const process = std.process; const Target = std.Target; const CrossTarget = std.zig.CrossTarget; -const macos = @import("system/macos.zig"); const native_endian = std.Target.current.cpu.arch.endian(); const linux = @import("system/linux.zig"); pub const windows = @import("system/windows.zig"); - -pub const getSDKPath = macos.getSDKPath; +pub const darwin = @import("system/darwin.zig"); pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), @@ -255,7 +253,7 @@ pub const NativeTargetInfo = struct { os.version_range.windows.min = detected_version; os.version_range.windows.max = detected_version; }, - .macos => try macos.detect(&os), + .macos => try darwin.macos.detect(&os), .freebsd, .netbsd, .dragonfly => { const key = switch (Target.current.os.tag) { .freebsd => "kern.osreldate", @@ -972,7 +970,7 @@ pub const NativeTargetInfo = struct { switch (std.Target.current.os.tag) { .linux => return linux.detectNativeCpuAndFeatures(), - .macos => return macos.detectNativeCpuAndFeatures(), + .macos => return darwin.macos.detectNativeCpuAndFeatures(), else => {}, } @@ -983,6 +981,6 @@ pub const NativeTargetInfo = struct { }; test { - _ = @import("system/macos.zig"); + _ = @import("system/darwin.zig"); _ = @import("system/linux.zig"); } diff --git a/lib/std/zig/system/darwin.zig b/lib/std/zig/system/darwin.zig new file mode 100644 index 0000000000..1e8d9e4b48 --- /dev/null +++ b/lib/std/zig/system/darwin.zig @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("std"); +const mem = std.mem; +const Allocator = mem.Allocator; +const Target = std.Target; + +pub const macos = @import("darwin/macos.zig"); + +/// Detect SDK path on Darwin. +/// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which result can be used to specify +/// `--sysroot` of the compiler. +/// The caller needs to free the resulting path slice. +pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 { + const is_simulator_abi = target.abi == .simulator; + const sdk = switch (target.os.tag) { + .macos => "macosx", + .ios => if (is_simulator_abi) "iphonesimulator" else "iphoneos", + .watchos => if (is_simulator_abi) "watchsimulator" else "watchos", + .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", + else => return null, + }; + + const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; + const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); + defer { + allocator.free(result.stderr); + allocator.free(result.stdout); + } + if (result.stderr.len != 0 or result.term.Exited != 0) { + // We don't actually care if there were errors as this is best-effort check anyhow + // and in the worst case the user can specify the sysroot manually. + return null; + } + const sysroot = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")); + return sysroot; +} + +test "" { + _ = @import("darwin/macos.zig"); +} diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/darwin/macos.zig index ae450ecae5..c8f48800c5 100644 --- a/lib/std/zig/system/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -408,28 +408,6 @@ fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) try testing.expectEqualStrings(s_expected, s_got); } -/// Detect SDK path on Darwin. -/// Calls `xcrun --show-sdk-path` which result can be used to specify -/// `-syslibroot` param of the linker. -/// The caller needs to free the resulting path slice. -pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { - assert(Target.current.isDarwin()); - const argv = &[_][]const u8{ "xcrun", "--show-sdk-path" }; - const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.stderr.len != 0) { - std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr}); - } - if (result.term.Exited != 0) { - return error.ProcessTerminated; - } - const syslibroot = mem.trimRight(u8, result.stdout, "\r\n"); - return mem.dupe(allocator, u8, syslibroot); -} - pub fn detectNativeCpuAndFeatures() ?Target.Cpu { var cpu_family: os.CPUFAMILY = undefined; var len: usize = @sizeOf(os.CPUFAMILY); diff --git a/src/Compilation.zig b/src/Compilation.zig index 24e2d9e67f..7987ed95df 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -944,8 +944,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { if (options.sysroot) |sysroot| { break :blk sysroot; } else if (darwin_can_use_system_sdk) { - const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; - break :blk if (at_least_big_sur) try std.zig.system.getSDKPath(arena) else null; + break :blk try std.zig.system.darwin.getSDKPath(arena, options.target); } else { break :blk null; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5d9b0e6fe1..d23b696cb8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -82,8 +82,8 @@ data_in_code_cmd_index: ?u16 = null, function_starts_cmd_index: ?u16 = null, main_cmd_index: ?u16 = null, dylib_id_cmd_index: ?u16 = null, -version_min_cmd_index: ?u16 = null, source_version_cmd_index: ?u16 = null, +build_version_cmd_index: ?u16 = null, uuid_cmd_index: ?u16 = null, code_signature_cmd_index: ?u16 = null, /// Path to libSystem @@ -794,15 +794,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { } } - // If we're compiling native and we can find libSystem.B.{dylib, tbd}, - // we link against that instead of embedded libSystem.B.tbd file. - var native_libsystem_available = false; - if (self.base.options.is_native_os) blk: { + // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}. + var libsystem_available = false; + if (self.base.options.sysroot != null) blk: { // Try stub file first. If we hit it, then we're done as the stub file // re-exports every single symbol definition. if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { try libs.append(full_path); - native_libsystem_available = true; + libsystem_available = true; break :blk; } // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib @@ -811,12 +810,12 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { try libs.append(libsystem_path); try libs.append(libc_path); - native_libsystem_available = true; + libsystem_available = true; break :blk; } } } - if (!native_libsystem_available) { + if (!libsystem_available) { const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin", "libSystem.B.tbd", }); @@ -841,7 +840,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { break; } } else { - log.warn("framework not found for '-f{s}'", .{framework}); + log.warn("framework not found for '-framework {s}'", .{framework}); framework_not_found = true; } } @@ -901,10 +900,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { try argv.append("-o"); try argv.append(full_out_path); - if (native_libsystem_available) { - try argv.append("-lSystem"); - try argv.append("-lc"); - } + try argv.append("-lSystem"); + try argv.append("-lc"); for (search_lib_names.items) |l_name| { try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); @@ -914,6 +911,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); } + for (self.base.options.frameworks) |framework| { + try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework})); + } + + for (self.base.options.framework_dirs) |framework_dir| { + try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir})); + } + Compilation.dump_argv(argv.items); } @@ -990,7 +995,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { } fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { - const arch = self.base.options.target.cpu.arch; for (files) |file_name| { const full_path = full_path: { var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; @@ -999,17 +1003,17 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const }; defer self.base.allocator.free(full_path); - if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| { + if (try Object.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |object| { try self.objects.append(self.base.allocator, object); continue; } - if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| { + if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |archive| { try self.archives.append(self.base.allocator, archive); continue; } - if (try Dylib.createAndParseFromPath(self.base.allocator, arch, full_path, .{ + if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path, .{ .syslibroot = syslibroot, })) |dylibs| { defer self.base.allocator.free(dylibs); @@ -1027,9 +1031,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const } fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { - const arch = self.base.options.target.cpu.arch; for (libs) |lib| { - if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{ + if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, lib, .{ .syslibroot = syslibroot, })) |dylibs| { defer self.base.allocator.free(dylibs); @@ -1042,7 +1045,7 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v continue; } - if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| { + if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, lib)) |archive| { try self.archives.append(self.base.allocator, archive); continue; } @@ -2231,11 +2234,7 @@ fn resolveSymbols(self: *MachO) !void { const object_id = @intCast(u16, self.objects.items.len); const object = try self.objects.addOne(self.base.allocator); - object.* = try archive.parseObject( - self.base.allocator, - self.base.options.target.cpu.arch, - offsets.items[0], - ); + object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]); try self.resolveSymbolsInObject(object_id); continue :loop; @@ -2717,27 +2716,6 @@ fn populateMetadata(self: *MachO) !void { try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); } - if (self.version_min_cmd_index == null) { - self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); - const cmd: u32 = switch (self.base.options.target.os.tag) { - .macos => macho.LC_VERSION_MIN_MACOSX, - .ios => macho.LC_VERSION_MIN_IPHONEOS, - .tvos => macho.LC_VERSION_MIN_TVOS, - .watchos => macho.LC_VERSION_MIN_WATCHOS, - else => unreachable, // wrong OS - }; - const ver = self.base.options.target.os.version_range.semver.min; - const version = ver.major << 16 | ver.minor << 8 | ver.patch; - try self.load_commands.append(self.base.allocator, .{ - .VersionMin = .{ - .cmd = cmd, - .cmdsize = @sizeOf(macho.version_min_command), - .version = version, - .sdk = version, - }, - }); - } - if (self.source_version_cmd_index == null) { self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ @@ -2749,6 +2727,40 @@ fn populateMetadata(self: *MachO) !void { }); } + if (self.build_version_cmd_index == null) { + self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len); + const cmdsize = @intCast(u32, mem.alignForwardGeneric( + u64, + @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version), + @sizeOf(u64), + )); + const ver = self.base.options.target.os.version_range.semver.min; + const version = ver.major << 16 | ver.minor << 8 | ver.patch; + const is_simulator_abi = self.base.options.target.abi == .simulator; + var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ + .cmd = macho.LC_BUILD_VERSION, + .cmdsize = cmdsize, + .platform = switch (self.base.options.target.os.tag) { + .macos => macho.PLATFORM_MACOS, + .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS, + .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS, + .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS, + else => unreachable, + }, + .minos = version, + .sdk = version, + .ntools = 1, + }); + const ld_ver = macho.build_tool_version{ + .tool = macho.TOOL_LD, + .version = 0x0, + }; + cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command)); + mem.set(u8, cmd.data, 0); + mem.copy(u8, cmd.data, mem.asBytes(&ld_ver)); + try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd }); + } + if (self.uuid_cmd_index == null) { self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); var uuid_cmd: macho.uuid_command = .{ @@ -2880,11 +2892,6 @@ fn flushZld(self: *MachO) !void { if (self.base.options.target.cpu.arch == .aarch64) { try self.writeCodeSignature(); } - - // if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { - // const out_path = self.output.?.path; - // try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); - // } } fn writeGotEntries(self: *MachO) !void { @@ -4340,26 +4347,38 @@ pub fn populateMissingMetadata(self: *MachO) !void { }); self.load_commands_dirty = true; } - if (self.version_min_cmd_index == null) { - self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); - const cmd: u32 = switch (self.base.options.target.os.tag) { - .macos => macho.LC_VERSION_MIN_MACOSX, - .ios => macho.LC_VERSION_MIN_IPHONEOS, - .tvos => macho.LC_VERSION_MIN_TVOS, - .watchos => macho.LC_VERSION_MIN_WATCHOS, - else => unreachable, // wrong OS - }; + if (self.build_version_cmd_index == null) { + self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len); + const cmdsize = @intCast(u32, mem.alignForwardGeneric( + u64, + @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version), + @sizeOf(u64), + )); const ver = self.base.options.target.os.version_range.semver.min; const version = ver.major << 16 | ver.minor << 8 | ver.patch; - try self.load_commands.append(self.base.allocator, .{ - .VersionMin = .{ - .cmd = cmd, - .cmdsize = @sizeOf(macho.version_min_command), - .version = version, - .sdk = version, + const is_simulator_abi = self.base.options.target.abi == .simulator; + var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ + .cmd = macho.LC_BUILD_VERSION, + .cmdsize = cmdsize, + .platform = switch (self.base.options.target.os.tag) { + .macos => macho.PLATFORM_MACOS, + .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS, + .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS, + .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS, + else => unreachable, }, + .minos = version, + .sdk = version, + .ntools = 1, }); - self.load_commands_dirty = true; + const ld_ver = macho.build_tool_version{ + .tool = macho.TOOL_LD, + .version = 0x0, + }; + cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command)); + mem.set(u8, cmd.data, 0); + mem.copy(u8, cmd.data, mem.asBytes(&ld_ver)); + try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd }); } if (self.source_version_cmd_index == null) { self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 6959dbac89..3b01233e1f 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -9,7 +9,6 @@ const mem = std.mem; const fat = @import("fat.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const Object = @import("Object.zig"); file: fs.File, @@ -104,7 +103,7 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void { allocator.free(self.name); } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Archive { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -119,7 +118,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - archive.parse(allocator, arch) catch |err| switch (err) { + archive.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotArchive => { archive.deinit(allocator); return null; @@ -130,9 +129,9 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return archive; } -pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Archive, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); - self.library_offset = try fat.getLibraryOffset(reader, arch); + self.library_offset = try fat.getLibraryOffset(reader, target); try self.file.seekTo(self.library_offset); const magic = try reader.readBytesNoEof(SARMAG); @@ -215,7 +214,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype) } } -pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32) !Object { +pub fn parseObject(self: Archive, allocator: *Allocator, target: std.Target, offset: u32) !Object { const reader = self.file.reader(); try reader.context.seekTo(offset + self.library_offset); @@ -244,7 +243,7 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32 .mtime = try self.header.?.date(), }; - try object.parse(allocator, arch); + try object.parse(allocator, target); try reader.context.seekTo(0); return object; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 71301ccbbf..64be1fd2fa 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -12,7 +12,6 @@ const fat = @import("fat.zig"); const commands = @import("commands.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LibStub = @import("../tapi.zig").LibStub; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); @@ -139,11 +138,12 @@ pub const Error = error{ pub const CreateOpts = struct { syslibroot: ?[]const u8 = null, id: ?Id = null, + target: ?std.Target = null, }; pub fn createAndParseFromPath( allocator: *Allocator, - arch: Arch, + target: std.Target, path: []const u8, opts: CreateOpts, ) Error!?[]Dylib { @@ -161,7 +161,7 @@ pub fn createAndParseFromPath( .file = file, }; - dylib.parse(allocator, arch) catch |err| switch (err) { + dylib.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -171,7 +171,7 @@ pub fn createAndParseFromPath( }; defer lib_stub.deinit(); - try dylib.parseFromStub(allocator, arch, lib_stub); + try dylib.parseFromStub(allocator, target, lib_stub); }, else => |e| return e, }; @@ -195,7 +195,7 @@ pub fn createAndParseFromPath( try dylibs.append(dylib); // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - try dylib.parseDependentLibs(allocator, arch, &dylibs, opts.syslibroot); + try dylib.parseDependentLibs(allocator, target, &dylibs, opts.syslibroot); return dylibs.toOwnedSlice(); } @@ -222,10 +222,10 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { } } -pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { log.debug("parsing shared library '{s}'", .{self.name}); - self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch); + self.library_offset = try fat.getLibraryOffset(self.file.reader(), target); try self.file.seekTo(self.library_offset); @@ -237,10 +237,10 @@ pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { return error.NotDylib; } - const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); + const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true); - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } @@ -334,7 +334,61 @@ fn addObjCClassSymbols(self: *Dylib, allocator: *Allocator, sym_name: []const u8 } } -pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: LibStub) !void { +fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { + const arch = switch (target.cpu.arch) { + .aarch64 => "arm64", + .x86_64 => "x86_64", + else => unreachable, + }; + const os = @tagName(target.os.tag); + const abi: ?[]const u8 = switch (target.abi) { + .gnu => null, + .simulator => "simulator", + else => unreachable, + }; + if (abi) |x| { + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch, os, x }); + } + return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, os }); +} + +const TargetMatcher = struct { + allocator: *Allocator, + target_strings: std.ArrayListUnmanaged([]const u8) = .{}, + + fn init(allocator: *Allocator, target: std.Target) !TargetMatcher { + var self = TargetMatcher{ .allocator = allocator }; + try self.target_strings.append(allocator, try targetToAppleString(allocator, target)); + + if (target.abi == .simulator) { + // For Apple simulator targets, linking gets tricky as we need to link against the simulator + // hosts dylibs too. + const host_target = try targetToAppleString(allocator, (std.zig.CrossTarget{ + .cpu_arch = target.cpu.arch, + .os_tag = .macos, + }).toTarget()); + try self.target_strings.append(allocator, host_target); + } + + return self; + } + + fn deinit(self: *TargetMatcher) void { + for (self.target_strings.items) |t| { + self.allocator.free(t); + } + self.target_strings.deinit(self.allocator); + } + + fn matches(self: TargetMatcher, targets: []const []const u8) bool { + for (self.target_strings.items) |t| { + if (hasTarget(targets, t)) return true; + } + return false; + } +}; + +pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; log.debug("parsing shared library from stub '{s}'", .{self.name}); @@ -350,17 +404,14 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: } self.id = id; - const target_string: []const u8 = switch (arch) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; + var matcher = try TargetMatcher.init(allocator, target); + defer matcher.deinit(); var umbrella_libs = std.StringHashMap(void).init(allocator); defer umbrella_libs.deinit(); for (lib_stub.inner) |stub, stub_index| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub_index > 0) { // TODO I thought that we could switch on presence of `parent-umbrella` map; @@ -371,7 +422,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: if (stub.exports) |exports| { for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; + if (!matcher.matches(exp.targets)) continue; if (exp.symbols) |symbols| { for (symbols) |sym_name| { @@ -390,7 +441,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: if (stub.reexports) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; if (reexp.symbols) |symbols| { for (symbols) |sym_name| { @@ -418,11 +469,11 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: // TODO track which libs were already parsed in different steps for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub.reexported_libraries) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; for (reexp.libraries) |lib| { if (umbrella_libs.contains(lib)) { @@ -443,7 +494,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: pub fn parseDependentLibs( self: *Dylib, allocator: *Allocator, - arch: Arch, + target: std.Target, out: *std.ArrayList(Dylib), syslibroot: ?[]const u8, ) !void { @@ -475,7 +526,7 @@ pub fn parseDependentLibs( const dylibs = (try createAndParseFromPath( allocator, - arch, + target, full_path, .{ .id = id, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 2e6a20ad4b..c6aa2fb631 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -15,7 +15,6 @@ const segmentName = commands.segmentName; const sectionName = commands.sectionName; const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); const TextBlock = @import("TextBlock.zig"); @@ -154,7 +153,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { } } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Object { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -169,7 +168,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - object.parse(allocator, arch) catch |err| switch (err) { + object.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotObject => { object.deinit(allocator); return null; @@ -180,7 +179,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return object; } -pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); if (self.file_offset) |offset| { try reader.context.seekTo(offset); @@ -195,7 +194,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.NotObject; } - const this_arch: Arch = switch (header.cputype) { + const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => |value| { @@ -203,8 +202,8 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.UnsupportedCpuArchitecture; }, }; - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index f7a2fd3eda..6e75af08c4 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -43,6 +43,7 @@ pub const LoadCommand = union(enum) { Main: macho.entry_point_command, VersionMin: macho.version_min_command, SourceVersion: macho.source_version_command, + BuildVersion: GenericCommandWithData(macho.build_version_command), Uuid: macho.uuid_command, LinkeditData: macho.linkedit_data_command, Rpath: GenericCommandWithData(macho.rpath_command), @@ -97,6 +98,9 @@ pub const LoadCommand = union(enum) { macho.LC_SOURCE_VERSION => LoadCommand{ .SourceVersion = try stream.reader().readStruct(macho.source_version_command), }, + macho.LC_BUILD_VERSION => LoadCommand{ + .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()), + }, macho.LC_UUID => LoadCommand{ .Uuid = try stream.reader().readStruct(macho.uuid_command), }, @@ -129,6 +133,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.write(writer), .Dylib => |x| x.write(writer), .Rpath => |x| x.write(writer), + .BuildVersion => |x| x.write(writer), .Unknown => |x| x.write(writer), }; } @@ -147,6 +152,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmd, .Dylib => |x| x.inner.cmd, .Rpath => |x| x.inner.cmd, + .BuildVersion => |x| x.inner.cmd, .Unknown => |x| x.inner.cmd, }; } @@ -165,6 +171,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmdsize, .Dylib => |x| x.inner.cmdsize, .Rpath => |x| x.inner.cmdsize, + .BuildVersion => |x| x.inner.cmdsize, .Unknown => |x| x.inner.cmdsize, }; } @@ -175,6 +182,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |*x| x.deinit(allocator), .Dylib => |*x| x.deinit(allocator), .Rpath => |*x| x.deinit(allocator), + .BuildVersion => |*x| x.deinit(allocator), .Unknown => |*x| x.deinit(allocator), else => {}, }; @@ -193,6 +201,7 @@ pub const LoadCommand = union(enum) { .Main => |x| meta.eql(x, other.Main), .VersionMin => |x| meta.eql(x, other.VersionMin), .SourceVersion => |x| meta.eql(x, other.SourceVersion), + .BuildVersion => |x| x.eql(other.BuildVersion), .Uuid => |x| meta.eql(x, other.Uuid), .LinkeditData => |x| meta.eql(x, other.LinkeditData), .Segment => |x| x.eql(other.Segment), diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 61592c88a5..c911d89535 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -5,10 +5,8 @@ const macho = std.macho; const mem = std.mem; const native_endian = builtin.target.cpu.arch.endian(); -const Arch = std.Target.Cpu.Arch; - pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { - const arch: Arch = switch (cputype) { + const arch: std.Target.Cpu.Arch = switch (cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => { @@ -31,7 +29,7 @@ fn readFatStruct(reader: anytype, comptime T: type) !T { return res; } -pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { +pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 { const fat_header = try readFatStruct(reader, macho.fat_header); if (fat_header.magic != macho.FAT_MAGIC) return 0; @@ -44,12 +42,12 @@ pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { error.UnsupportedCpuArchitecture => continue, else => |e| return e, }; - if (lib_arch == arch) { + if (lib_arch == target.cpu.arch) { // We have found a matching architecture! return fat_arch.offset; } } else { - log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch}); + log.err("Could not find matching cpu architecture in fat library: expected {s}", .{target.cpu.arch}); return error.MismatchedCpuArchitecture; } } diff --git a/src/main.zig b/src/main.zig index 0987a46989..285b6d2316 100644 --- a/src/main.zig +++ b/src/main.zig @@ -830,7 +830,10 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-D") or mem.eql(u8, arg, "-isystem") or mem.eql(u8, arg, "-I") or - mem.eql(u8, arg, "-dirafter")) + mem.eql(u8, arg, "-dirafter") or + mem.eql(u8, arg, "-iwithsysroot") or + mem.eql(u8, arg, "-iframework") or + mem.eql(u8, arg, "-iframeworkwithsysroot")) { if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; @@ -873,6 +876,8 @@ fn buildOutputType( if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; sysroot = args[i]; + try clang_argv.append("-isysroot"); + try clang_argv.append(args[i]); } else if (mem.eql(u8, arg, "--libc")) { if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; @@ -1673,7 +1678,9 @@ fn buildOutputType( want_native_include_dirs = true; } - if (sysroot == null and cross_target.isNativeOs() and + const is_darwin_on_darwin = (comptime std.Target.current.isDarwin()) and cross_target.isDarwin(); + + if (sysroot == null and (cross_target.isNativeOs() or is_darwin_on_darwin) and (system_libs.items.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { @@ -1684,16 +1691,18 @@ fn buildOutputType( } const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: { - const min = target_info.target.os.getVersionRange().semver.min; - const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); - if (at_least_mojave) { - const sdk_path = try std.zig.system.getSDKPath(arena); + const should_get_sdk_path = if (cross_target.isNativeOs() and target_info.target.os.tag == .macos) inner: { + const min = target_info.target.os.getVersionRange().semver.min; + const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); + break :inner at_least_mojave; + } else true; + if (!should_get_sdk_path) break :outer false; + if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| { try clang_argv.ensureCapacity(clang_argv.items.len + 2); clang_argv.appendAssumeCapacity("-isysroot"); clang_argv.appendAssumeCapacity(sdk_path); break :outer true; - } - break :outer false; + } else break :outer false; } else false; try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); |
