| ... | ... | @@ -76,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx |
| 76 | 76 | } |
| 77 | 77 | // LC_SOURCE_VERSION |
| 78 | 78 | sizeofcmds += @sizeOf(macho.source_version_command); |
| 79 | | // LC_BUILD_VERSION |
| 80 | | sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); |
| 79 | // LC_BUILD_VERSION or LC_VERSION_MIN_ |
| 80 | if (Platform.fromOptions(options).isBuildVersionCompatible()) { |
| 81 | // LC_BUILD_VERSION |
| 82 | sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); |
| 83 | } else { |
| 84 | // LC_VERSION_MIN_ |
| 85 | sizeofcmds += @sizeOf(macho.version_min_command); |
| 86 | } |
| 81 | 87 | // LC_UUID |
| 82 | 88 | sizeofcmds += @sizeOf(macho.uuid_command); |
| 83 | 89 | // LC_LOAD_DYLIB |
| ... | ... | @@ -252,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an |
| 252 | 258 | } |
| 253 | 259 | } |
| 254 | 260 | |
| 255 | | pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void { |
| 256 | | const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); |
| 257 | | const platform_version = blk: { |
| 258 | | const ver = options.target.os.version_range.semver.min; |
| 259 | | const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); |
| 260 | | break :blk platform_version; |
| 261 | | }; |
| 262 | | const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: { |
| 263 | | if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path); |
| 264 | | break :blk null; |
| 261 | pub fn writeVersionMinLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void { |
| 262 | const cmd: macho.LC = switch (platform.os_tag) { |
| 263 | .macos => .VERSION_MIN_MACOSX, |
| 264 | .ios => .VERSION_MIN_IPHONEOS, |
| 265 | .tvos => .VERSION_MIN_TVOS, |
| 266 | .watchos => .VERSION_MIN_WATCHOS, |
| 267 | else => unreachable, |
| 265 | 268 | }; |
| 266 | | const sdk_version_value: u32 = if (sdk_version) |ver| |
| 267 | | @intCast(ver.major << 16 | ver.minor << 8) |
| 268 | | else |
| 269 | | platform_version; |
| 270 | | const is_simulator_abi = options.target.abi == .simulator; |
| 269 | try lc_writer.writeAll(mem.asBytes(&macho.version_min_command{ |
| 270 | .cmd = cmd, |
| 271 | .version = platform.toAppleVersion(), |
| 272 | .sdk = if (sdk_version) |ver| Platform.semanticVersionToAppleVersion(ver) else platform.toAppleVersion(), |
| 273 | })); |
| 274 | } |
| 275 | |
| 276 | pub fn writeBuildVersionLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void { |
| 277 | const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); |
| 271 | 278 | try lc_writer.writeStruct(macho.build_version_command{ |
| 272 | 279 | .cmdsize = cmdsize, |
| 273 | | .platform = switch (options.target.os.tag) { |
| 274 | | .macos => .MACOS, |
| 275 | | .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS, |
| 276 | | .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS, |
| 277 | | .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS, |
| 278 | | else => unreachable, |
| 279 | | }, |
| 280 | | .minos = platform_version, |
| 281 | | .sdk = sdk_version_value, |
| 280 | .platform = platform.toApplePlatform(), |
| 281 | .minos = platform.toAppleVersion(), |
| 282 | .sdk = if (sdk_version) |ver| Platform.semanticVersionToAppleVersion(ver) else platform.toAppleVersion(), |
| 282 | 283 | .ntools = 1, |
| 283 | 284 | }); |
| 284 | 285 | 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 |
| 301 | 302 | } |
| 302 | 303 | } |
| 303 | 304 | |
| 304 | | fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion { |
| 305 | pub const Platform = struct { |
| 306 | os_tag: std.Target.Os.Tag, |
| 307 | abi: std.Target.Abi, |
| 308 | version: std.SemanticVersion, |
| 309 | |
| 310 | /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to |
| 311 | /// the extracted minimum platform version. |
| 312 | pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform { |
| 313 | switch (lc.cmd()) { |
| 314 | .BUILD_VERSION => { |
| 315 | const cmd = lc.cast(macho.build_version_command).?; |
| 316 | return .{ |
| 317 | .os_tag = switch (cmd.platform) { |
| 318 | .MACOS => .macos, |
| 319 | .IOS, .IOSSIMULATOR => .ios, |
| 320 | .TVOS, .TVOSSIMULATOR => .tvos, |
| 321 | .WATCHOS, .WATCHOSSIMULATOR => .watchos, |
| 322 | else => @panic("TODO"), |
| 323 | }, |
| 324 | .abi = switch (cmd.platform) { |
| 325 | .IOSSIMULATOR, |
| 326 | .TVOSSIMULATOR, |
| 327 | .WATCHOSSIMULATOR, |
| 328 | => .simulator, |
| 329 | else => .none, |
| 330 | }, |
| 331 | .version = appleVersionToSemanticVersion(cmd.minos), |
| 332 | }; |
| 333 | }, |
| 334 | .VERSION_MIN_MACOSX, |
| 335 | .VERSION_MIN_IPHONEOS, |
| 336 | .VERSION_MIN_TVOS, |
| 337 | .VERSION_MIN_WATCHOS, |
| 338 | => { |
| 339 | const cmd = lc.cast(macho.version_min_command).?; |
| 340 | return .{ |
| 341 | .os_tag = switch (lc.cmd()) { |
| 342 | .VERSION_MIN_MACOSX => .macos, |
| 343 | .VERSION_MIN_IPHONEOS => .ios, |
| 344 | .VERSION_MIN_TVOS => .tvos, |
| 345 | .VERSION_MIN_WATCHOS => .watchos, |
| 346 | else => unreachable, |
| 347 | }, |
| 348 | .abi = .none, |
| 349 | .version = appleVersionToSemanticVersion(cmd.version), |
| 350 | }; |
| 351 | }, |
| 352 | else => unreachable, |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | pub fn fromOptions(options: *const link.Options) Platform { |
| 357 | return .{ |
| 358 | .os_tag = options.target.os.tag, |
| 359 | .abi = options.target.abi, |
| 360 | .version = options.target.os.version_range.semver.min, |
| 361 | }; |
| 362 | } |
| 363 | |
| 364 | pub fn toAppleVersion(plat: Platform) u32 { |
| 365 | return semanticVersionToAppleVersion(plat.version); |
| 366 | } |
| 367 | |
| 368 | pub fn toApplePlatform(plat: Platform) macho.PLATFORM { |
| 369 | return switch (plat.os_tag) { |
| 370 | .macos => .MACOS, |
| 371 | .ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS, |
| 372 | .tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS, |
| 373 | .watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS, |
| 374 | else => unreachable, |
| 375 | }; |
| 376 | } |
| 377 | |
| 378 | pub fn isBuildVersionCompatible(plat: Platform) bool { |
| 379 | inline for (supported_platforms) |sup_plat| { |
| 380 | if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) { |
| 381 | return sup_plat[2] <= plat.toAppleVersion(); |
| 382 | } |
| 383 | } |
| 384 | return false; |
| 385 | } |
| 386 | |
| 387 | pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { |
| 388 | const major = version.major; |
| 389 | const minor = version.minor; |
| 390 | const patch = version.patch; |
| 391 | return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch)); |
| 392 | } |
| 393 | |
| 394 | inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion { |
| 395 | return .{ |
| 396 | .major = @as(u16, @truncate(version >> 16)), |
| 397 | .minor = @as(u8, @truncate(version >> 8)), |
| 398 | .patch = @as(u8, @truncate(version)), |
| 399 | }; |
| 400 | } |
| 401 | }; |
| 402 | |
| 403 | const SupportedPlatforms = struct { |
| 404 | std.Target.Os.Tag, |
| 405 | std.Target.Abi, |
| 406 | u32, // Min platform version for which to emit LC_BUILD_VERSION |
| 407 | u32, // Min supported platform version |
| 408 | ?[]const u8, // Env var to look for |
| 409 | }; |
| 410 | |
| 411 | // Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52 |
| 412 | const supported_platforms = [_]SupportedPlatforms{ |
| 413 | .{ .macos, .none, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" }, |
| 414 | .{ .ios, .none, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" }, |
| 415 | .{ .tvos, .none, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" }, |
| 416 | .{ .watchos, .none, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" }, |
| 417 | .{ .ios, .simulator, 0xD0000, 0x80000, null }, |
| 418 | .{ .tvos, .simulator, 0xD0000, 0x80000, null }, |
| 419 | .{ .watchos, .simulator, 0x60000, 0x20000, null }, |
| 420 | }; |
| 421 | |
| 422 | pub fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion { |
| 305 | 423 | const stem = std.fs.path.stem(path); |
| 306 | 424 | const start = for (stem, 0..) |c, i| { |
| 307 | 425 | if (std.ascii.isDigit(c)) break i; |