macho: handle mismatched and missing platform errors

This commit is contained in:
Jakub Konka 2023-08-29 15:27:44 +02:00
parent 1cae41bbbb
commit 79b3285aa2
6 changed files with 290 additions and 204 deletions

View File

@ -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");

View File

@ -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;

View File

@ -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{

View File

@ -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 {

View File

@ -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;

View File

@ -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,