authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-14 11:46:46+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-16 00:01:04+01:00
logdb2052bc3588c1c52494eef32ef66c5cf3e09d96
treeb730bf786f668980d0a16e058c708260a7c34ed9
parent0d92fcf6a503780dcaadccef87e72824c7942a96

macho: dedup LC emitting logic

Fix path written to `LC_ID_DYLIB` to include the current CWD (if any).

4 files changed, 372 insertions(+), 622 deletions(-)

CMakeLists.txt+3
...@@ -585,10 +585,13 @@ set(ZIG_STAGE2_SOURCES...@@ -585,10 +585,13 @@ set(ZIG_STAGE2_SOURCES
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"585 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"590 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"591 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"595 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"596 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"597 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
src/link/MachO.zig+21-313
...@@ -20,6 +20,7 @@ const dead_strip = @import("MachO/dead_strip.zig");...@@ -20,6 +20,7 @@ const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");20const fat = @import("MachO/fat.zig");
21const link = @import("../link.zig");21const link = @import("../link.zig");
22const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
23const load_commands = @import("MachO/load_commands.zig");
23const target_util = @import("../target.zig");24const target_util = @import("../target.zig");
24const trace = @import("../tracy.zig").trace;25const trace = @import("../tracy.zig").trace;
25const zld = @import("MachO/zld.zig");26const zld = @import("MachO/zld.zig");
...@@ -265,9 +266,6 @@ pub const SymbolWithLoc = struct {...@@ -265,9 +266,6 @@ pub const SymbolWithLoc = struct {
265/// actual_capacity + (actual_capacity / ideal_factor)266/// actual_capacity + (actual_capacity / ideal_factor)
266const ideal_factor = 3;267const ideal_factor = 3;
267268
268/// Default path to dyld
269pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
270
271/// In order for a slice of bytes to be considered eligible to keep metadata pointing at269/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
272/// it as a possible place to put new symbols, it must have enough room for this many bytes270/// it as a possible place to put new symbols, it must have enough room for this many bytes
273/// (plus extra for reserved capacity).271/// (plus extra for reserved capacity).
...@@ -561,17 +559,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -561,17 +559,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
561 var ncmds: u32 = 0;559 var ncmds: u32 = 0;
562560
563 try self.writeLinkeditSegmentData(&ncmds, lc_writer);561 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
564 try writeDylinkerLC(&ncmds, lc_writer);562 try load_commands.writeDylinkerLC(&ncmds, lc_writer);
565563
566 self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {564 if (self.base.options.output_mode == .Exe) blk: {
567 error.MissingMainEntrypoint => {565 const seg_id = self.header_segment_cmd_index.?;
568 self.error_flags.no_entry_point_found = true;566 const seg = self.segments.items[seg_id];
569 },567 const global = self.getEntryPoint() catch |err| switch (err) {
570 else => |e| return e,568 error.MissingMainEntrypoint => {
571 };569 self.error_flags.no_entry_point_found = true;
570 break :blk;
571 },
572 else => |e| return e,
573 };
574 const sym = self.getSymbol(global);
575 try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), &self.base.options, &ncmds, lc_writer);
576 }
572577
573 try self.writeDylibIdLC(&ncmds, lc_writer);578 try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, &ncmds, lc_writer);
574 try self.writeRpathLCs(&ncmds, lc_writer);579 try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, &ncmds, lc_writer);
575580
576 {581 {
577 try lc_writer.writeStruct(macho.source_version_command{582 try lc_writer.writeStruct(macho.source_version_command{
...@@ -581,7 +586,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -581,7 +586,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
581 ncmds += 1;586 ncmds += 1;
582 }587 }
583588
584 try self.writeBuildVersionLC(&ncmds, lc_writer);589 try load_commands.writeBuildVersionLC(&self.base.options, &ncmds, lc_writer);
585590
586 {591 {
587 std.crypto.random.bytes(&self.uuid.uuid);592 std.crypto.random.bytes(&self.uuid.uuid);
...@@ -589,7 +594,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -589,7 +594,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
589 ncmds += 1;594 ncmds += 1;
590 }595 }
591596
592 try self.writeLoadDylibLCs(&ncmds, lc_writer);597 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), &ncmds, lc_writer);
593598
594 const target = self.base.options.target;599 const target = self.base.options.target;
595 const requires_codesig = blk: {600 const requires_codesig = blk: {
...@@ -1702,195 +1707,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -1702,195 +1707,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
1702 try self.writePtrWidthAtom(got_atom);1707 try self.writePtrWidthAtom(got_atom);
1703}1708}
17041709
1705pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1706 const name_len = mem.sliceTo(default_dyld_path, 0).len;
1707 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1708 u64,
1709 @sizeOf(macho.dylinker_command) + name_len,
1710 @sizeOf(u64),
1711 ));
1712 try lc_writer.writeStruct(macho.dylinker_command{
1713 .cmd = .LOAD_DYLINKER,
1714 .cmdsize = cmdsize,
1715 .name = @sizeOf(macho.dylinker_command),
1716 });
1717 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
1718 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1719 if (padding > 0) {
1720 try lc_writer.writeByteNTimes(0, padding);
1721 }
1722 ncmds.* += 1;
1723}
1724
1725pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1726 if (self.base.options.output_mode != .Exe) return;
1727 const seg_id = self.header_segment_cmd_index.?;
1728 const seg = self.segments.items[seg_id];
1729 const global = try self.getEntryPoint();
1730 const sym = self.getSymbol(global);
1731 try lc_writer.writeStruct(macho.entry_point_command{
1732 .cmd = .MAIN,
1733 .cmdsize = @sizeOf(macho.entry_point_command),
1734 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1735 .stacksize = self.base.options.stack_size_override orelse 0,
1736 });
1737 ncmds.* += 1;
1738}
1739
1740const WriteDylibLCCtx = struct {
1741 cmd: macho.LC,
1742 name: []const u8,
1743 timestamp: u32 = 2,
1744 current_version: u32 = 0x10000,
1745 compatibility_version: u32 = 0x10000,
1746};
1747
1748pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1749 const name_len = ctx.name.len + 1;
1750 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1751 u64,
1752 @sizeOf(macho.dylib_command) + name_len,
1753 @sizeOf(u64),
1754 ));
1755 try lc_writer.writeStruct(macho.dylib_command{
1756 .cmd = ctx.cmd,
1757 .cmdsize = cmdsize,
1758 .dylib = .{
1759 .name = @sizeOf(macho.dylib_command),
1760 .timestamp = ctx.timestamp,
1761 .current_version = ctx.current_version,
1762 .compatibility_version = ctx.compatibility_version,
1763 },
1764 });
1765 try lc_writer.writeAll(ctx.name);
1766 try lc_writer.writeByte(0);
1767 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1768 if (padding > 0) {
1769 try lc_writer.writeByteNTimes(0, padding);
1770 }
1771 ncmds.* += 1;
1772}
1773
1774pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1775 if (self.base.options.output_mode != .Lib) return;
1776 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
1777 const curr = self.base.options.version orelse std.builtin.Version{
1778 .major = 1,
1779 .minor = 0,
1780 .patch = 0,
1781 };
1782 const compat = self.base.options.compatibility_version orelse std.builtin.Version{
1783 .major = 1,
1784 .minor = 0,
1785 .patch = 0,
1786 };
1787 try writeDylibLC(.{
1788 .cmd = .ID_DYLIB,
1789 .name = install_name,
1790 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1791 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1792 }, ncmds, lc_writer);
1793}
1794
1795const RpathIterator = struct {
1796 buffer: []const []const u8,
1797 table: std.StringHashMap(void),
1798 count: usize = 0,
1799
1800 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1801 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
1802 }
1803
1804 fn deinit(it: *RpathIterator) void {
1805 it.table.deinit();
1806 }
1807
1808 fn next(it: *RpathIterator) !?[]const u8 {
1809 while (true) {
1810 if (it.count >= it.buffer.len) return null;
1811 const rpath = it.buffer[it.count];
1812 it.count += 1;
1813 const gop = try it.table.getOrPut(rpath);
1814 if (gop.found_existing) continue;
1815 return rpath;
1816 }
1817 }
1818};
1819
1820pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1821 const gpa = self.base.allocator;
1822
1823 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
1824 defer it.deinit();
1825
1826 while (try it.next()) |rpath| {
1827 const rpath_len = rpath.len + 1;
1828 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1829 u64,
1830 @sizeOf(macho.rpath_command) + rpath_len,
1831 @sizeOf(u64),
1832 ));
1833 try lc_writer.writeStruct(macho.rpath_command{
1834 .cmdsize = cmdsize,
1835 .path = @sizeOf(macho.rpath_command),
1836 });
1837 try lc_writer.writeAll(rpath);
1838 try lc_writer.writeByte(0);
1839 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1840 if (padding > 0) {
1841 try lc_writer.writeByteNTimes(0, padding);
1842 }
1843 ncmds.* += 1;
1844 }
1845}
1846
1847pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1848 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1849 const platform_version = blk: {
1850 const ver = self.base.options.target.os.version_range.semver.min;
1851 const platform_version = ver.major << 16 | ver.minor << 8;
1852 break :blk platform_version;
1853 };
1854 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
1855 const ver = sdk.version;
1856 const sdk_version = ver.major << 16 | ver.minor << 8;
1857 break :blk sdk_version;
1858 } else platform_version;
1859 const is_simulator_abi = self.base.options.target.abi == .simulator;
1860 try lc_writer.writeStruct(macho.build_version_command{
1861 .cmdsize = cmdsize,
1862 .platform = switch (self.base.options.target.os.tag) {
1863 .macos => .MACOS,
1864 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1865 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1866 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1867 else => unreachable,
1868 },
1869 .minos = platform_version,
1870 .sdk = sdk_version,
1871 .ntools = 1,
1872 });
1873 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1874 .tool = .LD,
1875 .version = 0x0,
1876 }));
1877 ncmds.* += 1;
1878}
1879
1880pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1881 for (self.referenced_dylibs.keys()) |id| {
1882 const dylib = self.dylibs.items[id];
1883 const dylib_id = dylib.id orelse unreachable;
1884 try writeDylibLC(.{
1885 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1886 .name = dylib_id.name,
1887 .timestamp = dylib_id.timestamp,
1888 .current_version = dylib_id.current_version,
1889 .compatibility_version = dylib_id.compatibility_version,
1890 }, ncmds, lc_writer);
1891 }
1892}
1893
1894pub fn deinit(self: *MachO) void {1710pub fn deinit(self: *MachO) void {
1895 const gpa = self.base.allocator;1711 const gpa = self.base.allocator;
18961712
...@@ -2976,98 +2792,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2976,98 +2792,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2976 }2792 }
2977}2793}
29782794
2979pub inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {2795fn calcPagezeroSize(self: *MachO) u64 {
2980 const darwin_path_max = 1024;
2981 const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
2982 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
2983}
2984
2985fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
2986 const gpa = self.base.allocator;
2987 var sizeofcmds: u64 = 0;
2988 for (self.segments.items) |seg| {
2989 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
2990 }
2991
2992 // LC_DYLD_INFO_ONLY
2993 sizeofcmds += @sizeOf(macho.dyld_info_command);
2994 // LC_FUNCTION_STARTS
2995 if (self.text_section_index != null) {
2996 sizeofcmds += @sizeOf(macho.linkedit_data_command);
2997 }
2998 // LC_DATA_IN_CODE
2999 sizeofcmds += @sizeOf(macho.linkedit_data_command);
3000 // LC_SYMTAB
3001 sizeofcmds += @sizeOf(macho.symtab_command);
3002 // LC_DYSYMTAB
3003 sizeofcmds += @sizeOf(macho.dysymtab_command);
3004 // LC_LOAD_DYLINKER
3005 sizeofcmds += calcInstallNameLen(
3006 @sizeOf(macho.dylinker_command),
3007 mem.sliceTo(default_dyld_path, 0),
3008 false,
3009 );
3010 // LC_MAIN
3011 if (self.base.options.output_mode == .Exe) {
3012 sizeofcmds += @sizeOf(macho.entry_point_command);
3013 }
3014 // LC_ID_DYLIB
3015 if (self.base.options.output_mode == .Lib) {
3016 sizeofcmds += blk: {
3017 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
3018 break :blk calcInstallNameLen(
3019 @sizeOf(macho.dylib_command),
3020 install_name,
3021 assume_max_path_len,
3022 );
3023 };
3024 }
3025 // LC_RPATH
3026 {
3027 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
3028 defer it.deinit();
3029 while (try it.next()) |rpath| {
3030 sizeofcmds += calcInstallNameLen(
3031 @sizeOf(macho.rpath_command),
3032 rpath,
3033 assume_max_path_len,
3034 );
3035 }
3036 }
3037 // LC_SOURCE_VERSION
3038 sizeofcmds += @sizeOf(macho.source_version_command);
3039 // LC_BUILD_VERSION
3040 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
3041 // LC_UUID
3042 sizeofcmds += @sizeOf(macho.uuid_command);
3043 // LC_LOAD_DYLIB
3044 for (self.referenced_dylibs.keys()) |id| {
3045 const dylib = self.dylibs.items[id];
3046 const dylib_id = dylib.id orelse unreachable;
3047 sizeofcmds += calcInstallNameLen(
3048 @sizeOf(macho.dylib_command),
3049 dylib_id.name,
3050 assume_max_path_len,
3051 );
3052 }
3053 // LC_CODE_SIGNATURE
3054 {
3055 const target = self.base.options.target;
3056 const requires_codesig = blk: {
3057 if (self.base.options.entitlements) |_| break :blk true;
3058 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
3059 break :blk true;
3060 break :blk false;
3061 };
3062 if (requires_codesig) {
3063 sizeofcmds += @sizeOf(macho.linkedit_data_command);
3064 }
3065 }
3066
3067 return @intCast(u32, sizeofcmds);
3068}
3069
3070pub fn calcPagezeroSize(self: *MachO) u64 {
3071 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;2796 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
3072 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);2797 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
3073 if (self.base.options.output_mode == .Lib) return 0;2798 if (self.base.options.output_mode == .Lib) return 0;
...@@ -3079,23 +2804,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {...@@ -3079,23 +2804,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {
3079 return aligned_pagezero_vmsize;2804 return aligned_pagezero_vmsize;
3080}2805}
30812806
3082pub fn calcMinHeaderPad(self: *MachO) !u64 {
3083 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
3084 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
3085
3086 if (self.base.options.headerpad_max_install_names) {
3087 var min_headerpad_size: u32 = try self.calcLCsSize(true);
3088 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
3089 min_headerpad_size + @sizeOf(macho.mach_header_64),
3090 });
3091 padding = @max(padding, min_headerpad_size);
3092 }
3093 const offset = @sizeOf(macho.mach_header_64) + padding;
3094 log.debug("actual headerpad size 0x{x}", .{offset});
3095
3096 return offset;
3097}
3098
3099fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {2807fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3100 size: u64 = 0,2808 size: u64 = 0,
3101 alignment: u32 = 0,2809 alignment: u32 = 0,
src/link/MachO/load_commands.zig created+325
...@@ -0,0 +1,325 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const link = @import("../../link.zig");
4const log = std.log.scoped(.link);
5const macho = std.macho;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Dylib = @import("Dylib.zig");
10
11pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
12
13fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
14 const darwin_path_max = 1024;
15 const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
16 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
17}
18
19const CalcLCsSizeCtx = struct {
20 segments: []const macho.segment_command_64,
21 dylibs: []const Dylib,
22 referenced_dylibs: []u16,
23 wants_function_starts: bool = true,
24};
25
26fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 {
27 var has_text_segment: bool = false;
28 var sizeofcmds: u64 = 0;
29 for (ctx.segments) |seg| {
30 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
31 if (mem.eql(u8, seg.segName(), "__TEXT")) {
32 has_text_segment = true;
33 }
34 }
35
36 // LC_DYLD_INFO_ONLY
37 sizeofcmds += @sizeOf(macho.dyld_info_command);
38 // LC_FUNCTION_STARTS
39 if (has_text_segment and ctx.wants_function_starts) |_| {
40 sizeofcmds += @sizeOf(macho.linkedit_data_command);
41 }
42 // LC_DATA_IN_CODE
43 sizeofcmds += @sizeOf(macho.linkedit_data_command);
44 // LC_SYMTAB
45 sizeofcmds += @sizeOf(macho.symtab_command);
46 // LC_DYSYMTAB
47 sizeofcmds += @sizeOf(macho.dysymtab_command);
48 // LC_LOAD_DYLINKER
49 sizeofcmds += calcInstallNameLen(
50 @sizeOf(macho.dylinker_command),
51 mem.sliceTo(default_dyld_path, 0),
52 false,
53 );
54 // LC_MAIN
55 if (options.output_mode == .Exe) {
56 sizeofcmds += @sizeOf(macho.entry_point_command);
57 }
58 // LC_ID_DYLIB
59 if (options.output_mode == .Lib and options.link_mode == .Dynamic) {
60 sizeofcmds += blk: {
61 const emit = options.emit.?;
62 const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
63 defer if (options.install_name == null) gpa.free(install_name);
64 break :blk calcInstallNameLen(
65 @sizeOf(macho.dylib_command),
66 install_name,
67 assume_max_path_len,
68 );
69 };
70 }
71 // LC_RPATH
72 {
73 var it = RpathIterator.init(gpa, options.rpath_list);
74 defer it.deinit();
75 while (try it.next()) |rpath| {
76 sizeofcmds += calcInstallNameLen(
77 @sizeOf(macho.rpath_command),
78 rpath,
79 assume_max_path_len,
80 );
81 }
82 }
83 // LC_SOURCE_VERSION
84 sizeofcmds += @sizeOf(macho.source_version_command);
85 // LC_BUILD_VERSION
86 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
87 // LC_UUID
88 sizeofcmds += @sizeOf(macho.uuid_command);
89 // LC_LOAD_DYLIB
90 for (ctx.referenced_dylibs) |id| {
91 const dylib = ctx.dylibs[id];
92 const dylib_id = dylib.id orelse unreachable;
93 sizeofcmds += calcInstallNameLen(
94 @sizeOf(macho.dylib_command),
95 dylib_id.name,
96 assume_max_path_len,
97 );
98 }
99 // LC_CODE_SIGNATURE
100 {
101 const target = options.target;
102 const requires_codesig = blk: {
103 if (options.entitlements) |_| break :blk true;
104 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
105 break :blk true;
106 break :blk false;
107 };
108 if (requires_codesig) {
109 sizeofcmds += @sizeOf(macho.linkedit_data_command);
110 }
111 }
112
113 return @intCast(u32, sizeofcmds);
114}
115
116pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 {
117 var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0);
118 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
119
120 if (options.headerpad_max_install_names) {
121 var min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true);
122 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
123 min_headerpad_size + @sizeOf(macho.mach_header_64),
124 });
125 padding = @max(padding, min_headerpad_size);
126 }
127
128 const offset = @sizeOf(macho.mach_header_64) + padding;
129 log.debug("actual headerpad size 0x{x}", .{offset});
130
131 return offset;
132}
133
134pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
135 const name_len = mem.sliceTo(default_dyld_path, 0).len;
136 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
137 u64,
138 @sizeOf(macho.dylinker_command) + name_len,
139 @sizeOf(u64),
140 ));
141 try lc_writer.writeStruct(macho.dylinker_command{
142 .cmd = .LOAD_DYLINKER,
143 .cmdsize = cmdsize,
144 .name = @sizeOf(macho.dylinker_command),
145 });
146 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
147 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
148 if (padding > 0) {
149 try lc_writer.writeByteNTimes(0, padding);
150 }
151 ncmds.* += 1;
152}
153
154const WriteDylibLCCtx = struct {
155 cmd: macho.LC,
156 name: []const u8,
157 timestamp: u32 = 2,
158 current_version: u32 = 0x10000,
159 compatibility_version: u32 = 0x10000,
160};
161
162fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
163 const name_len = ctx.name.len + 1;
164 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
165 u64,
166 @sizeOf(macho.dylib_command) + name_len,
167 @sizeOf(u64),
168 ));
169 try lc_writer.writeStruct(macho.dylib_command{
170 .cmd = ctx.cmd,
171 .cmdsize = cmdsize,
172 .dylib = .{
173 .name = @sizeOf(macho.dylib_command),
174 .timestamp = ctx.timestamp,
175 .current_version = ctx.current_version,
176 .compatibility_version = ctx.compatibility_version,
177 },
178 });
179 try lc_writer.writeAll(ctx.name);
180 try lc_writer.writeByte(0);
181 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
182 if (padding > 0) {
183 try lc_writer.writeByteNTimes(0, padding);
184 }
185 ncmds.* += 1;
186}
187
188pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
189 assert(options.output_mode == .Lib and options.link_mode == .Dynamic);
190 const emit = options.emit.?;
191 const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
192 defer if (options.install_name == null) gpa.free(install_name);
193 const curr = options.version orelse std.builtin.Version{
194 .major = 1,
195 .minor = 0,
196 .patch = 0,
197 };
198 const compat = options.compatibility_version orelse std.builtin.Version{
199 .major = 1,
200 .minor = 0,
201 .patch = 0,
202 };
203 try writeDylibLC(.{
204 .cmd = .ID_DYLIB,
205 .name = install_name,
206 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
207 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
208 }, ncmds, lc_writer);
209}
210
211pub fn writeMainLC(entryoff: u32, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
212 assert(options.output_mode == .Exe);
213 try lc_writer.writeStruct(macho.entry_point_command{
214 .cmd = .MAIN,
215 .cmdsize = @sizeOf(macho.entry_point_command),
216 .entryoff = entryoff,
217 .stacksize = options.stack_size_override orelse 0,
218 });
219 ncmds.* += 1;
220}
221
222const RpathIterator = struct {
223 buffer: []const []const u8,
224 table: std.StringHashMap(void),
225 count: usize = 0,
226
227 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
228 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
229 }
230
231 fn deinit(it: *RpathIterator) void {
232 it.table.deinit();
233 }
234
235 fn next(it: *RpathIterator) !?[]const u8 {
236 while (true) {
237 if (it.count >= it.buffer.len) return null;
238 const rpath = it.buffer[it.count];
239 it.count += 1;
240 const gop = try it.table.getOrPut(rpath);
241 if (gop.found_existing) continue;
242 return rpath;
243 }
244 }
245};
246
247pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
248 var it = RpathIterator.init(gpa, options.rpath_list);
249 defer it.deinit();
250
251 while (try it.next()) |rpath| {
252 const rpath_len = rpath.len + 1;
253 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
254 u64,
255 @sizeOf(macho.rpath_command) + rpath_len,
256 @sizeOf(u64),
257 ));
258 try lc_writer.writeStruct(macho.rpath_command{
259 .cmdsize = cmdsize,
260 .path = @sizeOf(macho.rpath_command),
261 });
262 try lc_writer.writeAll(rpath);
263 try lc_writer.writeByte(0);
264 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
265 if (padding > 0) {
266 try lc_writer.writeByteNTimes(0, padding);
267 }
268 ncmds.* += 1;
269 }
270}
271
272pub fn writeBuildVersionLC(options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void {
273 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
274 const platform_version = blk: {
275 const ver = options.target.os.version_range.semver.min;
276 const platform_version = ver.major << 16 | ver.minor << 8;
277 break :blk platform_version;
278 };
279 const sdk_version = if (options.native_darwin_sdk) |sdk| blk: {
280 const ver = sdk.version;
281 const sdk_version = ver.major << 16 | ver.minor << 8;
282 break :blk sdk_version;
283 } else platform_version;
284 const is_simulator_abi = options.target.abi == .simulator;
285 try lc_writer.writeStruct(macho.build_version_command{
286 .cmdsize = cmdsize,
287 .platform = switch (options.target.os.tag) {
288 .macos => .MACOS,
289 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
290 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
291 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
292 else => unreachable,
293 },
294 .minos = platform_version,
295 .sdk = sdk_version,
296 .ntools = 1,
297 });
298 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
299 .tool = .LD,
300 .version = 0x0,
301 }));
302 ncmds.* += 1;
303}
304
305pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, ncmds: *u32, lc_writer: anytype) !void {
306 for (referenced) |index| {
307 const dylib = dylibs[index];
308 const dylib_id = dylib.id orelse unreachable;
309 try writeDylibLC(.{
310 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
311 .name = dylib_id.name,
312 .timestamp = dylib_id.timestamp,
313 .current_version = dylib_id.current_version,
314 .compatibility_version = dylib_id.compatibility_version,
315 }, ncmds, lc_writer);
316 }
317}
318
319pub fn writeSourceVersionLC(ncmds: *u32, lc_writer: anytype) !void {
320 try lc_writer.writeStruct(macho.source_version_command{
321 .cmdsize = @sizeOf(macho.source_version_command),
322 .version = 0x0,
323 });
324 ncmds.* += 1;
325}
src/link/MachO/zld.zig+23-309
...@@ -13,6 +13,7 @@ const bind = @import("bind.zig");...@@ -13,6 +13,7 @@ const bind = @import("bind.zig");
13const dead_strip = @import("dead_strip.zig");13const dead_strip = @import("dead_strip.zig");
14const fat = @import("fat.zig");14const fat = @import("fat.zig");
15const link = @import("../../link.zig");15const link = @import("../../link.zig");
16const load_commands = @import("load_commands.zig");
16const thunks = @import("thunks.zig");17const thunks = @import("thunks.zig");
17const trace = @import("../../tracy.zig").trace;18const trace = @import("../../tracy.zig").trace;
1819
...@@ -34,7 +35,7 @@ pub const Zld = struct {...@@ -34,7 +35,7 @@ pub const Zld = struct {
34 gpa: Allocator,35 gpa: Allocator,
35 file: fs.File,36 file: fs.File,
36 page_size: u16,37 page_size: u16,
37 options: link.Options,38 options: *const link.Options,
3839
39 objects: std.ArrayListUnmanaged(Object) = .{},40 objects: std.ArrayListUnmanaged(Object) = .{},
40 archives: std.ArrayListUnmanaged(Archive) = .{},41 archives: std.ArrayListUnmanaged(Archive) = .{},
...@@ -1227,195 +1228,6 @@ pub const Zld = struct {...@@ -1227,195 +1228,6 @@ pub const Zld = struct {
1227 }1228 }
1228 }1229 }
12291230
1230 fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1231 const name_len = mem.sliceTo(MachO.default_dyld_path, 0).len;
1232 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1233 u64,
1234 @sizeOf(macho.dylinker_command) + name_len,
1235 @sizeOf(u64),
1236 ));
1237 try lc_writer.writeStruct(macho.dylinker_command{
1238 .cmd = .LOAD_DYLINKER,
1239 .cmdsize = cmdsize,
1240 .name = @sizeOf(macho.dylinker_command),
1241 });
1242 try lc_writer.writeAll(mem.sliceTo(MachO.default_dyld_path, 0));
1243 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1244 if (padding > 0) {
1245 try lc_writer.writeByteNTimes(0, padding);
1246 }
1247 ncmds.* += 1;
1248 }
1249
1250 fn writeMainLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1251 if (self.options.output_mode != .Exe) return;
1252 const seg_id = self.getSegmentByName("__TEXT").?;
1253 const seg = self.segments.items[seg_id];
1254 const global = self.getEntryPoint();
1255 const sym = self.getSymbol(global);
1256 try lc_writer.writeStruct(macho.entry_point_command{
1257 .cmd = .MAIN,
1258 .cmdsize = @sizeOf(macho.entry_point_command),
1259 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1260 .stacksize = self.options.stack_size_override orelse 0,
1261 });
1262 ncmds.* += 1;
1263 }
1264
1265 const WriteDylibLCCtx = struct {
1266 cmd: macho.LC,
1267 name: []const u8,
1268 timestamp: u32 = 2,
1269 current_version: u32 = 0x10000,
1270 compatibility_version: u32 = 0x10000,
1271 };
1272
1273 fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1274 const name_len = ctx.name.len + 1;
1275 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1276 u64,
1277 @sizeOf(macho.dylib_command) + name_len,
1278 @sizeOf(u64),
1279 ));
1280 try lc_writer.writeStruct(macho.dylib_command{
1281 .cmd = ctx.cmd,
1282 .cmdsize = cmdsize,
1283 .dylib = .{
1284 .name = @sizeOf(macho.dylib_command),
1285 .timestamp = ctx.timestamp,
1286 .current_version = ctx.current_version,
1287 .compatibility_version = ctx.compatibility_version,
1288 },
1289 });
1290 try lc_writer.writeAll(ctx.name);
1291 try lc_writer.writeByte(0);
1292 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1293 if (padding > 0) {
1294 try lc_writer.writeByteNTimes(0, padding);
1295 }
1296 ncmds.* += 1;
1297 }
1298
1299 fn writeDylibIdLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1300 if (self.options.output_mode != .Lib) return;
1301 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1302 const curr = self.options.version orelse std.builtin.Version{
1303 .major = 1,
1304 .minor = 0,
1305 .patch = 0,
1306 };
1307 const compat = self.options.compatibility_version orelse std.builtin.Version{
1308 .major = 1,
1309 .minor = 0,
1310 .patch = 0,
1311 };
1312 try writeDylibLC(.{
1313 .cmd = .ID_DYLIB,
1314 .name = install_name,
1315 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1316 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1317 }, ncmds, lc_writer);
1318 }
1319
1320 const RpathIterator = struct {
1321 buffer: []const []const u8,
1322 table: std.StringHashMap(void),
1323 count: usize = 0,
1324
1325 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1326 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
1327 }
1328
1329 fn deinit(it: *RpathIterator) void {
1330 it.table.deinit();
1331 }
1332
1333 fn next(it: *RpathIterator) !?[]const u8 {
1334 while (true) {
1335 if (it.count >= it.buffer.len) return null;
1336 const rpath = it.buffer[it.count];
1337 it.count += 1;
1338 const gop = try it.table.getOrPut(rpath);
1339 if (gop.found_existing) continue;
1340 return rpath;
1341 }
1342 }
1343 };
1344
1345 fn writeRpathLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1346 const gpa = self.gpa;
1347
1348 var it = RpathIterator.init(gpa, self.options.rpath_list);
1349 defer it.deinit();
1350
1351 while (try it.next()) |rpath| {
1352 const rpath_len = rpath.len + 1;
1353 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1354 u64,
1355 @sizeOf(macho.rpath_command) + rpath_len,
1356 @sizeOf(u64),
1357 ));
1358 try lc_writer.writeStruct(macho.rpath_command{
1359 .cmdsize = cmdsize,
1360 .path = @sizeOf(macho.rpath_command),
1361 });
1362 try lc_writer.writeAll(rpath);
1363 try lc_writer.writeByte(0);
1364 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1365 if (padding > 0) {
1366 try lc_writer.writeByteNTimes(0, padding);
1367 }
1368 ncmds.* += 1;
1369 }
1370 }
1371
1372 fn writeBuildVersionLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1373 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1374 const platform_version = blk: {
1375 const ver = self.options.target.os.version_range.semver.min;
1376 const platform_version = ver.major << 16 | ver.minor << 8;
1377 break :blk platform_version;
1378 };
1379 const sdk_version = if (self.options.native_darwin_sdk) |sdk| blk: {
1380 const ver = sdk.version;
1381 const sdk_version = ver.major << 16 | ver.minor << 8;
1382 break :blk sdk_version;
1383 } else platform_version;
1384 const is_simulator_abi = self.options.target.abi == .simulator;
1385 try lc_writer.writeStruct(macho.build_version_command{
1386 .cmdsize = cmdsize,
1387 .platform = switch (self.options.target.os.tag) {
1388 .macos => .MACOS,
1389 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1390 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1391 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1392 else => unreachable,
1393 },
1394 .minos = platform_version,
1395 .sdk = sdk_version,
1396 .ntools = 1,
1397 });
1398 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1399 .tool = .LD,
1400 .version = 0x0,
1401 }));
1402 ncmds.* += 1;
1403 }
1404
1405 fn writeLoadDylibLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1406 for (self.referenced_dylibs.keys()) |id| {
1407 const dylib = self.dylibs.items[id];
1408 const dylib_id = dylib.id orelse unreachable;
1409 try writeDylibLC(.{
1410 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1411 .name = dylib_id.name,
1412 .timestamp = dylib_id.timestamp,
1413 .current_version = dylib_id.current_version,
1414 .compatibility_version = dylib_id.compatibility_version,
1415 }, ncmds, lc_writer);
1416 }
1417 }
1418
1419 pub fn deinit(self: *Zld) void {1231 pub fn deinit(self: *Zld) void {
1420 const gpa = self.gpa;1232 const gpa = self.gpa;
14211233
...@@ -1516,110 +1328,6 @@ pub const Zld = struct {...@@ -1516,110 +1328,6 @@ pub const Zld = struct {
1516 }1328 }
1517 }1329 }
15181330
1519 fn calcLCsSize(self: *Zld, assume_max_path_len: bool) !u32 {
1520 const gpa = self.gpa;
1521
1522 var sizeofcmds: u64 = 0;
1523 for (self.segments.items) |seg| {
1524 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
1525 }
1526
1527 // LC_DYLD_INFO_ONLY
1528 sizeofcmds += @sizeOf(macho.dyld_info_command);
1529 // LC_FUNCTION_STARTS
1530 if (self.getSectionByName("__TEXT", "__text")) |_| {
1531 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1532 }
1533 // LC_DATA_IN_CODE
1534 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1535 // LC_SYMTAB
1536 sizeofcmds += @sizeOf(macho.symtab_command);
1537 // LC_DYSYMTAB
1538 sizeofcmds += @sizeOf(macho.dysymtab_command);
1539 // LC_LOAD_DYLINKER
1540 sizeofcmds += MachO.calcInstallNameLen(
1541 @sizeOf(macho.dylinker_command),
1542 mem.sliceTo(MachO.default_dyld_path, 0),
1543 false,
1544 );
1545 // LC_MAIN
1546 if (self.options.output_mode == .Exe) {
1547 sizeofcmds += @sizeOf(macho.entry_point_command);
1548 }
1549 // LC_ID_DYLIB
1550 if (self.options.output_mode == .Lib) {
1551 sizeofcmds += blk: {
1552 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1553 break :blk MachO.calcInstallNameLen(
1554 @sizeOf(macho.dylib_command),
1555 install_name,
1556 assume_max_path_len,
1557 );
1558 };
1559 }
1560 // LC_RPATH
1561 {
1562 var it = RpathIterator.init(gpa, self.options.rpath_list);
1563 defer it.deinit();
1564 while (try it.next()) |rpath| {
1565 sizeofcmds += MachO.calcInstallNameLen(
1566 @sizeOf(macho.rpath_command),
1567 rpath,
1568 assume_max_path_len,
1569 );
1570 }
1571 }
1572 // LC_SOURCE_VERSION
1573 sizeofcmds += @sizeOf(macho.source_version_command);
1574 // LC_BUILD_VERSION
1575 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1576 // LC_UUID
1577 sizeofcmds += @sizeOf(macho.uuid_command);
1578 // LC_LOAD_DYLIB
1579 for (self.referenced_dylibs.keys()) |id| {
1580 const dylib = self.dylibs.items[id];
1581 const dylib_id = dylib.id orelse unreachable;
1582 sizeofcmds += MachO.calcInstallNameLen(
1583 @sizeOf(macho.dylib_command),
1584 dylib_id.name,
1585 assume_max_path_len,
1586 );
1587 }
1588 // LC_CODE_SIGNATURE
1589 {
1590 const target = self.options.target;
1591 const requires_codesig = blk: {
1592 if (self.options.entitlements) |_| break :blk true;
1593 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
1594 break :blk true;
1595 break :blk false;
1596 };
1597 if (requires_codesig) {
1598 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1599 }
1600 }
1601
1602 return @intCast(u32, sizeofcmds);
1603 }
1604
1605 fn calcMinHeaderPad(self: *Zld) !u64 {
1606 var padding: u32 = (try self.calcLCsSize(false)) + (self.options.headerpad_size orelse 0);
1607 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
1608
1609 if (self.options.headerpad_max_install_names) {
1610 var min_headerpad_size: u32 = try self.calcLCsSize(true);
1611 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
1612 min_headerpad_size + @sizeOf(macho.mach_header_64),
1613 });
1614 padding = @max(padding, min_headerpad_size);
1615 }
1616
1617 const offset = @sizeOf(macho.mach_header_64) + padding;
1618 log.debug("actual headerpad size 0x{x}", .{offset});
1619
1620 return offset;
1621 }
1622
1623 pub fn allocateSymbol(self: *Zld) !u32 {1331 pub fn allocateSymbol(self: *Zld) !u32 {
1624 try self.locals.ensureUnusedCapacity(self.gpa, 1);1332 try self.locals.ensureUnusedCapacity(self.gpa, 1);
1625 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});1333 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
...@@ -1842,7 +1550,11 @@ pub const Zld = struct {...@@ -1842,7 +1550,11 @@ pub const Zld = struct {
1842 fn allocateSegments(self: *Zld) !void {1550 fn allocateSegments(self: *Zld) !void {
1843 for (self.segments.items) |*segment, segment_index| {1551 for (self.segments.items) |*segment, segment_index| {
1844 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");1552 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1845 const base_size = if (is_text_segment) try self.calcMinHeaderPad() else 0;1553 const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
1554 .segments = self.segments.items,
1555 .dylibs = self.dylibs.items,
1556 .referenced_dylibs = self.referenced_dylibs.keys(),
1557 }) else 0;
1846 try self.allocateSegment(@intCast(u8, segment_index), base_size);1558 try self.allocateSegment(@intCast(u8, segment_index), base_size);
1847 }1559 }
1848 }1560 }
...@@ -3734,7 +3446,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3734,7 +3446,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3734 defer tracy.end();3446 defer tracy.end();
37353447
3736 const gpa = macho_file.base.allocator;3448 const gpa = macho_file.base.allocator;
3737 const options = macho_file.base.options;3449 const options = &macho_file.base.options;
3738 const target = options.target;3450 const target = options.target;
37393451
3740 var arena_allocator = std.heap.ArenaAllocator.init(gpa);3452 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
...@@ -3884,7 +3596,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3884,7 +3596,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3884 macho_file.base.file = try directory.handle.createFile(sub_path, .{3596 macho_file.base.file = try directory.handle.createFile(sub_path, .{
3885 .truncate = true,3597 .truncate = true,
3886 .read = true,3598 .read = true,
3887 .mode = link.determineMode(options),3599 .mode = link.determineMode(options.*),
3888 });3600 });
3889 }3601 }
3890 var zld = Zld{3602 var zld = Zld{
...@@ -4301,20 +4013,22 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4301,20 +4013,22 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4301 }4013 }
4302 }4014 }
43034015
4304 try Zld.writeDylinkerLC(&ncmds, lc_writer);4016 try load_commands.writeDylinkerLC(&ncmds, lc_writer);
4305 try zld.writeMainLC(&ncmds, lc_writer);
4306 try zld.writeDylibIdLC(&ncmds, lc_writer);
4307 try zld.writeRpathLCs(&ncmds, lc_writer);
43084017
4309 {4018 if (zld.options.output_mode == .Exe) {
4310 try lc_writer.writeStruct(macho.source_version_command{4019 const seg_id = zld.getSegmentByName("__TEXT").?;
4311 .cmdsize = @sizeOf(macho.source_version_command),4020 const seg = zld.segments.items[seg_id];
4312 .version = 0x0,4021 const global = zld.getEntryPoint();
4313 });4022 const sym = zld.getSymbol(global);
4314 ncmds += 1;4023 try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), options, &ncmds, lc_writer);
4024 } else {
4025 assert(zld.options.output_mode == .Lib);
4026 try load_commands.writeDylibIdLC(zld.gpa, zld.options, &ncmds, lc_writer);
4315 }4027 }
43164028
4317 try zld.writeBuildVersionLC(&ncmds, lc_writer);4029 try load_commands.writeRpathLCs(zld.gpa, zld.options, &ncmds, lc_writer);
4030 try load_commands.writeSourceVersionLC(&ncmds, lc_writer);
4031 try load_commands.writeBuildVersionLC(zld.options, &ncmds, lc_writer);
43184032
4319 {4033 {
4320 var uuid_lc = macho.uuid_command{4034 var uuid_lc = macho.uuid_command{
...@@ -4326,7 +4040,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4326,7 +4040,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4326 ncmds += 1;4040 ncmds += 1;
4327 }4041 }
43284042
4329 try zld.writeLoadDylibLCs(&ncmds, lc_writer);4043 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), &ncmds, lc_writer);
43304044
4331 const requires_codesig = blk: {4045 const requires_codesig = blk: {
4332 if (options.entitlements) |_| break :blk true;4046 if (options.entitlements) |_| break :blk true;