authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-29 15:27:44+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-29 15:27:44+02:00
log79b3285aa216350e0c2ff18436a169af69e4570f
treed4529cb6e9e0c39db171612bc95aa9d8371be98f
parent1cae41bbbb3cb1cf10bfa808ecbe289bfb4d5180

macho: handle mismatched and missing platform errors


6 files changed, 290 insertions(+), 204 deletions(-)

src/link/MachO.zig+172-109
......@@ -396,16 +396,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
396396 self.dylibs_map.clearRetainingCapacity();
397397 self.referenced_dylibs.clearRetainingCapacity();
398398
399 const cpu_arch = self.base.options.target.cpu.arch;
400399 var dependent_libs = std.fifo.LinearFifo(struct {
401400 id: Dylib.Id,
402401 parent: u16,
403402 }, .Dynamic).init(arena);
404403
405 var parse_error_ctx: union {
406 none: void,
404 var parse_error_ctx: struct {
407405 detected_arch: std.Target.Cpu.Arch,
408 } = .{ .none = {} };
406 detected_platform: ?Platform,
407 detected_stub_targets: []const []const u8,
408 } = .{
409 .detected_arch = undefined,
410 .detected_platform = null,
411 .detected_stub_targets = &[0][]const u8{},
412 };
413 defer {
414 for (parse_error_ctx.detected_stub_targets) |target| self.base.allocator.free(target);
415 self.base.allocator.free(parse_error_ctx.detected_stub_targets);
416 }
409417
410418 for (libs.keys(), libs.values()) |path, lib| {
411419 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
418426 false,
419427 &dependent_libs,
420428 &parse_error_ctx,
421 ) catch |err| switch (err) {
422 error.DylibAlreadyExists => {},
423 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
424 error.MissingArchFatLib => try self.reportParseError(
425 path,
426 "missing architecture in universal file, expected '{s}'",
427 .{@tagName(cpu_arch)},
428 ),
429 error.InvalidArch => try self.reportParseError(
430 path,
431 "invalid architecture '{s}', expected '{s}'",
432 .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
433 ),
434 else => |e| try self.reportParseError(
435 path,
436 "parsing library failed with error '{s}'",
437 .{@errorName(e)},
438 ),
439 };
429 ) catch |err| try self.handleAndReportParseError(path, err, parse_error_ctx);
440430 }
441431
442432 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
586576 .version = 0,
587577 });
588578 {
589 const platform = load_commands.Platform.fromOptions(&self.base.options);
579 const platform = Platform.fromTarget(self.base.options.target);
590580 const sdk_version: ?std.SemanticVersion = if (self.base.options.sysroot) |path|
591581 load_commands.inferSdkVersionFromSdkPath(path)
592582 else
......@@ -738,7 +728,8 @@ fn resolveLib(
738728const ParseError = error{
739729 UnknownFileType,
740730 MissingArchFatLib,
741 InvalidArch,
731 InvalidTarget,
732 InvalidLibStubTargets,
742733 DylibAlreadyExists,
743734 IncompatibleDylibVersion,
744735 OutOfMemory,
......@@ -798,19 +789,24 @@ fn parseObject(
798789 };
799790 errdefer object.deinit(gpa);
800791 try object.parse(gpa);
801 try self.objects.append(gpa, object);
802792
803793 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
804794 macho.CPU_TYPE_ARM64 => .aarch64,
805795 macho.CPU_TYPE_X86_64 => .x86_64,
806796 else => unreachable,
807797 };
808 const self_cpu_arch = self.base.options.target.cpu.arch;
798 error_ctx.detected_arch = cpu_arch;
809799
810 if (self_cpu_arch != cpu_arch) {
811 error_ctx.detected_arch = cpu_arch;
812 return error.InvalidArch;
800 if (object.getPlatform()) |platform| {
801 error_ctx.detected_platform = platform;
802 }
803
804 if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
805 if (error_ctx.detected_platform) |platform| {
806 if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
813807 }
808
809 try self.objects.append(gpa, object);
814810}
815811
816812pub fn parseLibrary(
......@@ -825,14 +821,12 @@ pub fn parseLibrary(
825821 const tracy = trace(@src());
826822 defer tracy.end();
827823
828 const cpu_arch = self.base.options.target.cpu.arch;
829
830824 if (fat.isFatLibrary(file)) {
831 const offset = try self.parseFatLibrary(file, cpu_arch);
825 const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch);
832826 try file.seekTo(offset);
833827
834828 if (Archive.isArchive(file, offset)) {
835 try self.parseArchive(path, offset, must_link, cpu_arch, error_ctx);
829 try self.parseArchive(path, offset, must_link, error_ctx);
836830 } else if (Dylib.isDylib(file, offset)) {
837831 try self.parseDylib(file, path, offset, dependent_libs, .{
838832 .needed = lib.needed,
......@@ -840,7 +834,7 @@ pub fn parseLibrary(
840834 }, error_ctx);
841835 } else return error.UnknownFileType;
842836 } else if (Archive.isArchive(file, 0)) {
843 try self.parseArchive(path, 0, must_link, cpu_arch, error_ctx);
837 try self.parseArchive(path, 0, must_link, error_ctx);
844838 } else if (Dylib.isDylib(file, 0)) {
845839 try self.parseDylib(file, path, 0, dependent_libs, .{
846840 .needed = lib.needed,
......@@ -850,7 +844,7 @@ pub fn parseLibrary(
850844 self.parseLibStub(file, path, dependent_libs, .{
851845 .needed = lib.needed,
852846 .weak = lib.weak,
853 }) catch |err| switch (err) {
847 }, error_ctx) catch |err| switch (err) {
854848 error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType,
855849 else => |e| return e,
856850 };
......@@ -872,7 +866,6 @@ fn parseArchive(
872866 path: []const u8,
873867 fat_offset: u64,
874868 must_link: bool,
875 cpu_arch: std.Target.Cpu.Arch,
876869 error_ctx: anytype,
877870) ParseError!void {
878871 const gpa = self.base.allocator;
......@@ -899,14 +892,20 @@ fn parseArchive(
899892 var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only!
900893 defer object.deinit(gpa);
901894
902 const parsed_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
895 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
903896 macho.CPU_TYPE_ARM64 => .aarch64,
904897 macho.CPU_TYPE_X86_64 => .x86_64,
905898 else => unreachable,
906899 };
907 if (cpu_arch != parsed_cpu_arch) {
908 error_ctx.detected_arch = parsed_cpu_arch;
909 return error.InvalidArch;
900 error_ctx.detected_arch = cpu_arch;
901
902 if (object.getPlatform()) |platform| {
903 error_ctx.detected_platform = platform;
904 }
905
906 if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
907 if (error_ctx.detected_platform) |platform| {
908 if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
910909 }
911910 }
912911
......@@ -945,8 +944,6 @@ fn parseDylib(
945944 error_ctx: anytype,
946945) ParseError!void {
947946 const gpa = self.base.allocator;
948 const self_cpu_arch = self.base.options.target.cpu.arch;
949
950947 const file_stat = try file.stat();
951948 const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow;
952949
......@@ -969,12 +966,16 @@ fn parseDylib(
969966 macho.CPU_TYPE_X86_64 => .x86_64,
970967 else => unreachable,
971968 };
972 if (self_cpu_arch != cpu_arch) {
973 error_ctx.detected_arch = cpu_arch;
974 return error.InvalidArch;
969 error_ctx.detected_arch = cpu_arch;
970
971 if (dylib.getPlatform(contents)) |platform| {
972 error_ctx.detected_platform = platform;
975973 }
976974
977 // TODO verify platform
975 if (self.base.options.target.cpu.arch != cpu_arch) return error.InvalidTarget;
976 if (error_ctx.detected_platform) |platform| {
977 if (!Platform.fromTarget(self.base.options.target).eqlTarget(platform)) return error.InvalidTarget;
978 }
978979
979980 try self.addDylib(dylib, .{
980981 .needed = dylib_options.needed,
......@@ -988,6 +989,7 @@ fn parseLibStub(
988989 path: []const u8,
989990 dependent_libs: anytype,
990991 dylib_options: DylibOpts,
992 error_ctx: anytype,
991993) ParseError!void {
992994 const gpa = self.base.allocator;
993995 var lib_stub = try LibStub.loadFromFile(gpa, file);
......@@ -995,7 +997,20 @@ fn parseLibStub(
995997
996998 if (lib_stub.inner.len == 0) return error.NotLibStub;
997999
998 // TODO verify platform
1000 // Verify target
1001 {
1002 var matcher = try Dylib.TargetMatcher.init(gpa, self.base.options.target);
1003 defer matcher.deinit();
1004
1005 const first_tbd = lib_stub.inner[0];
1006 const targets = try first_tbd.targets(gpa);
1007 if (!matcher.matchesTarget(targets)) {
1008 error_ctx.detected_stub_targets = targets;
1009 return error.InvalidLibStubTargets;
1010 }
1011 for (targets) |t| gpa.free(t);
1012 gpa.free(targets);
1013 }
9991014
10001015 var dylib = Dylib{ .weak = dylib_options.weak };
10011016 errdefer dylib.deinit(gpa);
......@@ -1104,7 +1119,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, error_ctx: anyt
11041119 self.parseLibStub(file, full_path, dependent_libs, .{
11051120 .dependent = true,
11061121 .weak = weak,
1107 }) catch |err| switch (err) {
1122 }, error_ctx) catch |err| switch (err) {
11081123 error.NotLibStub, error.UnexpectedToken => continue,
11091124 else => |e| return e,
11101125 };
......@@ -4830,6 +4845,53 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
48304845 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
48314846}
48324847
4848pub fn handleAndReportParseError(self: *MachO, path: []const u8, err: ParseError, parse_error_ctx: anytype) !void {
4849 const cpu_arch = self.base.options.target.cpu.arch;
4850 switch (err) {
4851 error.DylibAlreadyExists => {},
4852 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
4853 error.MissingArchFatLib => try self.reportParseError(
4854 path,
4855 "missing architecture in universal file, expected '{s}'",
4856 .{@tagName(cpu_arch)},
4857 ),
4858 error.InvalidTarget => if (parse_error_ctx.detected_platform) |platform| {
4859 try self.reportParseError(path, "invalid target '{s}-{}', expected '{s}-{}'", .{
4860 @tagName(parse_error_ctx.detected_arch),
4861 platform.fmtTarget(),
4862 @tagName(cpu_arch),
4863 Platform.fromTarget(self.base.options.target).fmtTarget(),
4864 });
4865 } else {
4866 try self.reportParseError(
4867 path,
4868 "invalid architecture '{s}', expected '{s}'",
4869 .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
4870 );
4871 },
4872 error.InvalidLibStubTargets => {
4873 var targets_string = std.ArrayList(u8).init(self.base.allocator);
4874 defer targets_string.deinit();
4875 try targets_string.writer().writeAll("(");
4876 for (parse_error_ctx.detected_stub_targets) |t| {
4877 try targets_string.writer().print("{s}, ", .{t});
4878 }
4879 try targets_string.resize(targets_string.items.len - 2);
4880 try targets_string.writer().writeAll(")");
4881 try self.reportParseError(path, "invalid targets '{s}', expected '{s}-{}'", .{
4882 targets_string.items,
4883 @tagName(cpu_arch),
4884 Platform.fromTarget(self.base.options.target).fmtTarget(),
4885 });
4886 },
4887 else => |e| try self.reportParseError(
4888 path,
4889 "parsing positional argument failed with error '{s}'",
4890 .{@errorName(e)},
4891 ),
4892 }
4893}
4894
48334895pub fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void {
48344896 const gpa = self.base.allocator;
48354897 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
......@@ -5140,66 +5202,6 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
51405202 }
51415203}
51425204
5143const MachO = @This();
5144
5145const std = @import("std");
5146const build_options = @import("build_options");
5147const builtin = @import("builtin");
5148const assert = std.debug.assert;
5149const dwarf = std.dwarf;
5150const fs = std.fs;
5151const log = std.log.scoped(.link);
5152const macho = std.macho;
5153const math = std.math;
5154const mem = std.mem;
5155const meta = std.meta;
5156
5157const aarch64 = @import("../arch/aarch64/bits.zig");
5158const calcUuid = @import("MachO/uuid.zig").calcUuid;
5159const codegen = @import("../codegen.zig");
5160const dead_strip = @import("MachO/dead_strip.zig");
5161const fat = @import("MachO/fat.zig");
5162const link = @import("../link.zig");
5163const llvm_backend = @import("../codegen/llvm.zig");
5164const load_commands = @import("MachO/load_commands.zig");
5165const stubs = @import("MachO/stubs.zig");
5166const tapi = @import("tapi.zig");
5167const target_util = @import("../target.zig");
5168const thunks = @import("MachO/thunks.zig");
5169const trace = @import("../tracy.zig").trace;
5170const zld = @import("MachO/zld.zig");
5171
5172const Air = @import("../Air.zig");
5173const Allocator = mem.Allocator;
5174const Archive = @import("MachO/Archive.zig");
5175pub const Atom = @import("MachO/Atom.zig");
5176const Cache = std.Build.Cache;
5177const CodeSignature = @import("MachO/CodeSignature.zig");
5178const Compilation = @import("../Compilation.zig");
5179const Dwarf = File.Dwarf;
5180const DwarfInfo = @import("MachO/DwarfInfo.zig");
5181const Dylib = @import("MachO/Dylib.zig");
5182const File = link.File;
5183const Object = @import("MachO/Object.zig");
5184const LibStub = tapi.LibStub;
5185const Liveness = @import("../Liveness.zig");
5186const LlvmObject = @import("../codegen/llvm.zig").Object;
5187const Md5 = std.crypto.hash.Md5;
5188const Module = @import("../Module.zig");
5189const InternPool = @import("../InternPool.zig");
5190const Relocation = @import("MachO/Relocation.zig");
5191const StringTable = @import("strtab.zig").StringTable;
5192const TableSection = @import("table_section.zig").TableSection;
5193const Trie = @import("MachO/Trie.zig");
5194const Type = @import("../type.zig").Type;
5195const TypedValue = @import("../TypedValue.zig");
5196const Value = @import("../value.zig").Value;
5197
5198pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5199pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
5200pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
5201pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
5202
52035205pub const base_tag: File.Tag = File.Tag.macho;
52045206pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
52055207
......@@ -5332,3 +5334,64 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;
53325334/// the table of load commands. This should be plenty for any
53335335/// potential future extensions.
53345336pub const default_headerpad_size: u32 = 0x1000;
5337
5338const MachO = @This();
5339
5340const std = @import("std");
5341const build_options = @import("build_options");
5342const builtin = @import("builtin");
5343const assert = std.debug.assert;
5344const dwarf = std.dwarf;
5345const fs = std.fs;
5346const log = std.log.scoped(.link);
5347const macho = std.macho;
5348const math = std.math;
5349const mem = std.mem;
5350const meta = std.meta;
5351
5352const aarch64 = @import("../arch/aarch64/bits.zig");
5353const calcUuid = @import("MachO/uuid.zig").calcUuid;
5354const codegen = @import("../codegen.zig");
5355const dead_strip = @import("MachO/dead_strip.zig");
5356const fat = @import("MachO/fat.zig");
5357const link = @import("../link.zig");
5358const llvm_backend = @import("../codegen/llvm.zig");
5359const load_commands = @import("MachO/load_commands.zig");
5360const stubs = @import("MachO/stubs.zig");
5361const tapi = @import("tapi.zig");
5362const target_util = @import("../target.zig");
5363const thunks = @import("MachO/thunks.zig");
5364const trace = @import("../tracy.zig").trace;
5365const zld = @import("MachO/zld.zig");
5366
5367const Air = @import("../Air.zig");
5368const Allocator = mem.Allocator;
5369const Archive = @import("MachO/Archive.zig");
5370pub const Atom = @import("MachO/Atom.zig");
5371const Cache = std.Build.Cache;
5372const CodeSignature = @import("MachO/CodeSignature.zig");
5373const Compilation = @import("../Compilation.zig");
5374const Dwarf = File.Dwarf;
5375const DwarfInfo = @import("MachO/DwarfInfo.zig");
5376const Dylib = @import("MachO/Dylib.zig");
5377const File = link.File;
5378const Object = @import("MachO/Object.zig");
5379const LibStub = tapi.LibStub;
5380const Liveness = @import("../Liveness.zig");
5381const LlvmObject = @import("../codegen/llvm.zig").Object;
5382const Md5 = std.crypto.hash.Md5;
5383const Module = @import("../Module.zig");
5384const InternPool = @import("../InternPool.zig");
5385const Platform = load_commands.Platform;
5386const Relocation = @import("MachO/Relocation.zig");
5387const StringTable = @import("strtab.zig").StringTable;
5388const TableSection = @import("table_section.zig").TableSection;
5389const Trie = @import("MachO/Trie.zig");
5390const Type = @import("../type.zig").Type;
5391const TypedValue = @import("../TypedValue.zig");
5392const Value = @import("../value.zig").Value;
5393
5394pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5395pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
5396pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
5397pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
src/link/MachO/Dylib.zig+54-48
......@@ -178,6 +178,26 @@ pub fn parseFromBinary(
178178 }
179179}
180180
181/// Returns Platform composed from the first encountered build version type load command:
182/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
183pub fn getPlatform(self: Dylib, data: []align(@alignOf(u64)) const u8) ?Platform {
184 var it = LoadCommandIterator{
185 .ncmds = self.header.?.ncmds,
186 .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
187 };
188 while (it.next()) |cmd| {
189 switch (cmd.cmd()) {
190 .BUILD_VERSION,
191 .VERSION_MIN_MACOSX,
192 .VERSION_MIN_IPHONEOS,
193 .VERSION_MIN_TVOS,
194 .VERSION_MIN_WATCHOS,
195 => return Platform.fromLoadCommand(cmd),
196 else => {},
197 }
198 } else return null;
199}
200
181201fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
182202 const expanded = &[_][]const u8{
183203 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
212232 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);
213233}
214234
215const TargetMatcher = struct {
235pub const TargetMatcher = struct {
216236 allocator: Allocator,
217 target: CrossTarget,
237 cpu_arch: std.Target.Cpu.Arch,
238 os_tag: std.Target.Os.Tag,
239 abi: std.Target.Abi,
218240 target_strings: std.ArrayListUnmanaged([]const u8) = .{},
219241
220 pub fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
242 pub fn init(allocator: Allocator, target: std.Target) !TargetMatcher {
221243 var self = TargetMatcher{
222244 .allocator = allocator,
223 .target = target,
245 .cpu_arch = target.cpu.arch,
246 .os_tag = target.os.tag,
247 .abi = target.abi,
224248 };
225 const apple_string = try targetToAppleString(allocator, target);
249 const apple_string = try toAppleTargetTriple(allocator, self.cpu_arch, self.os_tag, self.abi);
226250 try self.target_strings.append(allocator, apple_string);
227251
228 const abi = target.abi orelse .none;
229 if (abi == .simulator) {
252 if (self.abi == .simulator) {
230253 // For Apple simulator targets, linking gets tricky as we need to link against the simulator
231254 // hosts dylibs too.
232 const host_target = try targetToAppleString(allocator, .{
233 .cpu_arch = target.cpu_arch.?,
234 .os_tag = .macos,
235 });
255 const host_target = try toAppleTargetTriple(allocator, self.cpu_arch, .macos, .none);
236256 try self.target_strings.append(allocator, host_target);
237257 }
238258
......@@ -246,7 +266,7 @@ const TargetMatcher = struct {
246266 self.target_strings.deinit(self.allocator);
247267 }
248268
249 inline fn cpuArchToAppleString(cpu_arch: std.Target.Cpu.Arch) []const u8 {
269 inline fn fmtCpuArch(cpu_arch: std.Target.Cpu.Arch) []const u8 {
250270 return switch (cpu_arch) {
251271 .aarch64 => "arm64",
252272 .x86_64 => "x86_64",
......@@ -254,7 +274,7 @@ const TargetMatcher = struct {
254274 };
255275 }
256276
257 inline fn abiToAppleString(abi: std.Target.Abi) ?[]const u8 {
277 inline fn fmtAbi(abi: std.Target.Abi) ?[]const u8 {
258278 return switch (abi) {
259279 .none => null,
260280 .simulator => "simulator",
......@@ -263,14 +283,18 @@ const TargetMatcher = struct {
263283 };
264284 }
265285
266 pub fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {
267 const cpu_arch = cpuArchToAppleString(target.cpu_arch.?);
268 const os_tag = @tagName(target.os_tag.?);
269 const target_abi = abiToAppleString(target.abi orelse .none);
270 if (target_abi) |abi| {
271 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch, os_tag, abi });
286 pub fn toAppleTargetTriple(
287 allocator: Allocator,
288 cpu_arch: std.Target.Cpu.Arch,
289 os_tag: std.Target.Os.Tag,
290 abi: std.Target.Abi,
291 ) ![]const u8 {
292 const cpu_arch_s = fmtCpuArch(cpu_arch);
293 const os_tag_s = @tagName(os_tag);
294 if (fmtAbi(abi)) |abi_s| {
295 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch_s, os_tag_s, abi_s });
272296 }
273 return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch, os_tag });
297 return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch_s, os_tag_s });
274298 }
275299
276300 fn hasValue(stack: []const []const u8, needle: []const u8) bool {
......@@ -280,7 +304,7 @@ const TargetMatcher = struct {
280304 return false;
281305 }
282306
283 fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
307 pub fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
284308 for (self.target_strings.items) |t| {
285309 if (hasValue(targets, t)) return true;
286310 }
......@@ -288,26 +312,7 @@ const TargetMatcher = struct {
288312 }
289313
290314 fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {
291 return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));
292 }
293
294 pub fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
295 var arena = std.heap.ArenaAllocator.init(self.allocator);
296 defer arena.deinit();
297
298 const targets = switch (tbd) {
299 .v3 => |v3| blk: {
300 var targets = std.ArrayList([]const u8).init(arena.allocator());
301 for (v3.archs) |arch| {
302 const target = try std.fmt.allocPrint(arena.allocator(), "{s}-{s}", .{ arch, v3.platform });
303 try targets.append(target);
304 }
305 break :blk targets.items;
306 },
307 .v4 => |v4| v4.targets,
308 };
309
310 return self.matchesTarget(targets);
315 return hasValue(archs, fmtCpuArch(self.cpu_arch));
311316 }
312317};
313318
......@@ -342,15 +347,16 @@ pub fn parseFromStub(
342347
343348 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
344349
345 var matcher = try TargetMatcher.init(allocator, .{
346 .cpu_arch = target.cpu.arch,
347 .os_tag = target.os.tag,
348 .abi = target.abi,
349 });
350 var matcher = try TargetMatcher.init(allocator, target);
350351 defer matcher.deinit();
351352
352353 for (lib_stub.inner, 0..) |elem, stub_index| {
353 if (!(try matcher.matchesTargetTbd(elem))) continue;
354 const targets = try elem.targets(allocator);
355 defer {
356 for (targets) |t| allocator.free(t);
357 allocator.free(targets);
358 }
359 if (!matcher.matchesTarget(targets)) continue;
354360
355361 if (stub_index > 0) {
356362 // TODO I thought that we could switch on presence of `parent-umbrella` map;
......@@ -541,8 +547,8 @@ const fat = @import("fat.zig");
541547const tapi = @import("../tapi.zig");
542548
543549const Allocator = mem.Allocator;
544const CrossTarget = std.zig.CrossTarget;
545550const LibStub = tapi.LibStub;
546551const LoadCommandIterator = macho.LoadCommandIterator;
547552const MachO = @import("../MachO.zig");
553const Platform = @import("load_commands.zig").Platform;
548554const Tbd = tapi.Tbd;
src/link/MachO/Object.zig+1-1
......@@ -940,7 +940,7 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
940940 return di;
941941}
942942
943/// Returns Options.Platform composed from the first encountered build version type load command:
943/// Returns Platform composed from the first encountered build version type load command:
944944/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
945945pub fn getPlatform(self: Object) ?Platform {
946946 var it = LoadCommandIterator{
src/link/MachO/load_commands.zig+27-5
......@@ -77,7 +77,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
7777 // LC_SOURCE_VERSION
7878 sizeofcmds += @sizeOf(macho.source_version_command);
7979 // LC_BUILD_VERSION or LC_VERSION_MIN_
80 if (Platform.fromOptions(options).isBuildVersionCompatible()) {
80 if (Platform.fromTarget(options.target).isBuildVersionCompatible()) {
8181 // LC_BUILD_VERSION
8282 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
8383 } else {
......@@ -353,11 +353,11 @@ pub const Platform = struct {
353353 }
354354 }
355355
356 pub fn fromOptions(options: *const link.Options) Platform {
356 pub fn fromTarget(target: std.Target) Platform {
357357 return .{
358 .os_tag = options.target.os.tag,
359 .abi = options.target.abi,
360 .version = options.target.os.version_range.semver.min,
358 .os_tag = target.os.tag,
359 .abi = target.abi,
360 .version = target.os.version_range.semver.min,
361361 };
362362 }
363363
......@@ -383,6 +383,28 @@ pub const Platform = struct {
383383 }
384384 return false;
385385 }
386
387 pub fn fmtTarget(plat: Platform) std.fmt.Formatter(formatTarget) {
388 return .{ .data = plat };
389 }
390
391 pub fn formatTarget(
392 plat: Platform,
393 comptime unused_fmt_string: []const u8,
394 options: std.fmt.FormatOptions,
395 writer: anytype,
396 ) !void {
397 _ = unused_fmt_string;
398 _ = options;
399 try writer.print("{s}", .{@tagName(plat.os_tag)});
400 if (plat.abi != .none) {
401 try writer.print("-{s}", .{@tagName(plat.abi)});
402 }
403 }
404
405 pub fn eqlTarget(plat: Platform, other: Platform) bool {
406 return plat.os_tag == other.os_tag and plat.abi == other.abi;
407 }
386408};
387409
388410const SupportedPlatforms = struct {
src/link/MachO/zld.zig+12-41
......@@ -347,11 +347,17 @@ pub fn linkWithZld(
347347
348348 var parse_error_ctx: struct {
349349 detected_arch: std.Target.Cpu.Arch,
350 detected_os: std.Target.Os.Tag,
350 detected_platform: ?Platform,
351 detected_stub_targets: []const []const u8,
351352 } = .{
352353 .detected_arch = undefined,
353 .detected_os = undefined,
354 .detected_platform = null,
355 .detected_stub_targets = &[0][]const u8{},
354356 };
357 defer {
358 for (parse_error_ctx.detected_stub_targets) |t| gpa.free(t);
359 gpa.free(parse_error_ctx.detected_stub_targets);
360 }
355361
356362 for (positionals.items) |obj| {
357363 const in_file = try std.fs.cwd().openFile(obj.path, .{});
......@@ -363,25 +369,7 @@ pub fn linkWithZld(
363369 obj.must_link,
364370 &dependent_libs,
365371 &parse_error_ctx,
366 ) catch |err| switch (err) {
367 error.DylibAlreadyExists => {},
368 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type", .{}),
369 error.MissingArchFatLib => try macho_file.reportParseError(
370 obj.path,
371 "missing architecture in universal file, expected '{s}'",
372 .{@tagName(cpu_arch)},
373 ),
374 error.InvalidArch => try macho_file.reportParseError(
375 obj.path,
376 "invalid architecture '{s}', expected '{s}'",
377 .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
378 ),
379 else => |e| try macho_file.reportParseError(
380 obj.path,
381 "parsing positional argument failed with error '{s}'",
382 .{@errorName(e)},
383 ),
384 };
372 ) catch |err| try macho_file.handleAndReportParseError(obj.path, err, parse_error_ctx);
385373 }
386374
387375 for (libs.keys(), libs.values()) |path, lib| {
......@@ -395,25 +383,7 @@ pub fn linkWithZld(
395383 false,
396384 &dependent_libs,
397385 &parse_error_ctx,
398 ) catch |err| switch (err) {
399 error.DylibAlreadyExists => {},
400 error.UnknownFileType => try macho_file.reportParseError(path, "unknown file type", .{}),
401 error.MissingArchFatLib => try macho_file.reportParseError(
402 path,
403 "missing architecture in universal file, expected '{s}'",
404 .{@tagName(cpu_arch)},
405 ),
406 error.InvalidArch => try macho_file.reportParseError(
407 path,
408 "invalid architecture '{s}', expected '{s}'",
409 .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) },
410 ),
411 else => |e| try macho_file.reportParseError(
412 path,
413 "parsing library failed with error '{s}'",
414 .{@errorName(e)},
415 ),
416 };
386 ) catch |err| try macho_file.handleAndReportParseError(path, err, parse_error_ctx);
417387 }
418388
419389 macho_file.parseDependentLibs(&dependent_libs, &parse_error_ctx) catch |err| {
......@@ -590,7 +560,7 @@ pub fn linkWithZld(
590560 .version = 0,
591561 });
592562 {
593 const platform = load_commands.Platform.fromOptions(&macho_file.base.options);
563 const platform = Platform.fromTarget(macho_file.base.options.target);
594564 const sdk_version: ?std.SemanticVersion = if (macho_file.base.options.sysroot) |path|
595565 load_commands.inferSdkVersionFromSdkPath(path)
596566 else
......@@ -1252,6 +1222,7 @@ const MachO = @import("../MachO.zig");
12521222const Md5 = std.crypto.hash.Md5;
12531223const LibStub = @import("../tapi.zig").LibStub;
12541224const Object = @import("Object.zig");
1225const Platform = load_commands.Platform;
12551226const Section = MachO.Section;
12561227const StringTable = @import("../strtab.zig").StringTable;
12571228const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/tapi.zig+24
......@@ -81,6 +81,30 @@ pub const Tbd = union(enum) {
8181 v3: TbdV3,
8282 v4: TbdV4,
8383
84 /// Caller owns memory.
85 pub fn targets(self: Tbd, gpa: Allocator) error{OutOfMemory}![]const []const u8 {
86 var out = std.ArrayList([]const u8).init(gpa);
87 defer out.deinit();
88
89 switch (self) {
90 .v3 => |v3| {
91 try out.ensureTotalCapacityPrecise(v3.archs.len);
92 for (v3.archs) |arch| {
93 const target = try std.fmt.allocPrint(gpa, "{s}-{s}", .{ arch, v3.platform });
94 out.appendAssumeCapacity(target);
95 }
96 },
97 .v4 => |v4| {
98 try out.ensureTotalCapacityPrecise(v4.targets.len);
99 for (v4.targets) |t| {
100 out.appendAssumeCapacity(try gpa.dupe(u8, t));
101 }
102 },
103 }
104
105 return out.toOwnedSlice();
106 }
107
84108 pub fn currentVersion(self: Tbd) ?VersionField {
85109 return switch (self) {
86110 .v3 => |v3| v3.current_version,