diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-08-29 15:27:44 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-08-29 15:27:44 +0200 |
| commit | 79b3285aa216350e0c2ff18436a169af69e4570f (patch) | |
| tree | d4529cb6e9e0c39db171612bc95aa9d8371be98f /src | |
| parent | 1cae41bbbb3cb1cf10bfa808ecbe289bfb4d5180 (diff) | |
| download | zig-79b3285aa216350e0c2ff18436a169af69e4570f.tar.gz zig-79b3285aa216350e0c2ff18436a169af69e4570f.zip | |
macho: handle mismatched and missing platform errors
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 281 | ||||
| -rw-r--r-- | src/link/MachO/Dylib.zig | 102 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 2 | ||||
| -rw-r--r-- | src/link/MachO/load_commands.zig | 32 | ||||
| -rw-r--r-- | src/link/MachO/zld.zig | 53 | ||||
| -rw-r--r-- | src/link/tapi.zig | 24 |
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, |
