diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-08-11 19:36:21 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-11 19:36:21 +0200 |
| commit | 60a5552d414ffedf84117df57963fd5bf099c2ea (patch) | |
| tree | 586178dc0f24bf9875e58a830af1e2fdde51af2f /src/link/MachO | |
| parent | f2bf1390a29a9decaa5ca49d3ae720b360583b35 (diff) | |
| parent | 509fe33d10e4e89a351678f4d466f30a7870ebcf (diff) | |
| download | zig-60a5552d414ffedf84117df57963fd5bf099c2ea.tar.gz zig-60a5552d414ffedf84117df57963fd5bf099c2ea.zip | |
Merge pull request #9532 from ziglang/basic-ios-support
Add minimal support to Zig toolchain to support building iOS binaries
Diffstat (limited to 'src/link/MachO')
| -rw-r--r-- | src/link/MachO/Archive.zig | 13 | ||||
| -rw-r--r-- | src/link/MachO/Dylib.zig | 97 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 13 | ||||
| -rw-r--r-- | src/link/MachO/commands.zig | 9 | ||||
| -rw-r--r-- | src/link/MachO/fat.zig | 10 |
5 files changed, 99 insertions, 43 deletions
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 6959dbac89..3b01233e1f 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -9,7 +9,6 @@ const mem = std.mem; const fat = @import("fat.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const Object = @import("Object.zig"); file: fs.File, @@ -104,7 +103,7 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void { allocator.free(self.name); } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Archive { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -119,7 +118,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - archive.parse(allocator, arch) catch |err| switch (err) { + archive.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotArchive => { archive.deinit(allocator); return null; @@ -130,9 +129,9 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return archive; } -pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Archive, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); - self.library_offset = try fat.getLibraryOffset(reader, arch); + self.library_offset = try fat.getLibraryOffset(reader, target); try self.file.seekTo(self.library_offset); const magic = try reader.readBytesNoEof(SARMAG); @@ -215,7 +214,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype) } } -pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32) !Object { +pub fn parseObject(self: Archive, allocator: *Allocator, target: std.Target, offset: u32) !Object { const reader = self.file.reader(); try reader.context.seekTo(offset + self.library_offset); @@ -244,7 +243,7 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32 .mtime = try self.header.?.date(), }; - try object.parse(allocator, arch); + try object.parse(allocator, target); try reader.context.seekTo(0); return object; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 71301ccbbf..64be1fd2fa 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -12,7 +12,6 @@ const fat = @import("fat.zig"); const commands = @import("commands.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LibStub = @import("../tapi.zig").LibStub; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); @@ -139,11 +138,12 @@ pub const Error = error{ pub const CreateOpts = struct { syslibroot: ?[]const u8 = null, id: ?Id = null, + target: ?std.Target = null, }; pub fn createAndParseFromPath( allocator: *Allocator, - arch: Arch, + target: std.Target, path: []const u8, opts: CreateOpts, ) Error!?[]Dylib { @@ -161,7 +161,7 @@ pub fn createAndParseFromPath( .file = file, }; - dylib.parse(allocator, arch) catch |err| switch (err) { + dylib.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -171,7 +171,7 @@ pub fn createAndParseFromPath( }; defer lib_stub.deinit(); - try dylib.parseFromStub(allocator, arch, lib_stub); + try dylib.parseFromStub(allocator, target, lib_stub); }, else => |e| return e, }; @@ -195,7 +195,7 @@ pub fn createAndParseFromPath( try dylibs.append(dylib); // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - try dylib.parseDependentLibs(allocator, arch, &dylibs, opts.syslibroot); + try dylib.parseDependentLibs(allocator, target, &dylibs, opts.syslibroot); return dylibs.toOwnedSlice(); } @@ -222,10 +222,10 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { } } -pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { log.debug("parsing shared library '{s}'", .{self.name}); - self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch); + self.library_offset = try fat.getLibraryOffset(self.file.reader(), target); try self.file.seekTo(self.library_offset); @@ -237,10 +237,10 @@ pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { return error.NotDylib; } - const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); + const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true); - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } @@ -334,7 +334,61 @@ fn addObjCClassSymbols(self: *Dylib, allocator: *Allocator, sym_name: []const u8 } } -pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: LibStub) !void { +fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { + const arch = switch (target.cpu.arch) { + .aarch64 => "arm64", + .x86_64 => "x86_64", + else => unreachable, + }; + const os = @tagName(target.os.tag); + const abi: ?[]const u8 = switch (target.abi) { + .gnu => null, + .simulator => "simulator", + else => unreachable, + }; + if (abi) |x| { + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch, os, x }); + } + return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, os }); +} + +const TargetMatcher = struct { + allocator: *Allocator, + target_strings: std.ArrayListUnmanaged([]const u8) = .{}, + + fn init(allocator: *Allocator, target: std.Target) !TargetMatcher { + var self = TargetMatcher{ .allocator = allocator }; + try self.target_strings.append(allocator, try targetToAppleString(allocator, target)); + + if (target.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, (std.zig.CrossTarget{ + .cpu_arch = target.cpu.arch, + .os_tag = .macos, + }).toTarget()); + try self.target_strings.append(allocator, host_target); + } + + return self; + } + + fn deinit(self: *TargetMatcher) void { + for (self.target_strings.items) |t| { + self.allocator.free(t); + } + self.target_strings.deinit(self.allocator); + } + + fn matches(self: TargetMatcher, targets: []const []const u8) bool { + for (self.target_strings.items) |t| { + if (hasTarget(targets, t)) return true; + } + return false; + } +}; + +pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; log.debug("parsing shared library from stub '{s}'", .{self.name}); @@ -350,17 +404,14 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: } self.id = id; - const target_string: []const u8 = switch (arch) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; + var matcher = try TargetMatcher.init(allocator, target); + defer matcher.deinit(); var umbrella_libs = std.StringHashMap(void).init(allocator); defer umbrella_libs.deinit(); for (lib_stub.inner) |stub, stub_index| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub_index > 0) { // TODO I thought that we could switch on presence of `parent-umbrella` map; @@ -371,7 +422,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: if (stub.exports) |exports| { for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; + if (!matcher.matches(exp.targets)) continue; if (exp.symbols) |symbols| { for (symbols) |sym_name| { @@ -390,7 +441,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: if (stub.reexports) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; if (reexp.symbols) |symbols| { for (symbols) |sym_name| { @@ -418,11 +469,11 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: // TODO track which libs were already parsed in different steps for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub.reexported_libraries) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; for (reexp.libraries) |lib| { if (umbrella_libs.contains(lib)) { @@ -443,7 +494,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: pub fn parseDependentLibs( self: *Dylib, allocator: *Allocator, - arch: Arch, + target: std.Target, out: *std.ArrayList(Dylib), syslibroot: ?[]const u8, ) !void { @@ -475,7 +526,7 @@ pub fn parseDependentLibs( const dylibs = (try createAndParseFromPath( allocator, - arch, + target, full_path, .{ .id = id, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 2e6a20ad4b..c6aa2fb631 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -15,7 +15,6 @@ const segmentName = commands.segmentName; const sectionName = commands.sectionName; const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); const TextBlock = @import("TextBlock.zig"); @@ -154,7 +153,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { } } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Object { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -169,7 +168,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - object.parse(allocator, arch) catch |err| switch (err) { + object.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotObject => { object.deinit(allocator); return null; @@ -180,7 +179,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return object; } -pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); if (self.file_offset) |offset| { try reader.context.seekTo(offset); @@ -195,7 +194,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.NotObject; } - const this_arch: Arch = switch (header.cputype) { + const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => |value| { @@ -203,8 +202,8 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.UnsupportedCpuArchitecture; }, }; - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index f7a2fd3eda..6e75af08c4 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -43,6 +43,7 @@ pub const LoadCommand = union(enum) { Main: macho.entry_point_command, VersionMin: macho.version_min_command, SourceVersion: macho.source_version_command, + BuildVersion: GenericCommandWithData(macho.build_version_command), Uuid: macho.uuid_command, LinkeditData: macho.linkedit_data_command, Rpath: GenericCommandWithData(macho.rpath_command), @@ -97,6 +98,9 @@ pub const LoadCommand = union(enum) { macho.LC_SOURCE_VERSION => LoadCommand{ .SourceVersion = try stream.reader().readStruct(macho.source_version_command), }, + macho.LC_BUILD_VERSION => LoadCommand{ + .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()), + }, macho.LC_UUID => LoadCommand{ .Uuid = try stream.reader().readStruct(macho.uuid_command), }, @@ -129,6 +133,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.write(writer), .Dylib => |x| x.write(writer), .Rpath => |x| x.write(writer), + .BuildVersion => |x| x.write(writer), .Unknown => |x| x.write(writer), }; } @@ -147,6 +152,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmd, .Dylib => |x| x.inner.cmd, .Rpath => |x| x.inner.cmd, + .BuildVersion => |x| x.inner.cmd, .Unknown => |x| x.inner.cmd, }; } @@ -165,6 +171,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmdsize, .Dylib => |x| x.inner.cmdsize, .Rpath => |x| x.inner.cmdsize, + .BuildVersion => |x| x.inner.cmdsize, .Unknown => |x| x.inner.cmdsize, }; } @@ -175,6 +182,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |*x| x.deinit(allocator), .Dylib => |*x| x.deinit(allocator), .Rpath => |*x| x.deinit(allocator), + .BuildVersion => |*x| x.deinit(allocator), .Unknown => |*x| x.deinit(allocator), else => {}, }; @@ -193,6 +201,7 @@ pub const LoadCommand = union(enum) { .Main => |x| meta.eql(x, other.Main), .VersionMin => |x| meta.eql(x, other.VersionMin), .SourceVersion => |x| meta.eql(x, other.SourceVersion), + .BuildVersion => |x| x.eql(other.BuildVersion), .Uuid => |x| meta.eql(x, other.Uuid), .LinkeditData => |x| meta.eql(x, other.LinkeditData), .Segment => |x| x.eql(other.Segment), diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 61592c88a5..c911d89535 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -5,10 +5,8 @@ const macho = std.macho; const mem = std.mem; const native_endian = builtin.target.cpu.arch.endian(); -const Arch = std.Target.Cpu.Arch; - pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { - const arch: Arch = switch (cputype) { + const arch: std.Target.Cpu.Arch = switch (cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => { @@ -31,7 +29,7 @@ fn readFatStruct(reader: anytype, comptime T: type) !T { return res; } -pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { +pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 { const fat_header = try readFatStruct(reader, macho.fat_header); if (fat_header.magic != macho.FAT_MAGIC) return 0; @@ -44,12 +42,12 @@ pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { error.UnsupportedCpuArchitecture => continue, else => |e| return e, }; - if (lib_arch == arch) { + if (lib_arch == target.cpu.arch) { // We have found a matching architecture! return fat_arch.offset; } } else { - log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch}); + log.err("Could not find matching cpu architecture in fat library: expected {s}", .{target.cpu.arch}); return error.MismatchedCpuArchitecture; } } |
