aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-08-11 19:36:21 +0200
committerGitHub <noreply@github.com>2021-08-11 19:36:21 +0200
commit60a5552d414ffedf84117df57963fd5bf099c2ea (patch)
tree586178dc0f24bf9875e58a830af1e2fdde51af2f /src/link/MachO
parentf2bf1390a29a9decaa5ca49d3ae720b360583b35 (diff)
parent509fe33d10e4e89a351678f4d466f30a7870ebcf (diff)
downloadzig-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.zig13
-rw-r--r--src/link/MachO/Dylib.zig97
-rw-r--r--src/link/MachO/Object.zig13
-rw-r--r--src/link/MachO/commands.zig9
-rw-r--r--src/link/MachO/fat.zig10
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;
}
}