aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-12-14 11:46:46 +0100
committerJakub Konka <kubkon@jakubkonka.com>2022-12-16 00:01:04 +0100
commitdb2052bc3588c1c52494eef32ef66c5cf3e09d96 (patch)
treeb730bf786f668980d0a16e058c708260a7c34ed9 /src
parent0d92fcf6a503780dcaadccef87e72824c7942a96 (diff)
downloadzig-db2052bc3588c1c52494eef32ef66c5cf3e09d96.tar.gz
zig-db2052bc3588c1c52494eef32ef66c5cf3e09d96.zip
macho: dedup LC emitting logic
Fix path written to `LC_ID_DYLIB` to include the current CWD (if any).
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig334
-rw-r--r--src/link/MachO/load_commands.zig325
-rw-r--r--src/link/MachO/zld.zig332
3 files changed, 369 insertions, 622 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 4a1ca9a357..f83338f48b 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -20,6 +20,7 @@ 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 target_util = @import("../target.zig");
const trace = @import("../tracy.zig").trace;
const zld = @import("MachO/zld.zig");
@@ -265,9 +266,6 @@ pub const SymbolWithLoc = struct {
/// actual_capacity + (actual_capacity / ideal_factor)
const ideal_factor = 3;
-/// Default path to dyld
-pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
-
/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
/// it as a possible place to put new symbols, it must have enough room for this many bytes
/// (plus extra for reserved capacity).
@@ -561,17 +559,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
var ncmds: u32 = 0;
try self.writeLinkeditSegmentData(&ncmds, lc_writer);
- try writeDylinkerLC(&ncmds, lc_writer);
-
- self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {
- error.MissingMainEntrypoint => {
- self.error_flags.no_entry_point_found = true;
- },
- else => |e| return e,
- };
+ try load_commands.writeDylinkerLC(&ncmds, lc_writer);
+
+ if (self.base.options.output_mode == .Exe) blk: {
+ const seg_id = self.header_segment_cmd_index.?;
+ const seg = self.segments.items[seg_id];
+ const global = self.getEntryPoint() catch |err| switch (err) {
+ error.MissingMainEntrypoint => {
+ self.error_flags.no_entry_point_found = true;
+ break :blk;
+ },
+ else => |e| return e,
+ };
+ const sym = self.getSymbol(global);
+ try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), &self.base.options, &ncmds, lc_writer);
+ }
- try self.writeDylibIdLC(&ncmds, lc_writer);
- try self.writeRpathLCs(&ncmds, lc_writer);
+ try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, &ncmds, lc_writer);
+ try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, &ncmds, lc_writer);
{
try lc_writer.writeStruct(macho.source_version_command{
@@ -581,7 +586,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
ncmds += 1;
}
- try self.writeBuildVersionLC(&ncmds, lc_writer);
+ try load_commands.writeBuildVersionLC(&self.base.options, &ncmds, lc_writer);
{
std.crypto.random.bytes(&self.uuid.uuid);
@@ -589,7 +594,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
ncmds += 1;
}
- try self.writeLoadDylibLCs(&ncmds, lc_writer);
+ try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), &ncmds, lc_writer);
const target = self.base.options.target;
const requires_codesig = blk: {
@@ -1702,195 +1707,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
try self.writePtrWidthAtom(got_atom);
}
-pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
- const name_len = mem.sliceTo(default_dyld_path, 0).len;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.dylinker_command) + name_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.dylinker_command{
- .cmd = .LOAD_DYLINKER,
- .cmdsize = cmdsize,
- .name = @sizeOf(macho.dylinker_command),
- });
- try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
- const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
-}
-
-pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
- if (self.base.options.output_mode != .Exe) return;
- const seg_id = self.header_segment_cmd_index.?;
- const seg = self.segments.items[seg_id];
- const global = try self.getEntryPoint();
- const sym = self.getSymbol(global);
- try lc_writer.writeStruct(macho.entry_point_command{
- .cmd = .MAIN,
- .cmdsize = @sizeOf(macho.entry_point_command),
- .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
- .stacksize = self.base.options.stack_size_override orelse 0,
- });
- ncmds.* += 1;
-}
-
-const WriteDylibLCCtx = struct {
- cmd: macho.LC,
- name: []const u8,
- timestamp: u32 = 2,
- current_version: u32 = 0x10000,
- compatibility_version: u32 = 0x10000,
-};
-
-pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
- const name_len = ctx.name.len + 1;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.dylib_command) + name_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.dylib_command{
- .cmd = ctx.cmd,
- .cmdsize = cmdsize,
- .dylib = .{
- .name = @sizeOf(macho.dylib_command),
- .timestamp = ctx.timestamp,
- .current_version = ctx.current_version,
- .compatibility_version = ctx.compatibility_version,
- },
- });
- try lc_writer.writeAll(ctx.name);
- try lc_writer.writeByte(0);
- const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
-}
-
-pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
- if (self.base.options.output_mode != .Lib) return;
- const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
- const curr = self.base.options.version orelse std.builtin.Version{
- .major = 1,
- .minor = 0,
- .patch = 0,
- };
- const compat = self.base.options.compatibility_version orelse std.builtin.Version{
- .major = 1,
- .minor = 0,
- .patch = 0,
- };
- try writeDylibLC(.{
- .cmd = .ID_DYLIB,
- .name = install_name,
- .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
- .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
- }, ncmds, lc_writer);
-}
-
-const RpathIterator = struct {
- buffer: []const []const u8,
- table: std.StringHashMap(void),
- count: usize = 0,
-
- fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
- return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
- }
-
- fn deinit(it: *RpathIterator) void {
- it.table.deinit();
- }
-
- fn next(it: *RpathIterator) !?[]const u8 {
- while (true) {
- if (it.count >= it.buffer.len) return null;
- const rpath = it.buffer[it.count];
- it.count += 1;
- const gop = try it.table.getOrPut(rpath);
- if (gop.found_existing) continue;
- return rpath;
- }
- }
-};
-
-pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
- const gpa = self.base.allocator;
-
- var it = RpathIterator.init(gpa, self.base.options.rpath_list);
- defer it.deinit();
-
- while (try it.next()) |rpath| {
- const rpath_len = rpath.len + 1;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.rpath_command) + rpath_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.rpath_command{
- .cmdsize = cmdsize,
- .path = @sizeOf(macho.rpath_command),
- });
- try lc_writer.writeAll(rpath);
- try lc_writer.writeByte(0);
- const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
- }
-}
-
-pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
- const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
- const platform_version = blk: {
- const ver = self.base.options.target.os.version_range.semver.min;
- const platform_version = ver.major << 16 | ver.minor << 8;
- break :blk platform_version;
- };
- const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
- const ver = sdk.version;
- const sdk_version = ver.major << 16 | ver.minor << 8;
- break :blk sdk_version;
- } else platform_version;
- const is_simulator_abi = self.base.options.target.abi == .simulator;
- try lc_writer.writeStruct(macho.build_version_command{
- .cmdsize = cmdsize,
- .platform = switch (self.base.options.target.os.tag) {
- .macos => .MACOS,
- .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
- .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
- .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
- else => unreachable,
- },
- .minos = platform_version,
- .sdk = sdk_version,
- .ntools = 1,
- });
- try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
- .tool = .LD,
- .version = 0x0,
- }));
- ncmds.* += 1;
-}
-
-pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
- for (self.referenced_dylibs.keys()) |id| {
- const dylib = self.dylibs.items[id];
- const dylib_id = dylib.id orelse unreachable;
- try writeDylibLC(.{
- .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
- .name = dylib_id.name,
- .timestamp = dylib_id.timestamp,
- .current_version = dylib_id.current_version,
- .compatibility_version = dylib_id.compatibility_version,
- }, ncmds, lc_writer);
- }
-}
-
pub fn deinit(self: *MachO) void {
const gpa = self.base.allocator;
@@ -2976,98 +2792,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
}
}
-pub inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
- const darwin_path_max = 1024;
- const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
- return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
-}
-
-fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
- const gpa = self.base.allocator;
- var sizeofcmds: u64 = 0;
- for (self.segments.items) |seg| {
- sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
- }
-
- // LC_DYLD_INFO_ONLY
- sizeofcmds += @sizeOf(macho.dyld_info_command);
- // LC_FUNCTION_STARTS
- if (self.text_section_index != null) {
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- }
- // LC_DATA_IN_CODE
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- // LC_SYMTAB
- sizeofcmds += @sizeOf(macho.symtab_command);
- // LC_DYSYMTAB
- sizeofcmds += @sizeOf(macho.dysymtab_command);
- // LC_LOAD_DYLINKER
- sizeofcmds += calcInstallNameLen(
- @sizeOf(macho.dylinker_command),
- mem.sliceTo(default_dyld_path, 0),
- false,
- );
- // LC_MAIN
- if (self.base.options.output_mode == .Exe) {
- sizeofcmds += @sizeOf(macho.entry_point_command);
- }
- // LC_ID_DYLIB
- if (self.base.options.output_mode == .Lib) {
- sizeofcmds += blk: {
- const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
- break :blk calcInstallNameLen(
- @sizeOf(macho.dylib_command),
- install_name,
- assume_max_path_len,
- );
- };
- }
- // LC_RPATH
- {
- var it = RpathIterator.init(gpa, self.base.options.rpath_list);
- defer it.deinit();
- while (try it.next()) |rpath| {
- sizeofcmds += calcInstallNameLen(
- @sizeOf(macho.rpath_command),
- rpath,
- assume_max_path_len,
- );
- }
- }
- // LC_SOURCE_VERSION
- sizeofcmds += @sizeOf(macho.source_version_command);
- // LC_BUILD_VERSION
- sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
- // LC_UUID
- sizeofcmds += @sizeOf(macho.uuid_command);
- // LC_LOAD_DYLIB
- for (self.referenced_dylibs.keys()) |id| {
- const dylib = self.dylibs.items[id];
- const dylib_id = dylib.id orelse unreachable;
- sizeofcmds += calcInstallNameLen(
- @sizeOf(macho.dylib_command),
- dylib_id.name,
- assume_max_path_len,
- );
- }
- // LC_CODE_SIGNATURE
- {
- const target = self.base.options.target;
- const requires_codesig = blk: {
- if (self.base.options.entitlements) |_| break :blk true;
- if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
- break :blk true;
- break :blk false;
- };
- if (requires_codesig) {
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- }
- }
-
- return @intCast(u32, sizeofcmds);
-}
-
-pub fn calcPagezeroSize(self: *MachO) u64 {
+fn calcPagezeroSize(self: *MachO) u64 {
const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
if (self.base.options.output_mode == .Lib) return 0;
@@ -3079,23 +2804,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {
return aligned_pagezero_vmsize;
}
-pub fn calcMinHeaderPad(self: *MachO) !u64 {
- var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
- log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
-
- if (self.base.options.headerpad_max_install_names) {
- var min_headerpad_size: u32 = try self.calcLCsSize(true);
- log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
- min_headerpad_size + @sizeOf(macho.mach_header_64),
- });
- padding = @max(padding, min_headerpad_size);
- }
- const offset = @sizeOf(macho.mach_header_64) + padding;
- log.debug("actual headerpad size 0x{x}", .{offset});
-
- return offset;
-}
-
fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
size: u64 = 0,
alignment: u32 = 0,
diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig
new file mode 100644
index 0000000000..9b0e61e919
--- /dev/null
+++ b/src/link/MachO/load_commands.zig
@@ -0,0 +1,325 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const link = @import("../../link.zig");
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Dylib = @import("Dylib.zig");
+
+pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
+
+fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
+ const darwin_path_max = 1024;
+ const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
+ return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
+}
+
+const CalcLCsSizeCtx = struct {
+ segments: []const macho.segment_command_64,
+ dylibs: []const Dylib,
+ referenced_dylibs: []u16,
+ wants_function_starts: bool = true,
+};
+
+fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 {
+ var has_text_segment: bool = false;
+ var sizeofcmds: u64 = 0;
+ for (ctx.segments) |seg| {
+ sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
+ if (mem.eql(u8, seg.segName(), "__TEXT")) {
+ has_text_segment = true;
+ }
+ }
+
+ // LC_DYLD_INFO_ONLY
+ sizeofcmds += @sizeOf(macho.dyld_info_command);
+ // LC_FUNCTION_STARTS
+ if (has_text_segment and ctx.wants_function_starts) |_| {
+ sizeofcmds += @sizeOf(macho.linkedit_data_command);
+ }
+ // LC_DATA_IN_CODE
+ sizeofcmds += @sizeOf(macho.linkedit_data_command);
+ // LC_SYMTAB
+ sizeofcmds += @sizeOf(macho.symtab_command);
+ // LC_DYSYMTAB
+ sizeofcmds += @sizeOf(macho.dysymtab_command);
+ // LC_LOAD_DYLINKER
+ sizeofcmds += calcInstallNameLen(
+ @sizeOf(macho.dylinker_command),
+ mem.sliceTo(default_dyld_path, 0),
+ false,
+ );
+ // LC_MAIN
+ if (options.output_mode == .Exe) {
+ sizeofcmds += @sizeOf(macho.entry_point_command);
+ }
+ // LC_ID_DYLIB
+ if (options.output_mode == .Lib and options.link_mode == .Dynamic) {
+ sizeofcmds += blk: {
+ const emit = options.emit.?;
+ const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
+ defer if (options.install_name == null) gpa.free(install_name);
+ break :blk calcInstallNameLen(
+ @sizeOf(macho.dylib_command),
+ install_name,
+ assume_max_path_len,
+ );
+ };
+ }
+ // LC_RPATH
+ {
+ var it = RpathIterator.init(gpa, options.rpath_list);
+ defer it.deinit();
+ while (try it.next()) |rpath| {
+ sizeofcmds += calcInstallNameLen(
+ @sizeOf(macho.rpath_command),
+ rpath,
+ assume_max_path_len,
+ );
+ }
+ }
+ // LC_SOURCE_VERSION
+ sizeofcmds += @sizeOf(macho.source_version_command);
+ // LC_BUILD_VERSION
+ sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
+ // LC_UUID
+ sizeofcmds += @sizeOf(macho.uuid_command);
+ // LC_LOAD_DYLIB
+ for (ctx.referenced_dylibs) |id| {
+ const dylib = ctx.dylibs[id];
+ const dylib_id = dylib.id orelse unreachable;
+ sizeofcmds += calcInstallNameLen(
+ @sizeOf(macho.dylib_command),
+ dylib_id.name,
+ assume_max_path_len,
+ );
+ }
+ // LC_CODE_SIGNATURE
+ {
+ const target = options.target;
+ const requires_codesig = blk: {
+ if (options.entitlements) |_| break :blk true;
+ if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
+ break :blk true;
+ break :blk false;
+ };
+ if (requires_codesig) {
+ sizeofcmds += @sizeOf(macho.linkedit_data_command);
+ }
+ }
+
+ return @intCast(u32, sizeofcmds);
+}
+
+pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 {
+ var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0);
+ log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
+
+ if (options.headerpad_max_install_names) {
+ var min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true);
+ log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
+ min_headerpad_size + @sizeOf(macho.mach_header_64),
+ });
+ padding = @max(padding, min_headerpad_size);
+ }
+
+ const offset = @sizeOf(macho.mach_header_64) + padding;
+ log.debug("actual headerpad size 0x{x}", .{offset});
+
+ return offset;
+}
+
+pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
+ const name_len = mem.sliceTo(default_dyld_path, 0).len;
+ const cmdsize = @intCast(u32, mem.alignForwardGeneric(
+ u64,
+ @sizeOf(macho.dylinker_command) + name_len,
+ @sizeOf(u64),
+ ));
+ try lc_writer.writeStruct(macho.dylinker_command{
+ .cmd = .LOAD_DYLINKER,
+ .cmdsize = cmdsize,
+ .name = @sizeOf(macho.dylinker_command),
+ });
+ try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
+ const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
+ if (padding > 0) {
+ try lc_writer.writeByteNTimes(0, padding);
+ }
+ ncmds.* += 1;
+}
+
+const WriteDylibLCCtx = struct {
+ cmd: macho.LC,
+ name: []const u8,
+ timestamp: u32 = 2,
+ current_version: u32 = 0x10000,
+ compatibility_version: u32 = 0x10000,
+};
+
+fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
+ const name_len = ctx.name.len + 1;
+ const cmdsize = @intCast(u32, mem.alignForwardGeneric(
+ u64,
+ @sizeOf(macho.dylib_command) + name_len,
+ @sizeOf(u64),
+ ));
+ try lc_writer.writeStruct(macho.dylib_command{
+ .cmd = ctx.cmd,
+ .cmdsize = cmdsize,
+ .dylib = .{
+ .name = @sizeOf(macho.dylib_command),
+ .timestamp = ctx.timestamp,
+ .current_version = ctx.current_version,
+ .compatibility_version = ctx.compatibility_version,
+ },
+ });
+ try lc_writer.writeAll(ctx.name);
+ try lc_writer.writeByte(0);
+ const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
+ if (padding > 0) {
+ try lc_writer.writeByteNTimes(0, padding);
+ }
+ ncmds.* += 1;
+}
+
+pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
+ assert(options.output_mode == .Lib and options.link_mode == .Dynamic);
+ const emit = options.emit.?;
+ const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
+ defer if (options.install_name == null) gpa.free(install_name);
+ const curr = options.version orelse std.builtin.Version{
+ .major = 1,
+ .minor = 0,
+ .patch = 0,
+ };
+ const compat = options.compatibility_version orelse std.builtin.Version{
+ .major = 1,
+ .minor = 0,
+ .patch = 0,
+ };
+ try writeDylibLC(.{
+ .cmd = .ID_DYLIB,
+ .name = install_name,
+ .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
+ .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
+ }, ncmds, lc_writer);
+}
+
+pub fn writeMainLC(entryoff: u32, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
+ assert(options.output_mode == .Exe);
+ try lc_writer.writeStruct(macho.entry_point_command{
+ .cmd = .MAIN,
+ .cmdsize = @sizeOf(macho.entry_point_command),
+ .entryoff = entryoff,
+ .stacksize = options.stack_size_override orelse 0,
+ });
+ ncmds.* += 1;
+}
+
+const RpathIterator = struct {
+ buffer: []const []const u8,
+ table: std.StringHashMap(void),
+ count: usize = 0,
+
+ fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
+ return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
+ }
+
+ fn deinit(it: *RpathIterator) void {
+ it.table.deinit();
+ }
+
+ fn next(it: *RpathIterator) !?[]const u8 {
+ while (true) {
+ if (it.count >= it.buffer.len) return null;
+ const rpath = it.buffer[it.count];
+ it.count += 1;
+ const gop = try it.table.getOrPut(rpath);
+ if (gop.found_existing) continue;
+ return rpath;
+ }
+ }
+};
+
+pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
+ var it = RpathIterator.init(gpa, options.rpath_list);
+ defer it.deinit();
+
+ while (try it.next()) |rpath| {
+ const rpath_len = rpath.len + 1;
+ const cmdsize = @intCast(u32, mem.alignForwardGeneric(
+ u64,
+ @sizeOf(macho.rpath_command) + rpath_len,
+ @sizeOf(u64),
+ ));
+ try lc_writer.writeStruct(macho.rpath_command{
+ .cmdsize = cmdsize,
+ .path = @sizeOf(macho.rpath_command),
+ });
+ try lc_writer.writeAll(rpath);
+ try lc_writer.writeByte(0);
+ const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
+ if (padding > 0) {
+ try lc_writer.writeByteNTimes(0, padding);
+ }
+ ncmds.* += 1;
+ }
+}
+
+pub fn writeBuildVersionLC(options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
+ const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
+ const platform_version = blk: {
+ const ver = options.target.os.version_range.semver.min;
+ const platform_version = ver.major << 16 | ver.minor << 8;
+ break :blk platform_version;
+ };
+ const sdk_version = if (options.native_darwin_sdk) |sdk| blk: {
+ const ver = sdk.version;
+ const sdk_version = ver.major << 16 | ver.minor << 8;
+ break :blk sdk_version;
+ } else platform_version;
+ const is_simulator_abi = options.target.abi == .simulator;
+ try lc_writer.writeStruct(macho.build_version_command{
+ .cmdsize = cmdsize,
+ .platform = switch (options.target.os.tag) {
+ .macos => .MACOS,
+ .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
+ .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
+ .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
+ else => unreachable,
+ },
+ .minos = platform_version,
+ .sdk = sdk_version,
+ .ntools = 1,
+ });
+ try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
+ .tool = .LD,
+ .version = 0x0,
+ }));
+ ncmds.* += 1;
+}
+
+pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, ncmds: *u32, lc_writer: anytype) !void {
+ for (referenced) |index| {
+ const dylib = dylibs[index];
+ const dylib_id = dylib.id orelse unreachable;
+ try writeDylibLC(.{
+ .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
+ .name = dylib_id.name,
+ .timestamp = dylib_id.timestamp,
+ .current_version = dylib_id.current_version,
+ .compatibility_version = dylib_id.compatibility_version,
+ }, ncmds, lc_writer);
+ }
+}
+
+pub fn writeSourceVersionLC(ncmds: *u32, lc_writer: anytype) !void {
+ try lc_writer.writeStruct(macho.source_version_command{
+ .cmdsize = @sizeOf(macho.source_version_command),
+ .version = 0x0,
+ });
+ ncmds.* += 1;
+}
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index 9baecd326a..7bffc10e85 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -13,6 +13,7 @@ const bind = @import("bind.zig");
const dead_strip = @import("dead_strip.zig");
const fat = @import("fat.zig");
const link = @import("../../link.zig");
+const load_commands = @import("load_commands.zig");
const thunks = @import("thunks.zig");
const trace = @import("../../tracy.zig").trace;
@@ -34,7 +35,7 @@ pub const Zld = struct {
gpa: Allocator,
file: fs.File,
page_size: u16,
- options: link.Options,
+ options: *const link.Options,
objects: std.ArrayListUnmanaged(Object) = .{},
archives: std.ArrayListUnmanaged(Archive) = .{},
@@ -1227,195 +1228,6 @@ pub const Zld = struct {
}
}
- fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
- const name_len = mem.sliceTo(MachO.default_dyld_path, 0).len;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.dylinker_command) + name_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.dylinker_command{
- .cmd = .LOAD_DYLINKER,
- .cmdsize = cmdsize,
- .name = @sizeOf(macho.dylinker_command),
- });
- try lc_writer.writeAll(mem.sliceTo(MachO.default_dyld_path, 0));
- const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
- }
-
- fn writeMainLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- if (self.options.output_mode != .Exe) return;
- const seg_id = self.getSegmentByName("__TEXT").?;
- const seg = self.segments.items[seg_id];
- const global = self.getEntryPoint();
- const sym = self.getSymbol(global);
- try lc_writer.writeStruct(macho.entry_point_command{
- .cmd = .MAIN,
- .cmdsize = @sizeOf(macho.entry_point_command),
- .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
- .stacksize = self.options.stack_size_override orelse 0,
- });
- ncmds.* += 1;
- }
-
- const WriteDylibLCCtx = struct {
- cmd: macho.LC,
- name: []const u8,
- timestamp: u32 = 2,
- current_version: u32 = 0x10000,
- compatibility_version: u32 = 0x10000,
- };
-
- fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
- const name_len = ctx.name.len + 1;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.dylib_command) + name_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.dylib_command{
- .cmd = ctx.cmd,
- .cmdsize = cmdsize,
- .dylib = .{
- .name = @sizeOf(macho.dylib_command),
- .timestamp = ctx.timestamp,
- .current_version = ctx.current_version,
- .compatibility_version = ctx.compatibility_version,
- },
- });
- try lc_writer.writeAll(ctx.name);
- try lc_writer.writeByte(0);
- const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
- }
-
- fn writeDylibIdLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- if (self.options.output_mode != .Lib) return;
- const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
- const curr = self.options.version orelse std.builtin.Version{
- .major = 1,
- .minor = 0,
- .patch = 0,
- };
- const compat = self.options.compatibility_version orelse std.builtin.Version{
- .major = 1,
- .minor = 0,
- .patch = 0,
- };
- try writeDylibLC(.{
- .cmd = .ID_DYLIB,
- .name = install_name,
- .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
- .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
- }, ncmds, lc_writer);
- }
-
- const RpathIterator = struct {
- buffer: []const []const u8,
- table: std.StringHashMap(void),
- count: usize = 0,
-
- fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
- return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
- }
-
- fn deinit(it: *RpathIterator) void {
- it.table.deinit();
- }
-
- fn next(it: *RpathIterator) !?[]const u8 {
- while (true) {
- if (it.count >= it.buffer.len) return null;
- const rpath = it.buffer[it.count];
- it.count += 1;
- const gop = try it.table.getOrPut(rpath);
- if (gop.found_existing) continue;
- return rpath;
- }
- }
- };
-
- fn writeRpathLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- const gpa = self.gpa;
-
- var it = RpathIterator.init(gpa, self.options.rpath_list);
- defer it.deinit();
-
- while (try it.next()) |rpath| {
- const rpath_len = rpath.len + 1;
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(macho.rpath_command) + rpath_len,
- @sizeOf(u64),
- ));
- try lc_writer.writeStruct(macho.rpath_command{
- .cmdsize = cmdsize,
- .path = @sizeOf(macho.rpath_command),
- });
- try lc_writer.writeAll(rpath);
- try lc_writer.writeByte(0);
- const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
- if (padding > 0) {
- try lc_writer.writeByteNTimes(0, padding);
- }
- ncmds.* += 1;
- }
- }
-
- fn writeBuildVersionLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
- const platform_version = blk: {
- const ver = self.options.target.os.version_range.semver.min;
- const platform_version = ver.major << 16 | ver.minor << 8;
- break :blk platform_version;
- };
- const sdk_version = if (self.options.native_darwin_sdk) |sdk| blk: {
- const ver = sdk.version;
- const sdk_version = ver.major << 16 | ver.minor << 8;
- break :blk sdk_version;
- } else platform_version;
- const is_simulator_abi = self.options.target.abi == .simulator;
- try lc_writer.writeStruct(macho.build_version_command{
- .cmdsize = cmdsize,
- .platform = switch (self.options.target.os.tag) {
- .macos => .MACOS,
- .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
- .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
- .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
- else => unreachable,
- },
- .minos = platform_version,
- .sdk = sdk_version,
- .ntools = 1,
- });
- try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
- .tool = .LD,
- .version = 0x0,
- }));
- ncmds.* += 1;
- }
-
- fn writeLoadDylibLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- for (self.referenced_dylibs.keys()) |id| {
- const dylib = self.dylibs.items[id];
- const dylib_id = dylib.id orelse unreachable;
- try writeDylibLC(.{
- .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
- .name = dylib_id.name,
- .timestamp = dylib_id.timestamp,
- .current_version = dylib_id.current_version,
- .compatibility_version = dylib_id.compatibility_version,
- }, ncmds, lc_writer);
- }
- }
-
pub fn deinit(self: *Zld) void {
const gpa = self.gpa;
@@ -1516,110 +1328,6 @@ pub const Zld = struct {
}
}
- fn calcLCsSize(self: *Zld, assume_max_path_len: bool) !u32 {
- const gpa = self.gpa;
-
- var sizeofcmds: u64 = 0;
- for (self.segments.items) |seg| {
- sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
- }
-
- // LC_DYLD_INFO_ONLY
- sizeofcmds += @sizeOf(macho.dyld_info_command);
- // LC_FUNCTION_STARTS
- if (self.getSectionByName("__TEXT", "__text")) |_| {
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- }
- // LC_DATA_IN_CODE
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- // LC_SYMTAB
- sizeofcmds += @sizeOf(macho.symtab_command);
- // LC_DYSYMTAB
- sizeofcmds += @sizeOf(macho.dysymtab_command);
- // LC_LOAD_DYLINKER
- sizeofcmds += MachO.calcInstallNameLen(
- @sizeOf(macho.dylinker_command),
- mem.sliceTo(MachO.default_dyld_path, 0),
- false,
- );
- // LC_MAIN
- if (self.options.output_mode == .Exe) {
- sizeofcmds += @sizeOf(macho.entry_point_command);
- }
- // LC_ID_DYLIB
- if (self.options.output_mode == .Lib) {
- sizeofcmds += blk: {
- const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
- break :blk MachO.calcInstallNameLen(
- @sizeOf(macho.dylib_command),
- install_name,
- assume_max_path_len,
- );
- };
- }
- // LC_RPATH
- {
- var it = RpathIterator.init(gpa, self.options.rpath_list);
- defer it.deinit();
- while (try it.next()) |rpath| {
- sizeofcmds += MachO.calcInstallNameLen(
- @sizeOf(macho.rpath_command),
- rpath,
- assume_max_path_len,
- );
- }
- }
- // LC_SOURCE_VERSION
- sizeofcmds += @sizeOf(macho.source_version_command);
- // LC_BUILD_VERSION
- sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
- // LC_UUID
- sizeofcmds += @sizeOf(macho.uuid_command);
- // LC_LOAD_DYLIB
- for (self.referenced_dylibs.keys()) |id| {
- const dylib = self.dylibs.items[id];
- const dylib_id = dylib.id orelse unreachable;
- sizeofcmds += MachO.calcInstallNameLen(
- @sizeOf(macho.dylib_command),
- dylib_id.name,
- assume_max_path_len,
- );
- }
- // LC_CODE_SIGNATURE
- {
- const target = self.options.target;
- const requires_codesig = blk: {
- if (self.options.entitlements) |_| break :blk true;
- if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
- break :blk true;
- break :blk false;
- };
- if (requires_codesig) {
- sizeofcmds += @sizeOf(macho.linkedit_data_command);
- }
- }
-
- return @intCast(u32, sizeofcmds);
- }
-
- fn calcMinHeaderPad(self: *Zld) !u64 {
- var padding: u32 = (try self.calcLCsSize(false)) + (self.options.headerpad_size orelse 0);
- log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
-
- if (self.options.headerpad_max_install_names) {
- var min_headerpad_size: u32 = try self.calcLCsSize(true);
- log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
- min_headerpad_size + @sizeOf(macho.mach_header_64),
- });
- padding = @max(padding, min_headerpad_size);
- }
-
- const offset = @sizeOf(macho.mach_header_64) + padding;
- log.debug("actual headerpad size 0x{x}", .{offset});
-
- return offset;
- }
-
pub fn allocateSymbol(self: *Zld) !u32 {
try self.locals.ensureUnusedCapacity(self.gpa, 1);
log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
@@ -1842,7 +1550,11 @@ pub const Zld = struct {
fn allocateSegments(self: *Zld) !void {
for (self.segments.items) |*segment, segment_index| {
const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
- const base_size = if (is_text_segment) try self.calcMinHeaderPad() else 0;
+ const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
+ .segments = self.segments.items,
+ .dylibs = self.dylibs.items,
+ .referenced_dylibs = self.referenced_dylibs.keys(),
+ }) else 0;
try self.allocateSegment(@intCast(u8, segment_index), base_size);
}
}
@@ -3734,7 +3446,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
defer tracy.end();
const gpa = macho_file.base.allocator;
- const options = macho_file.base.options;
+ const options = &macho_file.base.options;
const target = options.target;
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
@@ -3884,7 +3596,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
macho_file.base.file = try directory.handle.createFile(sub_path, .{
.truncate = true,
.read = true,
- .mode = link.determineMode(options),
+ .mode = link.determineMode(options.*),
});
}
var zld = Zld{
@@ -4301,20 +4013,22 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
}
}
- try Zld.writeDylinkerLC(&ncmds, lc_writer);
- try zld.writeMainLC(&ncmds, lc_writer);
- try zld.writeDylibIdLC(&ncmds, lc_writer);
- try zld.writeRpathLCs(&ncmds, lc_writer);
+ try load_commands.writeDylinkerLC(&ncmds, lc_writer);
- {
- try lc_writer.writeStruct(macho.source_version_command{
- .cmdsize = @sizeOf(macho.source_version_command),
- .version = 0x0,
- });
- ncmds += 1;
+ if (zld.options.output_mode == .Exe) {
+ const seg_id = zld.getSegmentByName("__TEXT").?;
+ const seg = zld.segments.items[seg_id];
+ const global = zld.getEntryPoint();
+ const sym = zld.getSymbol(global);
+ try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), options, &ncmds, lc_writer);
+ } else {
+ assert(zld.options.output_mode == .Lib);
+ try load_commands.writeDylibIdLC(zld.gpa, zld.options, &ncmds, lc_writer);
}
- try zld.writeBuildVersionLC(&ncmds, lc_writer);
+ try load_commands.writeRpathLCs(zld.gpa, zld.options, &ncmds, lc_writer);
+ try load_commands.writeSourceVersionLC(&ncmds, lc_writer);
+ try load_commands.writeBuildVersionLC(zld.options, &ncmds, lc_writer);
{
var uuid_lc = macho.uuid_command{
@@ -4326,7 +4040,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
ncmds += 1;
}
- try zld.writeLoadDylibLCs(&ncmds, lc_writer);
+ try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), &ncmds, lc_writer);
const requires_codesig = blk: {
if (options.entitlements) |_| break :blk true;