macho: parse platform info from each object file into Platform struct
This commit is contained in:
@@ -585,7 +585,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
try lc_writer.writeStruct(macho.source_version_command{
|
||||
.version = 0,
|
||||
});
|
||||
try load_commands.writeBuildVersionLC(&self.base.options, lc_writer);
|
||||
{
|
||||
const platform = load_commands.Platform.fromOptions(&self.base.options);
|
||||
const sdk_version: ?std.SemanticVersion = self.base.options.darwin_sdk_version orelse blk: {
|
||||
if (self.base.options.sysroot) |path| break :blk load_commands.inferSdkVersionFromSdkPath(path);
|
||||
break :blk null;
|
||||
};
|
||||
if (platform.isBuildVersionCompatible()) {
|
||||
try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer);
|
||||
} else {
|
||||
try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer);
|
||||
}
|
||||
}
|
||||
|
||||
const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
|
||||
try lc_writer.writeStruct(self.uuid_cmd);
|
||||
@@ -797,7 +808,7 @@ fn parseObject(
|
||||
const self_cpu_arch = self.base.options.target.cpu.arch;
|
||||
|
||||
if (self_cpu_arch != cpu_arch) {
|
||||
error_ctx.* = .{ .detected_arch = cpu_arch };
|
||||
error_ctx.detected_arch = cpu_arch;
|
||||
return error.InvalidArch;
|
||||
}
|
||||
}
|
||||
@@ -894,7 +905,7 @@ fn parseArchive(
|
||||
else => unreachable,
|
||||
};
|
||||
if (cpu_arch != parsed_cpu_arch) {
|
||||
error_ctx.* = .{ .detected_arch = parsed_cpu_arch };
|
||||
error_ctx.detected_arch = parsed_cpu_arch;
|
||||
return error.InvalidArch;
|
||||
}
|
||||
}
|
||||
@@ -959,7 +970,7 @@ fn parseDylib(
|
||||
else => unreachable,
|
||||
};
|
||||
if (self_cpu_arch != cpu_arch) {
|
||||
error_ctx.* = .{ .detected_arch = cpu_arch };
|
||||
error_ctx.detected_arch = cpu_arch;
|
||||
return error.InvalidArch;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ const TargetMatcher = struct {
|
||||
target: CrossTarget,
|
||||
target_strings: std.ArrayListUnmanaged([]const u8) = .{},
|
||||
|
||||
fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
|
||||
pub fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
|
||||
var self = TargetMatcher{
|
||||
.allocator = allocator,
|
||||
.target = target,
|
||||
@@ -239,7 +239,7 @@ const TargetMatcher = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
fn deinit(self: *TargetMatcher) void {
|
||||
pub fn deinit(self: *TargetMatcher) void {
|
||||
for (self.target_strings.items) |t| {
|
||||
self.allocator.free(t);
|
||||
}
|
||||
@@ -263,7 +263,7 @@ const TargetMatcher = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {
|
||||
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);
|
||||
@@ -291,7 +291,7 @@ const TargetMatcher = struct {
|
||||
return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));
|
||||
}
|
||||
|
||||
fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
|
||||
pub fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
|
||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
|
||||
@@ -940,6 +940,26 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
|
||||
return di;
|
||||
}
|
||||
|
||||
/// Returns Options.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{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@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;
|
||||
}
|
||||
|
||||
pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
|
||||
const size = @as(usize, @intCast(sect.size));
|
||||
return self.contents[sect.offset..][0..size];
|
||||
@@ -1089,5 +1109,6 @@ const Atom = @import("Atom.zig");
|
||||
const DwarfInfo = @import("DwarfInfo.zig");
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Platform = @import("load_commands.zig").Platform;
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
|
||||
@@ -76,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
|
||||
}
|
||||
// LC_SOURCE_VERSION
|
||||
sizeofcmds += @sizeOf(macho.source_version_command);
|
||||
// LC_BUILD_VERSION
|
||||
sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
|
||||
// LC_BUILD_VERSION or LC_VERSION_MIN_
|
||||
if (Platform.fromOptions(options).isBuildVersionCompatible()) {
|
||||
// LC_BUILD_VERSION
|
||||
sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
|
||||
} else {
|
||||
// LC_VERSION_MIN_
|
||||
sizeofcmds += @sizeOf(macho.version_min_command);
|
||||
}
|
||||
// LC_UUID
|
||||
sizeofcmds += @sizeOf(macho.uuid_command);
|
||||
// LC_LOAD_DYLIB
|
||||
@@ -252,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void {
|
||||
pub fn writeVersionMinLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
|
||||
const cmd: macho.LC = switch (platform.os_tag) {
|
||||
.macos => .VERSION_MIN_MACOSX,
|
||||
.ios => .VERSION_MIN_IPHONEOS,
|
||||
.tvos => .VERSION_MIN_TVOS,
|
||||
.watchos => .VERSION_MIN_WATCHOS,
|
||||
else => unreachable,
|
||||
};
|
||||
try lc_writer.writeAll(mem.asBytes(&macho.version_min_command{
|
||||
.cmd = cmd,
|
||||
.version = platform.toAppleVersion(),
|
||||
.sdk = if (sdk_version) |ver| Platform.semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn writeBuildVersionLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
|
||||
const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
|
||||
const platform_version = blk: {
|
||||
const ver = options.target.os.version_range.semver.min;
|
||||
const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
|
||||
break :blk platform_version;
|
||||
};
|
||||
const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: {
|
||||
if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path);
|
||||
break :blk null;
|
||||
};
|
||||
const sdk_version_value: u32 = if (sdk_version) |ver|
|
||||
@intCast(ver.major << 16 | ver.minor << 8)
|
||||
else
|
||||
platform_version;
|
||||
const is_simulator_abi = options.target.abi == .simulator;
|
||||
try lc_writer.writeStruct(macho.build_version_command{
|
||||
.cmdsize = cmdsize,
|
||||
.platform = switch (options.target.os.tag) {
|
||||
.macos => .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 = platform_version,
|
||||
.sdk = sdk_version_value,
|
||||
.platform = platform.toApplePlatform(),
|
||||
.minos = platform.toAppleVersion(),
|
||||
.sdk = if (sdk_version) |ver| Platform.semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
|
||||
.ntools = 1,
|
||||
});
|
||||
try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
|
||||
@@ -301,7 +302,124 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an
|
||||
}
|
||||
}
|
||||
|
||||
fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
|
||||
pub const Platform = struct {
|
||||
os_tag: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
version: std.SemanticVersion,
|
||||
|
||||
/// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
|
||||
/// the extracted minimum platform version.
|
||||
pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
|
||||
switch (lc.cmd()) {
|
||||
.BUILD_VERSION => {
|
||||
const cmd = lc.cast(macho.build_version_command).?;
|
||||
return .{
|
||||
.os_tag = switch (cmd.platform) {
|
||||
.MACOS => .macos,
|
||||
.IOS, .IOSSIMULATOR => .ios,
|
||||
.TVOS, .TVOSSIMULATOR => .tvos,
|
||||
.WATCHOS, .WATCHOSSIMULATOR => .watchos,
|
||||
else => @panic("TODO"),
|
||||
},
|
||||
.abi = switch (cmd.platform) {
|
||||
.IOSSIMULATOR,
|
||||
.TVOSSIMULATOR,
|
||||
.WATCHOSSIMULATOR,
|
||||
=> .simulator,
|
||||
else => .none,
|
||||
},
|
||||
.version = appleVersionToSemanticVersion(cmd.minos),
|
||||
};
|
||||
},
|
||||
.VERSION_MIN_MACOSX,
|
||||
.VERSION_MIN_IPHONEOS,
|
||||
.VERSION_MIN_TVOS,
|
||||
.VERSION_MIN_WATCHOS,
|
||||
=> {
|
||||
const cmd = lc.cast(macho.version_min_command).?;
|
||||
return .{
|
||||
.os_tag = switch (lc.cmd()) {
|
||||
.VERSION_MIN_MACOSX => .macos,
|
||||
.VERSION_MIN_IPHONEOS => .ios,
|
||||
.VERSION_MIN_TVOS => .tvos,
|
||||
.VERSION_MIN_WATCHOS => .watchos,
|
||||
else => unreachable,
|
||||
},
|
||||
.abi = .none,
|
||||
.version = appleVersionToSemanticVersion(cmd.version),
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromOptions(options: *const link.Options) Platform {
|
||||
return .{
|
||||
.os_tag = options.target.os.tag,
|
||||
.abi = options.target.abi,
|
||||
.version = options.target.os.version_range.semver.min,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toAppleVersion(plat: Platform) u32 {
|
||||
return semanticVersionToAppleVersion(plat.version);
|
||||
}
|
||||
|
||||
pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
|
||||
return switch (plat.os_tag) {
|
||||
.macos => .MACOS,
|
||||
.ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS,
|
||||
.tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS,
|
||||
.watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isBuildVersionCompatible(plat: Platform) bool {
|
||||
inline for (supported_platforms) |sup_plat| {
|
||||
if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
|
||||
return sup_plat[2] <= plat.toAppleVersion();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
|
||||
const major = version.major;
|
||||
const minor = version.minor;
|
||||
const patch = version.patch;
|
||||
return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
|
||||
}
|
||||
|
||||
inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
|
||||
return .{
|
||||
.major = @as(u16, @truncate(version >> 16)),
|
||||
.minor = @as(u8, @truncate(version >> 8)),
|
||||
.patch = @as(u8, @truncate(version)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const SupportedPlatforms = struct {
|
||||
std.Target.Os.Tag,
|
||||
std.Target.Abi,
|
||||
u32, // Min platform version for which to emit LC_BUILD_VERSION
|
||||
u32, // Min supported platform version
|
||||
?[]const u8, // Env var to look for
|
||||
};
|
||||
|
||||
// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
|
||||
const supported_platforms = [_]SupportedPlatforms{
|
||||
.{ .macos, .none, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" },
|
||||
.{ .ios, .none, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" },
|
||||
.{ .tvos, .none, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" },
|
||||
.{ .watchos, .none, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" },
|
||||
.{ .ios, .simulator, 0xD0000, 0x80000, null },
|
||||
.{ .tvos, .simulator, 0xD0000, 0x80000, null },
|
||||
.{ .watchos, .simulator, 0x60000, 0x20000, null },
|
||||
};
|
||||
|
||||
pub fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
|
||||
const stem = std.fs.path.stem(path);
|
||||
const start = for (stem, 0..) |c, i| {
|
||||
if (std.ascii.isDigit(c)) break i;
|
||||
|
||||
@@ -345,10 +345,13 @@ pub fn linkWithZld(
|
||||
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_os: std.Target.Os.Tag,
|
||||
} = .{
|
||||
.detected_arch = undefined,
|
||||
.detected_os = undefined,
|
||||
};
|
||||
|
||||
for (positionals.items) |obj| {
|
||||
const in_file = try std.fs.cwd().openFile(obj.path, .{});
|
||||
@@ -586,7 +589,18 @@ pub fn linkWithZld(
|
||||
try lc_writer.writeStruct(macho.source_version_command{
|
||||
.version = 0,
|
||||
});
|
||||
try load_commands.writeBuildVersionLC(&macho_file.base.options, lc_writer);
|
||||
{
|
||||
const platform = load_commands.Platform.fromOptions(&macho_file.base.options);
|
||||
const sdk_version: ?std.SemanticVersion = macho_file.base.options.darwin_sdk_version orelse blk: {
|
||||
if (macho_file.base.options.sysroot) |path| break :blk load_commands.inferSdkVersionFromSdkPath(path);
|
||||
break :blk null;
|
||||
};
|
||||
if (platform.isBuildVersionCompatible()) {
|
||||
try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer);
|
||||
} else {
|
||||
try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer);
|
||||
}
|
||||
}
|
||||
|
||||
const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
|
||||
try lc_writer.writeStruct(macho_file.uuid_cmd);
|
||||
|
||||
Reference in New Issue
Block a user