aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-08-29 15:27:44 +0200
committerJakub Konka <kubkon@jakubkonka.com>2023-08-29 15:27:44 +0200
commit79b3285aa216350e0c2ff18436a169af69e4570f (patch)
treed4529cb6e9e0c39db171612bc95aa9d8371be98f /src
parent1cae41bbbb3cb1cf10bfa808ecbe289bfb4d5180 (diff)
downloadzig-79b3285aa216350e0c2ff18436a169af69e4570f.tar.gz
zig-79b3285aa216350e0c2ff18436a169af69e4570f.zip
macho: handle mismatched and missing platform errors
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig281
-rw-r--r--src/link/MachO/Dylib.zig102
-rw-r--r--src/link/MachO/Object.zig2
-rw-r--r--src/link/MachO/load_commands.zig32
-rw-r--r--src/link/MachO/zld.zig53
-rw-r--r--src/link/tapi.zig24
6 files changed, 290 insertions, 204 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index f06466b41f..127e9f7027 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -396,16 +396,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
self.dylibs_map.clearRetainingCapacity();
self.referenced_dylibs.clearRetainingCapacity();
- const cpu_arch = self.base.options.target.cpu.arch;
var dependent_libs = std.fifo.LinearFifo(struct {
id: Dylib.Id,
parent: u16,
}, .Dynamic).init(arena);
- var parse_error_ctx: union {
- none: void,
+ var parse_error_ctx: struct {
detected_arch: std.Target.Cpu.Arch,
- } = .{ .none = {} };
+ detected_platform: ?Platform,
+ detected_stub_targets: []const []const u8,
+ } = .{
+ .detected_arch = undefined,
+ .detected_platform = null,
+ .detected_stub_targets = &[0][]const u8{},
+ };
+ defer {
+ for (parse_error_ctx.detected_stub_targets) |target| self.base.allocator.free(target);
+ self.base.allocator.free(parse_error_ctx.detected_stub_targets);
+ }
for (libs.keys(), libs.values()) |path, lib| {
const in_file = try std.fs.cwd().openFile(path, .{});
@@ -418,25 +426,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
false,
&dependent_libs,
&parse_error_ctx,
- ) catch |err| switch (err) {
- error.DylibAlreadyExists => {},
- error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
- error.MissingArchFatLib => try self.reportParseError(
- path,
- "missing architecture in universal file, expected '{s}'",
- .{@tagName(cpu_arch)},
- ),
- error.InvalidArch => try self.reportParseError(
- path,
- "invalid architecture '{s}', expected '{s}'",
- .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
- ),
- else => |e| try self.reportParseError(
- path,
- "parsing library failed with error '{s}'",
- .{@errorName(e)},
- ),
- };
+ ) catch |err| try self.handleAndReportParseError(path, err, parse_error_ctx);
}
self.parseDependentLibs(&dependent_libs, &parse_error_ctx) catch |err| {
@@ -586,7 +576,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
.version = 0,
});
{
- const platform = load_commands.Platform.fromOptions(&self.base.options);
+ const platform = Platform.fromTarget(self.base.options.target);
const sdk_version: ?std.SemanticVersion = if (self.base.options.sysroot) |path|
load_commands.inferSdkVersionFromSdkPath(path)
else
@@ -738,7 +728,8 @@ fn resolveLib(
const ParseError = error{
UnknownFileType,
MissingArchFatLib,
- InvalidArch,
+ InvalidTarget,
+ InvalidLibStubTargets,
DylibAlreadyExists,
IncompatibleDylibVersion,
OutOfMemory,
@@ -798,19 +789,24 @@ fn parseObject(
};
errdefer object.deinit(gpa);
try object.parse(gpa);
- try self.objects.append(gpa, object);
const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
macho.CPU_TYPE_ARM64 => .aarch64,
macho.CPU_TYPE_X86_64 => .x86_64,
else => unreachable,
};
- const self_cpu_arch = self.base.options.target.cpu.arch;
+ error_ctx.detected_arch = cpu_arch;
- if (self_cpu_arch != cpu_arch) {
- error_ctx.detected_arch = cpu_arch;
- return error.InvalidArch;
+ if (object.getPlatform()) |platform| {
+ error_ctx.detected_platform = platform;
+ }
+
+ if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
+ if (error_ctx.detected_platform) |platform| {
+ if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
}
+
+ try self.objects.append(gpa, object);
}
pub fn parseLibrary(
@@ -825,14 +821,12 @@ pub fn parseLibrary(
const tracy = trace(@src());
defer tracy.end();
- const cpu_arch = self.base.options.target.cpu.arch;
-
if (fat.isFatLibrary(file)) {
- const offset = try self.parseFatLibrary(file, cpu_arch);
+ const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch);
try file.seekTo(offset);
if (Archive.isArchive(file, offset)) {
- try self.parseArchive(path, offset, must_link, cpu_arch, error_ctx);
+ try self.parseArchive(path, offset, must_link, error_ctx);
} else if (Dylib.isDylib(file, offset)) {
try self.parseDylib(file, path, offset, dependent_libs, .{
.needed = lib.needed,
@@ -840,7 +834,7 @@ pub fn parseLibrary(
}, error_ctx);
} else return error.UnknownFileType;
} else if (Archive.isArchive(file, 0)) {
- try self.parseArchive(path, 0, must_link, cpu_arch, error_ctx);
+ try self.parseArchive(path, 0, must_link, error_ctx);
} else if (Dylib.isDylib(file, 0)) {
try self.parseDylib(file, path, 0, dependent_libs, .{
.needed = lib.needed,
@@ -850,7 +844,7 @@ pub fn parseLibrary(
self.parseLibStub(file, path, dependent_libs, .{
.needed = lib.needed,
.weak = lib.weak,
- }) catch |err| switch (err) {
+ }, error_ctx) catch |err| switch (err) {
error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType,
else => |e| return e,
};
@@ -872,7 +866,6 @@ fn parseArchive(
path: []const u8,
fat_offset: u64,
must_link: bool,
- cpu_arch: std.Target.Cpu.Arch,
error_ctx: anytype,
) ParseError!void {
const gpa = self.base.allocator;
@@ -899,14 +892,20 @@ fn parseArchive(
var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only!
defer object.deinit(gpa);
- const parsed_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
+ const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
macho.CPU_TYPE_ARM64 => .aarch64,
macho.CPU_TYPE_X86_64 => .x86_64,
else => unreachable,
};
- if (cpu_arch != parsed_cpu_arch) {
- error_ctx.detected_arch = parsed_cpu_arch;
- return error.InvalidArch;
+ error_ctx.detected_arch = cpu_arch;
+
+ if (object.getPlatform()) |platform| {
+ error_ctx.detected_platform = platform;
+ }
+
+ if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
+ if (error_ctx.detected_platform) |platform| {
+ if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
}
}
@@ -945,8 +944,6 @@ fn parseDylib(
error_ctx: anytype,
) ParseError!void {
const gpa = self.base.allocator;
- const self_cpu_arch = self.base.options.target.cpu.arch;
-
const file_stat = try file.stat();
const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow;
@@ -969,12 +966,16 @@ fn parseDylib(
macho.CPU_TYPE_X86_64 => .x86_64,
else => unreachable,
};
- if (self_cpu_arch != cpu_arch) {
- error_ctx.detected_arch = cpu_arch;
- return error.InvalidArch;
+ error_ctx.detected_arch = cpu_arch;
+
+ if (dylib.getPlatform(contents)) |platform| {
+ error_ctx.detected_platform = platform;
}
- // TODO verify platform
+ if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
+ if (error_ctx.detected_platform) |platform| {
+ if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
+ }
try self.addDylib(dylib, .{
.needed = dylib_options.needed,
@@ -988,6 +989,7 @@ fn parseLibStub(
path: []const u8,
dependent_libs: anytype,
dylib_options: DylibOpts,
+ error_ctx: anytype,
) ParseError!void {
const gpa = self.base.allocator;
var lib_stub = try LibStub.loadFromFile(gpa, file);
@@ -995,7 +997,20 @@ fn parseLibStub(
if (lib_stub.inner.len == 0) return error.NotLibStub;
- // TODO verify platform
+ // Verify target
+ {
+ var matcher = try Dylib.TargetMatcher.init(gpa, self.base.options.target);
+ defer matcher.deinit();
+
+ const first_tbd = lib_stub.inner[0];
+ const targets = try first_tbd.targets(gpa);
+ if (!matcher.matchesTarget(targets)) {
+ error_ctx.detected_stub_targets = targets;
+ return error.InvalidLibStubTargets;
+ }
+ for (targets) |t| gpa.free(t);
+ gpa.free(targets);
+ }
var dylib = Dylib{ .weak = dylib_options.weak };
errdefer dylib.deinit(gpa);
@@ -1104,7 +1119,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, error_ctx: anyt
self.parseLibStub(file, full_path, dependent_libs, .{
.dependent = true,
.weak = weak,
- }) catch |err| switch (err) {
+ }, error_ctx) catch |err| switch (err) {
error.NotLibStub, error.UnexpectedToken => continue,
else => |e| return e,
};
@@ -4830,6 +4845,53 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
}
+pub fn handleAndReportParseError(self: *MachO, path: []const u8, err: ParseError, parse_error_ctx: anytype) !void {
+ const cpu_arch = self.base.options.target.cpu.arch;
+ switch (err) {
+ error.DylibAlreadyExists => {},
+ error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
+ error.MissingArchFatLib => try self.reportParseError(
+ path,
+ "missing architecture in universal file, expected '{s}'",
+ .{@tagName(cpu_arch)},
+ ),
+ error.InvalidTarget => if (parse_error_ctx.detected_platform) |platform| {
+ try self.reportParseError(path, "invalid target '{s}-{}', expected '{s}-{}'", .{
+ @tagName(parse_error_ctx.detected_arch),
+ platform.fmtTarget(),
+ @tagName(cpu_arch),
+ Platform.fromTarget(self.base.options.target).fmtTarget(),
+ });
+ } else {
+ try self.reportParseError(
+ path,
+ "invalid architecture '{s}', expected '{s}'",
+ .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
+ );
+ },
+ error.InvalidLibStubTargets => {
+ var targets_string = std.ArrayList(u8).init(self.base.allocator);
+ defer targets_string.deinit();
+ try targets_string.writer().writeAll("(");
+ for (parse_error_ctx.detected_stub_targets) |t| {
+ try targets_string.writer().print("{s}, ", .{t});
+ }
+ try targets_string.resize(targets_string.items.len - 2);
+ try targets_string.writer().writeAll(")");
+ try self.reportParseError(path, "invalid targets '{s}', expected '{s}-{}'", .{
+ targets_string.items,
+ @tagName(cpu_arch),
+ Platform.fromTarget(self.base.options.target).fmtTarget(),
+ });
+ },
+ else => |e| try self.reportParseError(
+ path,
+ "parsing positional argument failed with error '{s}'",
+ .{@errorName(e)},
+ ),
+ }
+}
+
pub fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void {
const gpa = self.base.allocator;
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
@@ -5140,66 +5202,6 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
}
}
-const MachO = @This();
-
-const std = @import("std");
-const build_options = @import("build_options");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const dwarf = std.dwarf;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const meta = std.meta;
-
-const aarch64 = @import("../arch/aarch64/bits.zig");
-const calcUuid = @import("MachO/uuid.zig").calcUuid;
-const codegen = @import("../codegen.zig");
-const dead_strip = @import("MachO/dead_strip.zig");
-const fat = @import("MachO/fat.zig");
-const link = @import("../link.zig");
-const llvm_backend = @import("../codegen/llvm.zig");
-const load_commands = @import("MachO/load_commands.zig");
-const stubs = @import("MachO/stubs.zig");
-const tapi = @import("tapi.zig");
-const target_util = @import("../target.zig");
-const thunks = @import("MachO/thunks.zig");
-const trace = @import("../tracy.zig").trace;
-const zld = @import("MachO/zld.zig");
-
-const Air = @import("../Air.zig");
-const Allocator = mem.Allocator;
-const Archive = @import("MachO/Archive.zig");
-pub const Atom = @import("MachO/Atom.zig");
-const Cache = std.Build.Cache;
-const CodeSignature = @import("MachO/CodeSignature.zig");
-const Compilation = @import("../Compilation.zig");
-const Dwarf = File.Dwarf;
-const DwarfInfo = @import("MachO/DwarfInfo.zig");
-const Dylib = @import("MachO/Dylib.zig");
-const File = link.File;
-const Object = @import("MachO/Object.zig");
-const LibStub = tapi.LibStub;
-const Liveness = @import("../Liveness.zig");
-const LlvmObject = @import("../codegen/llvm.zig").Object;
-const Md5 = std.crypto.hash.Md5;
-const Module = @import("../Module.zig");
-const InternPool = @import("../InternPool.zig");
-const Relocation = @import("MachO/Relocation.zig");
-const StringTable = @import("strtab.zig").StringTable;
-const TableSection = @import("table_section.zig").TableSection;
-const Trie = @import("MachO/Trie.zig");
-const Type = @import("../type.zig").Type;
-const TypedValue = @import("../TypedValue.zig");
-const Value = @import("../value.zig").Value;
-
-pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
-pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
-pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
-pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
-
pub const base_tag: File.Tag = File.Tag.macho;
pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
@@ -5332,3 +5334,64 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;
/// the table of load commands. This should be plenty for any
/// potential future extensions.
pub const default_headerpad_size: u32 = 0x1000;
+
+const MachO = @This();
+
+const std = @import("std");
+const build_options = @import("build_options");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const dwarf = std.dwarf;
+const fs = std.fs;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const meta = std.meta;
+
+const aarch64 = @import("../arch/aarch64/bits.zig");
+const calcUuid = @import("MachO/uuid.zig").calcUuid;
+const codegen = @import("../codegen.zig");
+const dead_strip = @import("MachO/dead_strip.zig");
+const fat = @import("MachO/fat.zig");
+const link = @import("../link.zig");
+const llvm_backend = @import("../codegen/llvm.zig");
+const load_commands = @import("MachO/load_commands.zig");
+const stubs = @import("MachO/stubs.zig");
+const tapi = @import("tapi.zig");
+const target_util = @import("../target.zig");
+const thunks = @import("MachO/thunks.zig");
+const trace = @import("../tracy.zig").trace;
+const zld = @import("MachO/zld.zig");
+
+const Air = @import("../Air.zig");
+const Allocator = mem.Allocator;
+const Archive = @import("MachO/Archive.zig");
+pub const Atom = @import("MachO/Atom.zig");
+const Cache = std.Build.Cache;
+const CodeSignature = @import("MachO/CodeSignature.zig");
+const Compilation = @import("../Compilation.zig");
+const Dwarf = File.Dwarf;
+const DwarfInfo = @import("MachO/DwarfInfo.zig");
+const Dylib = @import("MachO/Dylib.zig");
+const File = link.File;
+const Object = @import("MachO/Object.zig");
+const LibStub = tapi.LibStub;
+const Liveness = @import("../Liveness.zig");
+const LlvmObject = @import("../codegen/llvm.zig").Object;
+const Md5 = std.crypto.hash.Md5;
+const Module = @import("../Module.zig");
+const InternPool = @import("../InternPool.zig");
+const Platform = load_commands.Platform;
+const Relocation = @import("MachO/Relocation.zig");
+const StringTable = @import("strtab.zig").StringTable;
+const TableSection = @import("table_section.zig").TableSection;
+const Trie = @import("MachO/Trie.zig");
+const Type = @import("../type.zig").Type;
+const TypedValue = @import("../TypedValue.zig");
+const Value = @import("../value.zig").Value;
+
+pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
+pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
+pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
+pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index 6dd7b6ae96..581f804f13 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -178,6 +178,26 @@ pub fn parseFromBinary(
}
}
+/// Returns Platform composed from the first encountered build version type load command:
+/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
+pub fn getPlatform(self: Dylib, data: []align(@alignOf(u64)) const u8) ?Platform {
+ var it = LoadCommandIterator{
+ .ncmds = self.header.?.ncmds,
+ .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
+ };
+ while (it.next()) |cmd| {
+ switch (cmd.cmd()) {
+ .BUILD_VERSION,
+ .VERSION_MIN_MACOSX,
+ .VERSION_MIN_IPHONEOS,
+ .VERSION_MIN_TVOS,
+ .VERSION_MIN_WATCHOS,
+ => return Platform.fromLoadCommand(cmd),
+ else => {},
+ }
+ } else return null;
+}
+
fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
const expanded = &[_][]const u8{
try std.fmt.allocPrint(allocator, "_OBJC_CLASS_$_{s}", .{sym_name}),
@@ -212,27 +232,27 @@ fn addWeakSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void
try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);
}
-const TargetMatcher = struct {
+pub const TargetMatcher = struct {
allocator: Allocator,
- target: CrossTarget,
+ cpu_arch: std.Target.Cpu.Arch,
+ os_tag: std.Target.Os.Tag,
+ abi: std.Target.Abi,
target_strings: std.ArrayListUnmanaged([]const u8) = .{},
- pub fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
+ pub fn init(allocator: Allocator, target: std.Target) !TargetMatcher {
var self = TargetMatcher{
.allocator = allocator,
- .target = target,
+ .cpu_arch = target.cpu.arch,
+ .os_tag = target.os.tag,
+ .abi = target.abi,
};
- const apple_string = try targetToAppleString(allocator, target);
+ const apple_string = try toAppleTargetTriple(allocator, self.cpu_arch, self.os_tag, self.abi);
try self.target_strings.append(allocator, apple_string);
- const abi = target.abi orelse .none;
- if (abi == .simulator) {
+ if (self.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, .{
- .cpu_arch = target.cpu_arch.?,
- .os_tag = .macos,
- });
+ const host_target = try toAppleTargetTriple(allocator, self.cpu_arch, .macos, .none);
try self.target_strings.append(allocator, host_target);
}
@@ -246,7 +266,7 @@ const TargetMatcher = struct {
self.target_strings.deinit(self.allocator);
}
- inline fn cpuArchToAppleString(cpu_arch: std.Target.Cpu.Arch) []const u8 {
+ inline fn fmtCpuArch(cpu_arch: std.Target.Cpu.Arch) []const u8 {
return switch (cpu_arch) {
.aarch64 => "arm64",
.x86_64 => "x86_64",
@@ -254,7 +274,7 @@ const TargetMatcher = struct {
};
}
- inline fn abiToAppleString(abi: std.Target.Abi) ?[]const u8 {
+ inline fn fmtAbi(abi: std.Target.Abi) ?[]const u8 {
return switch (abi) {
.none => null,
.simulator => "simulator",
@@ -263,14 +283,18 @@ const TargetMatcher = struct {
};
}
- pub fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {
- const cpu_arch = cpuArchToAppleString(target.cpu_arch.?);
- const os_tag = @tagName(target.os_tag.?);
- const target_abi = abiToAppleString(target.abi orelse .none);
- if (target_abi) |abi| {
- return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch, os_tag, abi });
+ pub fn toAppleTargetTriple(
+ allocator: Allocator,
+ cpu_arch: std.Target.Cpu.Arch,
+ os_tag: std.Target.Os.Tag,
+ abi: std.Target.Abi,
+ ) ![]const u8 {
+ const cpu_arch_s = fmtCpuArch(cpu_arch);
+ const os_tag_s = @tagName(os_tag);
+ if (fmtAbi(abi)) |abi_s| {
+ return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch_s, os_tag_s, abi_s });
}
- return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch, os_tag });
+ return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch_s, os_tag_s });
}
fn hasValue(stack: []const []const u8, needle: []const u8) bool {
@@ -280,7 +304,7 @@ const TargetMatcher = struct {
return false;
}
- fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
+ pub fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
for (self.target_strings.items) |t| {
if (hasValue(targets, t)) return true;
}
@@ -288,26 +312,7 @@ const TargetMatcher = struct {
}
fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {
- return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));
- }
-
- pub fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
- var arena = std.heap.ArenaAllocator.init(self.allocator);
- defer arena.deinit();
-
- const targets = switch (tbd) {
- .v3 => |v3| blk: {
- var targets = std.ArrayList([]const u8).init(arena.allocator());
- for (v3.archs) |arch| {
- const target = try std.fmt.allocPrint(arena.allocator(), "{s}-{s}", .{ arch, v3.platform });
- try targets.append(target);
- }
- break :blk targets.items;
- },
- .v4 => |v4| v4.targets,
- };
-
- return self.matchesTarget(targets);
+ return hasValue(archs, fmtCpuArch(self.cpu_arch));
}
};
@@ -342,15 +347,16 @@ pub fn parseFromStub(
log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
- var matcher = try TargetMatcher.init(allocator, .{
- .cpu_arch = target.cpu.arch,
- .os_tag = target.os.tag,
- .abi = target.abi,
- });
+ var matcher = try TargetMatcher.init(allocator, target);
defer matcher.deinit();
for (lib_stub.inner, 0..) |elem, stub_index| {
- if (!(try matcher.matchesTargetTbd(elem))) continue;
+ const targets = try elem.targets(allocator);
+ defer {
+ for (targets) |t| allocator.free(t);
+ allocator.free(targets);
+ }
+ if (!matcher.matchesTarget(targets)) continue;
if (stub_index > 0) {
// TODO I thought that we could switch on presence of `parent-umbrella` map;
@@ -541,8 +547,8 @@ const fat = @import("fat.zig");
const tapi = @import("../tapi.zig");
const Allocator = mem.Allocator;
-const CrossTarget = std.zig.CrossTarget;
const LibStub = tapi.LibStub;
const LoadCommandIterator = macho.LoadCommandIterator;
const MachO = @import("../MachO.zig");
+const Platform = @import("load_commands.zig").Platform;
const Tbd = tapi.Tbd;
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 92f2899d8b..43c87cf092 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -940,7 +940,7 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
return di;
}
-/// Returns Options.Platform composed from the first encountered build version type load command:
+/// Returns Platform composed from the first encountered build version type load command:
/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
pub fn getPlatform(self: Object) ?Platform {
var it = LoadCommandIterator{
diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig
index 5d3a84c87f..50580d0275 100644
--- a/src/link/MachO/load_commands.zig
+++ b/src/link/MachO/load_commands.zig
@@ -77,7 +77,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
// LC_SOURCE_VERSION
sizeofcmds += @sizeOf(macho.source_version_command);
// LC_BUILD_VERSION or LC_VERSION_MIN_
- if (Platform.fromOptions(options).isBuildVersionCompatible()) {
+ if (Platform.fromTarget(options.target).isBuildVersionCompatible()) {
// LC_BUILD_VERSION
sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
} else {
@@ -353,11 +353,11 @@ pub const Platform = struct {
}
}
- pub fn fromOptions(options: *const link.Options) Platform {
+ pub fn fromTarget(target: std.Target) Platform {
return .{
- .os_tag = options.target.os.tag,
- .abi = options.target.abi,
- .version = options.target.os.version_range.semver.min,
+ .os_tag = target.os.tag,
+ .abi = target.abi,
+ .version = target.os.version_range.semver.min,
};
}
@@ -383,6 +383,28 @@ pub const Platform = struct {
}
return false;
}
+
+ pub fn fmtTarget(plat: Platform) std.fmt.Formatter(formatTarget) {
+ return .{ .data = plat };
+ }
+
+ pub fn formatTarget(
+ plat: Platform,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ try writer.print("{s}", .{@tagName(plat.os_tag)});
+ if (plat.abi != .none) {
+ try writer.print("-{s}", .{@tagName(plat.abi)});
+ }
+ }
+
+ pub fn eqlTarget(plat: Platform, other: Platform) bool {
+ return plat.os_tag == other.os_tag and plat.abi == other.abi;
+ }
};
const SupportedPlatforms = struct {
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index 7c1b870ed5..6dbc361c28 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -347,11 +347,17 @@ pub fn linkWithZld(
var parse_error_ctx: struct {
detected_arch: std.Target.Cpu.Arch,
- detected_os: std.Target.Os.Tag,
+ detected_platform: ?Platform,
+ detected_stub_targets: []const []const u8,
} = .{
.detected_arch = undefined,
- .detected_os = undefined,
+ .detected_platform = null,
+ .detected_stub_targets = &[0][]const u8{},
};
+ defer {
+ for (parse_error_ctx.detected_stub_targets) |t| gpa.free(t);
+ gpa.free(parse_error_ctx.detected_stub_targets);
+ }
for (positionals.items) |obj| {
const in_file = try std.fs.cwd().openFile(obj.path, .{});
@@ -363,25 +369,7 @@ pub fn linkWithZld(
obj.must_link,
&dependent_libs,
&parse_error_ctx,
- ) catch |err| switch (err) {
- error.DylibAlreadyExists => {},
- error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type", .{}),
- error.MissingArchFatLib => try macho_file.reportParseError(
- obj.path,
- "missing architecture in universal file, expected '{s}'",
- .{@tagName(cpu_arch)},
- ),
- error.InvalidArch => try macho_file.reportParseError(
- obj.path,
- "invalid architecture '{s}', expected '{s}'",
- .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
- ),
- else => |e| try macho_file.reportParseError(
- obj.path,
- "parsing positional argument failed with error '{s}'",
- .{@errorName(e)},
- ),
- };
+ ) catch |err| try macho_file.handleAndReportParseError(obj.path, err, parse_error_ctx);
}
for (libs.keys(), libs.values()) |path, lib| {
@@ -395,25 +383,7 @@ pub fn linkWithZld(
false,
&dependent_libs,
&parse_error_ctx,
- ) catch |err| switch (err) {
- error.DylibAlreadyExists => {},
- error.UnknownFileType => try macho_file.reportParseError(path, "unknown file type", .{}),
- error.MissingArchFatLib => try macho_file.reportParseError(
- path,
- "missing architecture in universal file, expected '{s}'",
- .{@tagName(cpu_arch)},
- ),
- error.InvalidArch => try macho_file.reportParseError(
- path,
- "invalid architecture '{s}', expected '{s}'",
- .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
- ),
- else => |e| try macho_file.reportParseError(
- path,
- "parsing library failed with error '{s}'",
- .{@errorName(e)},
- ),
- };
+ ) catch |err| try macho_file.handleAndReportParseError(path, err, parse_error_ctx);
}
macho_file.parseDependentLibs(&dependent_libs, &parse_error_ctx) catch |err| {
@@ -590,7 +560,7 @@ pub fn linkWithZld(
.version = 0,
});
{
- const platform = load_commands.Platform.fromOptions(&macho_file.base.options);
+ const platform = Platform.fromTarget(macho_file.base.options.target);
const sdk_version: ?std.SemanticVersion = if (macho_file.base.options.sysroot) |path|
load_commands.inferSdkVersionFromSdkPath(path)
else
@@ -1252,6 +1222,7 @@ const MachO = @import("../MachO.zig");
const Md5 = std.crypto.hash.Md5;
const LibStub = @import("../tapi.zig").LibStub;
const Object = @import("Object.zig");
+const Platform = load_commands.Platform;
const Section = MachO.Section;
const StringTable = @import("../strtab.zig").StringTable;
const SymbolWithLoc = MachO.SymbolWithLoc;
diff --git a/src/link/tapi.zig b/src/link/tapi.zig
index f9ffd43d62..6fc62e585d 100644
--- a/src/link/tapi.zig
+++ b/src/link/tapi.zig
@@ -81,6 +81,30 @@ pub const Tbd = union(enum) {
v3: TbdV3,
v4: TbdV4,
+ /// Caller owns memory.
+ pub fn targets(self: Tbd, gpa: Allocator) error{OutOfMemory}![]const []const u8 {
+ var out = std.ArrayList([]const u8).init(gpa);
+ defer out.deinit();
+
+ switch (self) {
+ .v3 => |v3| {
+ try out.ensureTotalCapacityPrecise(v3.archs.len);
+ for (v3.archs) |arch| {
+ const target = try std.fmt.allocPrint(gpa, "{s}-{s}", .{ arch, v3.platform });
+ out.appendAssumeCapacity(target);
+ }
+ },
+ .v4 => |v4| {
+ try out.ensureTotalCapacityPrecise(v4.targets.len);
+ for (v4.targets) |t| {
+ out.appendAssumeCapacity(try gpa.dupe(u8, t));
+ }
+ },
+ }
+
+ return out.toOwnedSlice();
+ }
+
pub fn currentVersion(self: Tbd) ?VersionField {
return switch (self) {
.v3 => |v3| v3.current_version,