authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-10 21:55:21+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-10 21:55:21+01:00
log75f3e7a4a05db7ea805e581f78117a41768945ae
tree6972d08f0c0292d27ebb1e8a74bc7c9a3087feae
parent77836e08a2384450b5e7933094511b61e3c22140
parent828f61e8dfcf17e0f7c42552311e6589bb187880
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10310 from ziglang/macho-common-functions

macho: move load command wrappers and parsing utils to std.macho

8 files changed, 723 insertions(+), 757 deletions(-)

CMakeLists.txt-1
......@@ -590,7 +590,6 @@ set(ZIG_STAGE2_SOURCES
590590 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
591591 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
592592 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
594593 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
595594 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
596595 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
lib/std/macho.zig+485-8
......@@ -1,3 +1,13 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const io = std.io;
5const mem = std.mem;
6const meta = std.meta;
7const testing = std.testing;
8
9const Allocator = mem.Allocator;
10
111pub const mach_header = extern struct {
212 magic: u32,
313 cputype: cpu_type_t,
......@@ -9,14 +19,14 @@ pub const mach_header = extern struct {
919};
1020
1121pub const mach_header_64 = extern struct {
12 magic: u32,
13 cputype: cpu_type_t,
14 cpusubtype: cpu_subtype_t,
15 filetype: u32,
16 ncmds: u32,
17 sizeofcmds: u32,
18 flags: u32,
19 reserved: u32,
22 magic: u32 = MH_MAGIC_64,
23 cputype: cpu_type_t = 0,
24 cpusubtype: cpu_subtype_t = 0,
25 filetype: u32 = 0,
26 ncmds: u32 = 0,
27 sizeofcmds: u32 = 0,
28 flags: u32 = 0,
29 reserved: u32 = 0,
2030};
2131
2232pub const fat_header = extern struct {
......@@ -630,6 +640,10 @@ pub const segment_command_64 = extern struct {
630640 /// number of sections in segment
631641 nsects: u32 = 0,
632642 flags: u32 = 0,
643
644 pub fn segName(seg: segment_command_64) []const u8 {
645 return parseName(&seg.segname);
646 }
633647};
634648
635649/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
......@@ -728,8 +742,46 @@ pub const section_64 = extern struct {
728742
729743 /// reserved
730744 reserved3: u32 = 0,
745
746 pub fn sectName(sect: section_64) []const u8 {
747 return parseName(&sect.sectname);
748 }
749
750 pub fn segName(sect: section_64) []const u8 {
751 return parseName(&sect.segname);
752 }
753
754 pub fn type_(sect: section_64) u8 {
755 return @truncate(u8, sect.flags & 0xff);
756 }
757
758 pub fn attrs(sect: section_64) u32 {
759 return sect.flags & 0xffffff00;
760 }
761
762 pub fn isCode(sect: section_64) bool {
763 const attr = sect.attrs();
764 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
765 }
766
767 pub fn isDebug(sect: section_64) bool {
768 return sect.attrs() & S_ATTR_DEBUG != 0;
769 }
770
771 pub fn isDontDeadStrip(sect: section_64) bool {
772 return sect.attrs() & S_ATTR_NO_DEAD_STRIP != 0;
773 }
774
775 pub fn isDontDeadStripIfReferencesLive(sect: section_64) bool {
776 return sect.attrs() & S_ATTR_LIVE_SUPPORT != 0;
777 }
731778};
732779
780fn parseName(name: *const [16]u8) []const u8 {
781 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
782 return name[0..len];
783}
784
733785pub const nlist = extern struct {
734786 n_strx: u32,
735787 n_type: u8,
......@@ -1760,3 +1812,428 @@ pub const data_in_code_entry = extern struct {
17601812 /// A DICE_KIND value.
17611813 kind: u16,
17621814};
1815
1816/// A Zig wrapper for all known MachO load commands.
1817/// Provides interface to read and write the load command data to a buffer.
1818pub const LoadCommand = union(enum) {
1819 segment: SegmentCommand,
1820 dyld_info_only: dyld_info_command,
1821 symtab: symtab_command,
1822 dysymtab: dysymtab_command,
1823 dylinker: GenericCommandWithData(dylinker_command),
1824 dylib: GenericCommandWithData(dylib_command),
1825 main: entry_point_command,
1826 version_min: version_min_command,
1827 source_version: source_version_command,
1828 build_version: GenericCommandWithData(build_version_command),
1829 uuid: uuid_command,
1830 linkedit_data: linkedit_data_command,
1831 rpath: GenericCommandWithData(rpath_command),
1832 unknown: GenericCommandWithData(load_command),
1833
1834 pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
1835 const header = try reader.readStruct(load_command);
1836 var buffer = try allocator.alloc(u8, header.cmdsize);
1837 defer allocator.free(buffer);
1838 mem.copy(u8, buffer, mem.asBytes(&header));
1839 try reader.readNoEof(buffer[@sizeOf(load_command)..]);
1840 var stream = io.fixedBufferStream(buffer);
1841
1842 return switch (header.cmd) {
1843 LC_SEGMENT_64 => LoadCommand{
1844 .segment = try SegmentCommand.read(allocator, stream.reader()),
1845 },
1846 LC_DYLD_INFO, LC_DYLD_INFO_ONLY => LoadCommand{
1847 .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
1848 },
1849 LC_SYMTAB => LoadCommand{
1850 .symtab = try stream.reader().readStruct(symtab_command),
1851 },
1852 LC_DYSYMTAB => LoadCommand{
1853 .dysymtab = try stream.reader().readStruct(dysymtab_command),
1854 },
1855 LC_ID_DYLINKER, LC_LOAD_DYLINKER, LC_DYLD_ENVIRONMENT => LoadCommand{
1856 .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
1857 },
1858 LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB => LoadCommand{
1859 .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
1860 },
1861 LC_MAIN => LoadCommand{
1862 .main = try stream.reader().readStruct(entry_point_command),
1863 },
1864 LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS => LoadCommand{
1865 .version_min = try stream.reader().readStruct(version_min_command),
1866 },
1867 LC_SOURCE_VERSION => LoadCommand{
1868 .source_version = try stream.reader().readStruct(source_version_command),
1869 },
1870 LC_BUILD_VERSION => LoadCommand{
1871 .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
1872 },
1873 LC_UUID => LoadCommand{
1874 .uuid = try stream.reader().readStruct(uuid_command),
1875 },
1876 LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_CODE_SIGNATURE => LoadCommand{
1877 .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
1878 },
1879 LC_RPATH => LoadCommand{
1880 .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
1881 },
1882 else => LoadCommand{
1883 .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
1884 },
1885 };
1886 }
1887
1888 pub fn write(self: LoadCommand, writer: anytype) !void {
1889 return switch (self) {
1890 .dyld_info_only => |x| writeStruct(x, writer),
1891 .symtab => |x| writeStruct(x, writer),
1892 .dysymtab => |x| writeStruct(x, writer),
1893 .main => |x| writeStruct(x, writer),
1894 .version_min => |x| writeStruct(x, writer),
1895 .source_version => |x| writeStruct(x, writer),
1896 .uuid => |x| writeStruct(x, writer),
1897 .linkedit_data => |x| writeStruct(x, writer),
1898 .segment => |x| x.write(writer),
1899 .dylinker => |x| x.write(writer),
1900 .dylib => |x| x.write(writer),
1901 .rpath => |x| x.write(writer),
1902 .build_version => |x| x.write(writer),
1903 .unknown => |x| x.write(writer),
1904 };
1905 }
1906
1907 pub fn cmd(self: LoadCommand) u32 {
1908 return switch (self) {
1909 .dyld_info_only => |x| x.cmd,
1910 .symtab => |x| x.cmd,
1911 .dysymtab => |x| x.cmd,
1912 .main => |x| x.cmd,
1913 .version_min => |x| x.cmd,
1914 .source_version => |x| x.cmd,
1915 .uuid => |x| x.cmd,
1916 .linkedit_data => |x| x.cmd,
1917 .segment => |x| x.inner.cmd,
1918 .dylinker => |x| x.inner.cmd,
1919 .dylib => |x| x.inner.cmd,
1920 .rpath => |x| x.inner.cmd,
1921 .build_version => |x| x.inner.cmd,
1922 .unknown => |x| x.inner.cmd,
1923 };
1924 }
1925
1926 pub fn cmdsize(self: LoadCommand) u32 {
1927 return switch (self) {
1928 .dyld_info_only => |x| x.cmdsize,
1929 .symtab => |x| x.cmdsize,
1930 .dysymtab => |x| x.cmdsize,
1931 .main => |x| x.cmdsize,
1932 .version_min => |x| x.cmdsize,
1933 .source_version => |x| x.cmdsize,
1934 .linkedit_data => |x| x.cmdsize,
1935 .uuid => |x| x.cmdsize,
1936 .segment => |x| x.inner.cmdsize,
1937 .dylinker => |x| x.inner.cmdsize,
1938 .dylib => |x| x.inner.cmdsize,
1939 .rpath => |x| x.inner.cmdsize,
1940 .build_version => |x| x.inner.cmdsize,
1941 .unknown => |x| x.inner.cmdsize,
1942 };
1943 }
1944
1945 pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
1946 return switch (self.*) {
1947 .segment => |*x| x.deinit(allocator),
1948 .dylinker => |*x| x.deinit(allocator),
1949 .dylib => |*x| x.deinit(allocator),
1950 .rpath => |*x| x.deinit(allocator),
1951 .build_version => |*x| x.deinit(allocator),
1952 .unknown => |*x| x.deinit(allocator),
1953 else => {},
1954 };
1955 }
1956
1957 fn writeStruct(command: anytype, writer: anytype) !void {
1958 return writer.writeAll(mem.asBytes(&command));
1959 }
1960
1961 pub fn eql(self: LoadCommand, other: LoadCommand) bool {
1962 if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
1963 return switch (self) {
1964 .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
1965 .symtab => |x| meta.eql(x, other.symtab),
1966 .dysymtab => |x| meta.eql(x, other.dysymtab),
1967 .main => |x| meta.eql(x, other.main),
1968 .version_min => |x| meta.eql(x, other.version_min),
1969 .source_version => |x| meta.eql(x, other.source_version),
1970 .build_version => |x| x.eql(other.build_version),
1971 .uuid => |x| meta.eql(x, other.uuid),
1972 .linkedit_data => |x| meta.eql(x, other.linkedit_data),
1973 .segment => |x| x.eql(other.segment),
1974 .dylinker => |x| x.eql(other.dylinker),
1975 .dylib => |x| x.eql(other.dylib),
1976 .rpath => |x| x.eql(other.rpath),
1977 .unknown => |x| x.eql(other.unknown),
1978 };
1979 }
1980};
1981
1982/// A Zig wrapper for segment_command_64.
1983/// Encloses the extern struct together with a list of sections for this segment.
1984pub const SegmentCommand = struct {
1985 inner: segment_command_64,
1986 sections: std.ArrayListUnmanaged(section_64) = .{},
1987
1988 pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
1989 const inner = try reader.readStruct(segment_command_64);
1990 var segment = SegmentCommand{
1991 .inner = inner,
1992 };
1993 try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
1994
1995 var i: usize = 0;
1996 while (i < inner.nsects) : (i += 1) {
1997 const sect = try reader.readStruct(section_64);
1998 segment.sections.appendAssumeCapacity(sect);
1999 }
2000
2001 return segment;
2002 }
2003
2004 pub fn write(self: SegmentCommand, writer: anytype) !void {
2005 try writer.writeAll(mem.asBytes(&self.inner));
2006 for (self.sections.items) |sect| {
2007 try writer.writeAll(mem.asBytes(&sect));
2008 }
2009 }
2010
2011 pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
2012 self.sections.deinit(allocator);
2013 }
2014
2015 pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
2016 if (!meta.eql(self.inner, other.inner)) return false;
2017 const lhs = self.sections.items;
2018 const rhs = other.sections.items;
2019 var i: usize = 0;
2020 while (i < self.inner.nsects) : (i += 1) {
2021 if (!meta.eql(lhs[i], rhs[i])) return false;
2022 }
2023 return true;
2024 }
2025};
2026
2027pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
2028 return .{ .inner = cmd };
2029}
2030
2031/// A Zig wrapper for a generic load command with variable-length data.
2032pub fn GenericCommandWithData(comptime Cmd: type) type {
2033 return struct {
2034 inner: Cmd,
2035 /// This field remains undefined until `read` is called.
2036 data: []u8 = undefined,
2037
2038 const Self = @This();
2039
2040 pub fn read(allocator: Allocator, reader: anytype) !Self {
2041 const inner = try reader.readStruct(Cmd);
2042 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
2043 errdefer allocator.free(data);
2044 try reader.readNoEof(data);
2045 return Self{
2046 .inner = inner,
2047 .data = data,
2048 };
2049 }
2050
2051 pub fn write(self: Self, writer: anytype) !void {
2052 try writer.writeAll(mem.asBytes(&self.inner));
2053 try writer.writeAll(self.data);
2054 }
2055
2056 pub fn deinit(self: *Self, allocator: Allocator) void {
2057 allocator.free(self.data);
2058 }
2059
2060 pub fn eql(self: Self, other: Self) bool {
2061 if (!meta.eql(self.inner, other.inner)) return false;
2062 return mem.eql(u8, self.data, other.data);
2063 }
2064 };
2065}
2066
2067pub fn createLoadDylibCommand(
2068 allocator: Allocator,
2069 name: []const u8,
2070 timestamp: u32,
2071 current_version: u32,
2072 compatibility_version: u32,
2073) !GenericCommandWithData(dylib_command) {
2074 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2075 u64,
2076 @sizeOf(dylib_command) + name.len + 1, // +1 for nul
2077 @sizeOf(u64),
2078 ));
2079
2080 var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2081 .cmd = LC_LOAD_DYLIB,
2082 .cmdsize = cmdsize,
2083 .dylib = .{
2084 .name = @sizeOf(dylib_command),
2085 .timestamp = timestamp,
2086 .current_version = current_version,
2087 .compatibility_version = compatibility_version,
2088 },
2089 });
2090 dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2091
2092 mem.set(u8, dylib_cmd.data, 0);
2093 mem.copy(u8, dylib_cmd.data, name);
2094
2095 return dylib_cmd;
2096}
2097
2098fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
2099 var stream = io.fixedBufferStream(buffer);
2100 var given = try LoadCommand.read(allocator, stream.reader());
2101 defer given.deinit(allocator);
2102 try testing.expect(expected.eql(given));
2103}
2104
2105fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
2106 var stream = io.fixedBufferStream(buffer);
2107 try cmd.write(stream.writer());
2108 try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
2109}
2110
2111fn makeStaticString(bytes: []const u8) [16]u8 {
2112 var buf = [_]u8{0} ** 16;
2113 assert(bytes.len <= buf.len);
2114 mem.copy(u8, &buf, bytes);
2115 return buf;
2116}
2117
2118test "read-write segment command" {
2119 // TODO compiling for macOS from big-endian arch
2120 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2121
2122 var gpa = testing.allocator;
2123 const in_buffer = &[_]u8{
2124 0x19, 0x00, 0x00, 0x00, // cmd
2125 0x98, 0x00, 0x00, 0x00, // cmdsize
2126 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2127 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
2128 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
2129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
2130 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
2131 0x07, 0x00, 0x00, 0x00, // maxprot
2132 0x05, 0x00, 0x00, 0x00, // initprot
2133 0x01, 0x00, 0x00, 0x00, // nsects
2134 0x00, 0x00, 0x00, 0x00, // flags
2135 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
2136 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2137 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
2138 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
2139 0x00, 0x40, 0x00, 0x00, // offset
2140 0x02, 0x00, 0x00, 0x00, // alignment
2141 0x00, 0x00, 0x00, 0x00, // reloff
2142 0x00, 0x00, 0x00, 0x00, // nreloc
2143 0x00, 0x04, 0x00, 0x80, // flags
2144 0x00, 0x00, 0x00, 0x00, // reserved1
2145 0x00, 0x00, 0x00, 0x00, // reserved2
2146 0x00, 0x00, 0x00, 0x00, // reserved3
2147 };
2148 var cmd = SegmentCommand{
2149 .inner = .{
2150 .cmdsize = 152,
2151 .segname = makeStaticString("__TEXT"),
2152 .vmaddr = 4294967296,
2153 .vmsize = 294912,
2154 .filesize = 294912,
2155 .maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
2156 .initprot = VM_PROT_EXECUTE | VM_PROT_READ,
2157 .nsects = 1,
2158 },
2159 };
2160 try cmd.sections.append(gpa, .{
2161 .sectname = makeStaticString("__text"),
2162 .segname = makeStaticString("__TEXT"),
2163 .addr = 4294983680,
2164 .size = 448,
2165 .offset = 16384,
2166 .@"align" = 2,
2167 .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
2168 });
2169 defer cmd.deinit(gpa);
2170 try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
2171
2172 var out_buffer: [in_buffer.len]u8 = undefined;
2173 try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
2174}
2175
2176test "read-write generic command with data" {
2177 // TODO compiling for macOS from big-endian arch
2178 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2179
2180 var gpa = testing.allocator;
2181 const in_buffer = &[_]u8{
2182 0x0c, 0x00, 0x00, 0x00, // cmd
2183 0x20, 0x00, 0x00, 0x00, // cmdsize
2184 0x18, 0x00, 0x00, 0x00, // name
2185 0x02, 0x00, 0x00, 0x00, // timestamp
2186 0x00, 0x00, 0x00, 0x00, // current_version
2187 0x00, 0x00, 0x00, 0x00, // compatibility_version
2188 0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
2189 };
2190 var cmd = GenericCommandWithData(dylib_command){
2191 .inner = .{
2192 .cmd = LC_LOAD_DYLIB,
2193 .cmdsize = 32,
2194 .dylib = .{
2195 .name = 24,
2196 .timestamp = 2,
2197 .current_version = 0,
2198 .compatibility_version = 0,
2199 },
2200 },
2201 };
2202 cmd.data = try gpa.alloc(u8, 8);
2203 defer gpa.free(cmd.data);
2204 cmd.data[0] = 0x2f;
2205 cmd.data[1] = 0x75;
2206 cmd.data[2] = 0x73;
2207 cmd.data[3] = 0x72;
2208 cmd.data[4] = 0x0;
2209 cmd.data[5] = 0x0;
2210 cmd.data[6] = 0x0;
2211 cmd.data[7] = 0x0;
2212 try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
2213
2214 var out_buffer: [in_buffer.len]u8 = undefined;
2215 try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
2216}
2217
2218test "read-write C struct command" {
2219 // TODO compiling for macOS from big-endian arch
2220 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2221
2222 var gpa = testing.allocator;
2223 const in_buffer = &[_]u8{
2224 0x28, 0x00, 0x00, 0x80, // cmd
2225 0x18, 0x00, 0x00, 0x00, // cmdsize
2226 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
2227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
2228 };
2229 const cmd = .{
2230 .cmd = LC_MAIN,
2231 .cmdsize = 24,
2232 .entryoff = 16644,
2233 .stacksize = 0,
2234 };
2235 try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
2236
2237 var out_buffer: [in_buffer.len]u8 = undefined;
2238 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
2239}
src/link/MachO.zig+121-131
......@@ -15,7 +15,6 @@ const meta = std.meta;
1515const aarch64 = @import("../arch/aarch64/bits.zig");
1616const bind = @import("MachO/bind.zig");
1717const codegen = @import("../codegen.zig");
18const commands = @import("MachO/commands.zig");
1918const link = @import("../link.zig");
2019const llvm_backend = @import("../codegen/llvm.zig");
2120const target_util = @import("../target.zig");
......@@ -35,9 +34,7 @@ const Object = @import("MachO/Object.zig");
3534const LibStub = @import("tapi.zig").LibStub;
3635const Liveness = @import("../Liveness.zig");
3736const LlvmObject = @import("../codegen/llvm.zig").Object;
38const LoadCommand = commands.LoadCommand;
3937const Module = @import("../Module.zig");
40const SegmentCommand = commands.SegmentCommand;
4138const StringIndexAdapter = std.hash_map.StringIndexAdapter;
4239const StringIndexContext = std.hash_map.StringIndexContext;
4340const Trie = @import("MachO/Trie.zig");
......@@ -83,7 +80,7 @@ dylibs: std.ArrayListUnmanaged(Dylib) = .{},
8380dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
8481referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
8582
86load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
83load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
8784
8885pagezero_segment_cmd_index: ?u16 = null,
8986text_segment_cmd_index: ?u16 = null,
......@@ -783,7 +780,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
783780 @sizeOf(macho.rpath_command) + rpath.len + 1,
784781 @sizeOf(u64),
785782 ));
786 var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{
783 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
787784 .cmd = macho.LC_RPATH,
788785 .cmdsize = cmdsize,
789786 .path = @sizeOf(macho.rpath_command),
......@@ -791,7 +788,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
791788 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
792789 mem.set(u8, rpath_cmd.data, 0);
793790 mem.copy(u8, rpath_cmd.data, rpath);
794 try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd });
791 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });
795792 try rpath_table.putNoClobber(rpath, {});
796793 self.load_commands_dirty = true;
797794 }
......@@ -861,12 +858,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
861858 }
862859
863860 if (self.bss_section_index) |idx| {
864 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
861 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
865862 const sect = &seg.sections.items[idx];
866863 sect.offset = self.bss_file_offset;
867864 }
868865 if (self.tlv_bss_section_index) |idx| {
869 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
866 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
870867 const sect = &seg.sections.items[idx];
871868 sect.offset = self.tlv_bss_file_offset;
872869 }
......@@ -942,13 +939,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
942939 }
943940
944941 if (self.bss_section_index) |idx| {
945 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
942 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
946943 const sect = &seg.sections.items[idx];
947944 self.bss_file_offset = sect.offset;
948945 sect.offset = 0;
949946 }
950947 if (self.tlv_bss_section_index) |idx| {
951 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
948 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
952949 const sect = &seg.sections.items[idx];
953950 self.tlv_bss_file_offset = sect.offset;
954951 sect.offset = 0;
......@@ -1324,10 +1321,10 @@ pub const MatchingSection = struct {
13241321};
13251322
13261323pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
1327 const segname = commands.segmentName(sect);
1328 const sectname = commands.sectionName(sect);
1324 const segname = sect.segName();
1325 const sectname = sect.sectName();
13291326 const res: ?MatchingSection = blk: {
1330 switch (commands.sectionType(sect)) {
1327 switch (sect.type_()) {
13311328 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
13321329 if (self.text_const_section_index == null) {
13331330 self.text_const_section_index = try self.initSection(
......@@ -1579,7 +1576,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
15791576 };
15801577 },
15811578 macho.S_REGULAR => {
1582 if (commands.sectionIsCode(sect)) {
1579 if (sect.isCode()) {
15831580 if (self.text_section_index == null) {
15841581 self.text_section_index = try self.initSection(
15851582 self.text_segment_cmd_index.?,
......@@ -1599,7 +1596,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
15991596 .sect = self.text_section_index.?,
16001597 };
16011598 }
1602 if (commands.sectionIsDebug(sect)) {
1599 if (sect.isDebug()) {
16031600 // TODO debug attributes
16041601 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
16051602 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
......@@ -1865,7 +1862,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:
18651862}
18661863
18671864pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
1868 const seg = self.load_commands.items[match.seg].Segment;
1865 const seg = self.load_commands.items[match.seg].segment;
18691866 const sect = seg.sections.items[match.sect];
18701867 const sym = self.locals.items[atom.local_sym_index];
18711868 const file_offset = sect.offset + sym.n_value - sect.addr;
......@@ -1885,14 +1882,11 @@ fn allocateLocals(self: *MachO) !void {
18851882 }
18861883
18871884 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1888 const seg = self.load_commands.items[match.seg].Segment;
1885 const seg = self.load_commands.items[match.seg].segment;
18891886 const sect = seg.sections.items[match.sect];
18901887 var base_vaddr = sect.addr;
18911888
1892 log.debug("allocating local symbols in {s},{s}", .{
1893 commands.segmentName(sect),
1894 commands.sectionName(sect),
1895 });
1889 log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() });
18961890
18971891 while (true) {
18981892 const alignment = try math.powi(u32, 2, atom.alignment);
......@@ -1979,7 +1973,7 @@ fn writeAllAtoms(self: *MachO) !void {
19791973 var it = self.atoms.iterator();
19801974 while (it.next()) |entry| {
19811975 const match = entry.key_ptr.*;
1982 const seg = self.load_commands.items[match.seg].Segment;
1976 const seg = self.load_commands.items[match.seg].segment;
19831977 const sect = seg.sections.items[match.sect];
19841978 var atom: *Atom = entry.value_ptr.*;
19851979
......@@ -1987,7 +1981,7 @@ fn writeAllAtoms(self: *MachO) !void {
19871981 defer buffer.deinit();
19881982 try buffer.ensureTotalCapacity(try math.cast(usize, sect.size));
19891983
1990 log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
1984 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
19911985
19921986 while (atom.prev) |prev| {
19931987 atom = prev;
......@@ -2031,11 +2025,11 @@ fn writeAtoms(self: *MachO) !void {
20312025 var it = self.atoms.iterator();
20322026 while (it.next()) |entry| {
20332027 const match = entry.key_ptr.*;
2034 const seg = self.load_commands.items[match.seg].Segment;
2028 const seg = self.load_commands.items[match.seg].segment;
20352029 const sect = seg.sections.items[match.sect];
20362030 var atom: *Atom = entry.value_ptr.*;
20372031
2038 log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
2032 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
20392033
20402034 while (atom.prev) |prev| {
20412035 atom = prev;
......@@ -2995,7 +2989,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
29952989
29962990 const first_atom = atom;
29972991
2998 const seg = self.load_commands.items[match.seg].Segment;
2992 const seg = self.load_commands.items[match.seg].segment;
29992993 const sect = seg.sections.items[match.sect];
30002994 const metadata = try section_metadata.getOrPut(match);
30012995 if (!metadata.found_existing) {
......@@ -3005,7 +2999,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
30052999 };
30063000 }
30073001
3008 log.debug("{s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
3002 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
30093003
30103004 while (true) {
30113005 const alignment = try math.powi(u32, 2, atom.alignment);
......@@ -3046,11 +3040,11 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
30463040 while (it.next()) |entry| {
30473041 const match = entry.key_ptr.*;
30483042 const metadata = entry.value_ptr.*;
3049 const seg = &self.load_commands.items[match.seg].Segment;
3043 const seg = &self.load_commands.items[match.seg].segment;
30503044 const sect = &seg.sections.items[match.sect];
30513045 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
3052 commands.segmentName(sect.*),
3053 commands.sectionName(sect.*),
3046 sect.segName(),
3047 sect.sectName(),
30543048 metadata.size,
30553049 metadata.alignment,
30563050 });
......@@ -3070,7 +3064,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
30703064 self.data_segment_cmd_index,
30713065 }) |maybe_seg_id| {
30723066 const seg_id = maybe_seg_id orelse continue;
3073 const seg = self.load_commands.items[seg_id].Segment;
3067 const seg = self.load_commands.items[seg_id].segment;
30743068
30753069 for (seg.sections.items) |sect, sect_id| {
30763070 const match = MatchingSection{
......@@ -3140,7 +3134,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
31403134fn addLoadDylibLC(self: *MachO, id: u16) !void {
31413135 const dylib = self.dylibs.items[id];
31423136 const dylib_id = dylib.id orelse unreachable;
3143 var dylib_cmd = try commands.createLoadDylibCommand(
3137 var dylib_cmd = try macho.createLoadDylibCommand(
31443138 self.base.allocator,
31453139 dylib_id.name,
31463140 dylib_id.timestamp,
......@@ -3148,7 +3142,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
31483142 dylib_id.compatibility_version,
31493143 );
31503144 errdefer dylib_cmd.deinit(self.base.allocator);
3151 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
3145 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
31523146 self.load_commands_dirty = true;
31533147}
31543148
......@@ -3156,7 +3150,7 @@ fn addCodeSignatureLC(self: *MachO) !void {
31563150 if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;
31573151 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
31583152 try self.load_commands.append(self.base.allocator, .{
3159 .LinkeditData = .{
3153 .linkedit_data = .{
31603154 .cmd = macho.LC_CODE_SIGNATURE,
31613155 .cmdsize = @sizeOf(macho.linkedit_data_command),
31623156 .dataoff = 0,
......@@ -3171,7 +3165,7 @@ fn setEntryPoint(self: *MachO) !void {
31713165
31723166 // TODO we should respect the -entry flag passed in by the user to set a custom
31733167 // entrypoint. For now, assume default of `_main`.
3174 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3168 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
31753169 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
31763170 .bytes = &self.strtab,
31773171 }) orelse {
......@@ -3181,7 +3175,7 @@ fn setEntryPoint(self: *MachO) !void {
31813175 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
31823176 assert(resolv.where == .global);
31833177 const sym = self.globals.items[resolv.where_index];
3184 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
3178 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
31853179 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
31863180 ec.stacksize = self.base.options.stack_size_override orelse 0;
31873181 self.entry_addr = sym.n_value;
......@@ -3878,7 +3872,7 @@ fn populateMissingMetadata(self: *MachO) !void {
38783872 if (self.pagezero_segment_cmd_index == null) {
38793873 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
38803874 try self.load_commands.append(self.base.allocator, .{
3881 .Segment = .{
3875 .segment = .{
38823876 .inner = .{
38833877 .segname = makeStaticString("__PAGEZERO"),
38843878 .vmsize = pagezero_vmsize,
......@@ -3899,7 +3893,7 @@ fn populateMissingMetadata(self: *MachO) !void {
38993893 break :blk needed_size;
39003894 } else 0;
39013895 try self.load_commands.append(self.base.allocator, .{
3902 .Segment = .{
3896 .segment = .{
39033897 .inner = .{
39043898 .segname = makeStaticString("__TEXT"),
39053899 .vmaddr = pagezero_vmsize,
......@@ -4003,7 +3997,7 @@ fn populateMissingMetadata(self: *MachO) !void {
40033997 });
40043998 }
40053999 try self.load_commands.append(self.base.allocator, .{
4006 .Segment = .{
4000 .segment = .{
40074001 .inner = .{
40084002 .segname = makeStaticString("__DATA_CONST"),
40094003 .vmaddr = vmaddr,
......@@ -4052,7 +4046,7 @@ fn populateMissingMetadata(self: *MachO) !void {
40524046 });
40534047 }
40544048 try self.load_commands.append(self.base.allocator, .{
4055 .Segment = .{
4049 .segment = .{
40564050 .inner = .{
40574051 .segname = makeStaticString("__DATA"),
40584052 .vmaddr = vmaddr,
......@@ -4136,7 +4130,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41364130 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
41374131 },
41384132 );
4139 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4133 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
41404134 const sect = seg.sections.items[self.tlv_bss_section_index.?];
41414135 self.tlv_bss_file_offset = sect.offset;
41424136 }
......@@ -4153,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41534147 .flags = macho.S_ZEROFILL,
41544148 },
41554149 );
4156 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4150 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
41574151 const sect = seg.sections.items[self.bss_section_index.?];
41584152 self.bss_file_offset = sect.offset;
41594153 }
......@@ -4169,7 +4163,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41694163 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
41704164 }
41714165 try self.load_commands.append(self.base.allocator, .{
4172 .Segment = .{
4166 .segment = .{
41734167 .inner = .{
41744168 .segname = makeStaticString("__LINKEDIT"),
41754169 .vmaddr = vmaddr,
......@@ -4185,7 +4179,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41854179 if (self.dyld_info_cmd_index == null) {
41864180 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
41874181 try self.load_commands.append(self.base.allocator, .{
4188 .DyldInfoOnly = .{
4182 .dyld_info_only = .{
41894183 .cmd = macho.LC_DYLD_INFO_ONLY,
41904184 .cmdsize = @sizeOf(macho.dyld_info_command),
41914185 .rebase_off = 0,
......@@ -4206,7 +4200,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42064200 if (self.symtab_cmd_index == null) {
42074201 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
42084202 try self.load_commands.append(self.base.allocator, .{
4209 .Symtab = .{
4203 .symtab = .{
42104204 .cmd = macho.LC_SYMTAB,
42114205 .cmdsize = @sizeOf(macho.symtab_command),
42124206 .symoff = 0,
......@@ -4221,7 +4215,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42214215 if (self.dysymtab_cmd_index == null) {
42224216 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
42234217 try self.load_commands.append(self.base.allocator, .{
4224 .Dysymtab = .{
4218 .dysymtab = .{
42254219 .cmd = macho.LC_DYSYMTAB,
42264220 .cmdsize = @sizeOf(macho.dysymtab_command),
42274221 .ilocalsym = 0,
......@@ -4254,7 +4248,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42544248 @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,
42554249 @sizeOf(u64),
42564250 ));
4257 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
4251 var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
42584252 .cmd = macho.LC_LOAD_DYLINKER,
42594253 .cmdsize = cmdsize,
42604254 .name = @sizeOf(macho.dylinker_command),
......@@ -4262,14 +4256,14 @@ fn populateMissingMetadata(self: *MachO) !void {
42624256 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
42634257 mem.set(u8, dylinker_cmd.data, 0);
42644258 mem.copy(u8, dylinker_cmd.data, mem.sliceTo(default_dyld_path, 0));
4265 try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
4259 try self.load_commands.append(self.base.allocator, .{ .dylinker = dylinker_cmd });
42664260 self.load_commands_dirty = true;
42674261 }
42684262
42694263 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
42704264 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
42714265 try self.load_commands.append(self.base.allocator, .{
4272 .Main = .{
4266 .main = .{
42734267 .cmd = macho.LC_MAIN,
42744268 .cmdsize = @sizeOf(macho.entry_point_command),
42754269 .entryoff = 0x0,
......@@ -4289,7 +4283,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42894283 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
42904284 const compat_version = self.base.options.compatibility_version orelse
42914285 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4292 var dylib_cmd = try commands.createLoadDylibCommand(
4286 var dylib_cmd = try macho.createLoadDylibCommand(
42934287 self.base.allocator,
42944288 install_name,
42954289 2,
......@@ -4298,14 +4292,14 @@ fn populateMissingMetadata(self: *MachO) !void {
42984292 );
42994293 errdefer dylib_cmd.deinit(self.base.allocator);
43004294 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
4301 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
4295 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
43024296 self.load_commands_dirty = true;
43034297 }
43044298
43054299 if (self.source_version_cmd_index == null) {
43064300 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
43074301 try self.load_commands.append(self.base.allocator, .{
4308 .SourceVersion = .{
4302 .source_version = .{
43094303 .cmd = macho.LC_SOURCE_VERSION,
43104304 .cmdsize = @sizeOf(macho.source_version_command),
43114305 .version = 0x0,
......@@ -4332,7 +4326,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43324326 break :blk sdk_version;
43334327 } else platform_version;
43344328 const is_simulator_abi = self.base.options.target.abi == .simulator;
4335 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
4329 var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{
43364330 .cmd = macho.LC_BUILD_VERSION,
43374331 .cmdsize = cmdsize,
43384332 .platform = switch (self.base.options.target.os.tag) {
......@@ -4353,7 +4347,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43534347 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
43544348 mem.set(u8, cmd.data, 0);
43554349 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4356 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
4350 try self.load_commands.append(self.base.allocator, .{ .build_version = cmd });
43574351 self.load_commands_dirty = true;
43584352 }
43594353
......@@ -4365,14 +4359,14 @@ fn populateMissingMetadata(self: *MachO) !void {
43654359 .uuid = undefined,
43664360 };
43674361 std.crypto.random.bytes(&uuid_cmd.uuid);
4368 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
4362 try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });
43694363 self.load_commands_dirty = true;
43704364 }
43714365
43724366 if (self.function_starts_cmd_index == null) {
43734367 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
43744368 try self.load_commands.append(self.base.allocator, .{
4375 .LinkeditData = .{
4369 .linkedit_data = .{
43764370 .cmd = macho.LC_FUNCTION_STARTS,
43774371 .cmdsize = @sizeOf(macho.linkedit_data_command),
43784372 .dataoff = 0,
......@@ -4385,7 +4379,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43854379 if (self.data_in_code_cmd_index == null) {
43864380 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
43874381 try self.load_commands.append(self.base.allocator, .{
4388 .LinkeditData = .{
4382 .linkedit_data = .{
43894383 .cmd = macho.LC_DATA_IN_CODE,
43904384 .cmdsize = @sizeOf(macho.linkedit_data_command),
43914385 .dataoff = 0,
......@@ -4399,8 +4393,8 @@ fn populateMissingMetadata(self: *MachO) !void {
43994393}
44004394
44014395fn allocateTextSegment(self: *MachO) !void {
4402 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4403 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
4396 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
4397 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].segment.inner.vmsize;
44044398 seg.inner.fileoff = 0;
44054399 seg.inner.vmaddr = base_vmaddr;
44064400
......@@ -4436,30 +4430,30 @@ fn allocateTextSegment(self: *MachO) !void {
44364430}
44374431
44384432fn allocateDataConstSegment(self: *MachO) !void {
4439 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4440 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4433 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
4434 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
44414435 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
44424436 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
44434437 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
44444438}
44454439
44464440fn allocateDataSegment(self: *MachO) !void {
4447 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4448 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4441 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
4442 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
44494443 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
44504444 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
44514445 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
44524446}
44534447
44544448fn allocateLinkeditSegment(self: *MachO) void {
4455 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4456 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4449 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
4450 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
44574451 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
44584452 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
44594453}
44604454
44614455fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
4462 const seg = &self.load_commands.items[index].Segment;
4456 const seg = &self.load_commands.items[index].segment;
44634457
44644458 // Allocate the sections according to their alignment at the beginning of the segment.
44654459 var start: u64 = offset;
......@@ -4491,7 +4485,7 @@ fn initSection(
44914485 alignment: u32,
44924486 opts: InitSectionOpts,
44934487) !u16 {
4494 const seg = &self.load_commands.items[segment_id].Segment;
4488 const seg = &self.load_commands.items[segment_id].segment;
44954489 var sect = macho.section_64{
44964490 .sectname = makeStaticString(sectname),
44974491 .segname = seg.inner.segname,
......@@ -4507,8 +4501,8 @@ fn initSection(
45074501 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
45084502 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
45094503 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4510 commands.segmentName(sect),
4511 commands.sectionName(sect),
4504 sect.segName(),
4505 sect.sectName(),
45124506 off,
45134507 off + size,
45144508 });
......@@ -4535,7 +4529,7 @@ fn initSection(
45354529}
45364530
45374531fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {
4538 const seg = self.load_commands.items[segment_id].Segment;
4532 const seg = self.load_commands.items[segment_id].segment;
45394533 if (seg.sections.items.len == 0) {
45404534 return if (start) |v| v else seg.inner.fileoff;
45414535 }
......@@ -4545,7 +4539,7 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64
45454539}
45464540
45474541fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
4548 const seg = &self.load_commands.items[seg_id].Segment;
4542 const seg = &self.load_commands.items[seg_id].segment;
45494543 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
45504544 assert(new_seg_size > seg.inner.filesize);
45514545 const offset_amt = new_seg_size - seg.inner.filesize;
......@@ -4567,13 +4561,13 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
45674561 // TODO We should probably nop the expanded by distance, or put 0s.
45684562
45694563 // TODO copyRangeAll doesn't automatically extend the file on macOS.
4570 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4564 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
45714565 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
45724566 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
45734567
45744568 var next: usize = seg_id + 1;
45754569 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4576 const next_seg = &self.load_commands.items[next].Segment;
4570 const next_seg = &self.load_commands.items[next].segment;
45774571 _ = try self.base.file.?.copyRangeAll(
45784572 next_seg.inner.fileoff,
45794573 self.base.file.?,
......@@ -4596,8 +4590,8 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
45964590 moved_sect.addr += offset_amt;
45974591
45984592 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4599 commands.segmentName(moved_sect.*),
4600 commands.sectionName(moved_sect.*),
4593 moved_sect.segName(),
4594 moved_sect.sectName(),
46014595 moved_sect.offset,
46024596 moved_sect.offset + moved_sect.size,
46034597 moved_sect.addr,
......@@ -4616,7 +4610,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
46164610 const tracy = trace(@src());
46174611 defer tracy.end();
46184612
4619 const seg = &self.load_commands.items[match.seg].Segment;
4613 const seg = &self.load_commands.items[match.seg].segment;
46204614 const sect = &seg.sections.items[match.sect];
46214615
46224616 const alignment = try math.powi(u32, 2, sect.@"align");
......@@ -4670,8 +4664,8 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
46704664 moved_sect.addr += offset_amt;
46714665
46724666 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4673 commands.segmentName(moved_sect.*),
4674 commands.sectionName(moved_sect.*),
4667 moved_sect.segName(),
4668 moved_sect.sectName(),
46754669 moved_sect.offset,
46764670 moved_sect.offset + moved_sect.size,
46774671 moved_sect.addr,
......@@ -4687,7 +4681,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
46874681}
46884682
46894683fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
4690 const seg = self.load_commands.items[segment_id].Segment;
4684 const seg = self.load_commands.items[segment_id].segment;
46914685 assert(start >= seg.inner.fileoff);
46924686 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
46934687 if (start > min_pos) return 0;
......@@ -4699,7 +4693,7 @@ fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
46994693}
47004694
47014695fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
4702 const seg = self.load_commands.items[segment_id].Segment;
4696 const seg = self.load_commands.items[segment_id].segment;
47034697 var max_alignment: u32 = 1;
47044698 var next = start_sect_id;
47054699 while (next < seg.sections.items.len) : (next += 1) {
......@@ -4714,7 +4708,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
47144708 const tracy = trace(@src());
47154709 defer tracy.end();
47164710
4717 const seg = &self.load_commands.items[match.seg].Segment;
4711 const seg = &self.load_commands.items[match.seg].segment;
47184712 const sect = &seg.sections.items[match.sect];
47194713 var free_list = self.atom_free_lists.get(match).?;
47204714 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
......@@ -4818,7 +4812,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
48184812}
48194813
48204814fn addAtomAndBumpSectionSize(self: *MachO, atom: *Atom, match: MatchingSection) !void {
4821 const seg = &self.load_commands.items[match.seg].Segment;
4815 const seg = &self.load_commands.items[match.seg].segment;
48224816 const sect = &seg.sections.items[match.sect];
48234817 const alignment = try math.powi(u32, 2, atom.alignment);
48244818 sect.size = mem.alignForwardGeneric(u64, sect.size, alignment) + atom.size;
......@@ -4865,11 +4859,11 @@ const NextSegmentAddressAndOffset = struct {
48654859fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
48664860 var prev_segment_idx: ?usize = null; // We use optional here for safety.
48674861 for (self.load_commands.items) |cmd, i| {
4868 if (cmd == .Segment) {
4862 if (cmd == .segment) {
48694863 prev_segment_idx = i;
48704864 }
48714865 }
4872 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
4866 const prev_segment = self.load_commands.items[prev_segment_idx.?].segment;
48734867 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
48744868 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
48754869 return .{
......@@ -4888,7 +4882,7 @@ fn sortSections(self: *MachO) !void {
48884882
48894883 {
48904884 // __TEXT segment
4891 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4885 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
48924886 var sections = seg.sections.toOwnedSlice(self.base.allocator);
48934887 defer self.base.allocator.free(sections);
48944888 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
......@@ -4920,7 +4914,7 @@ fn sortSections(self: *MachO) !void {
49204914
49214915 {
49224916 // __DATA_CONST segment
4923 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4917 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
49244918 var sections = seg.sections.toOwnedSlice(self.base.allocator);
49254919 defer self.base.allocator.free(sections);
49264920 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
......@@ -4947,7 +4941,7 @@ fn sortSections(self: *MachO) !void {
49474941
49484942 {
49494943 // __DATA segment
4950 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4944 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
49514945 var sections = seg.sections.toOwnedSlice(self.base.allocator);
49524946 defer self.base.allocator.free(sections);
49534947 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
......@@ -5003,7 +4997,7 @@ fn sortSections(self: *MachO) !void {
50034997 {
50044998 // Create new section ordinals.
50054999 self.section_ordinals.clearRetainingCapacity();
5006 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5000 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
50075001 for (text_seg.sections.items) |_, sect_id| {
50085002 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
50095003 .seg = self.text_segment_cmd_index.?,
......@@ -5011,7 +5005,7 @@ fn sortSections(self: *MachO) !void {
50115005 });
50125006 assert(!res.found_existing);
50135007 }
5014 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
5008 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
50155009 for (data_const_seg.sections.items) |_, sect_id| {
50165010 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
50175011 .seg = self.data_const_segment_cmd_index.?,
......@@ -5019,7 +5013,7 @@ fn sortSections(self: *MachO) !void {
50195013 });
50205014 assert(!res.found_existing);
50215015 }
5022 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5016 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
50235017 for (data_seg.sections.items) |_, sect_id| {
50245018 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
50255019 .seg = self.data_segment_cmd_index.?,
......@@ -5044,9 +5038,9 @@ fn updateSectionOrdinals(self: *MachO) !void {
50445038
50455039 var new_ordinal: u8 = 0;
50465040 for (self.load_commands.items) |lc, lc_id| {
5047 if (lc != .Segment) break;
5041 if (lc != .segment) break;
50485042
5049 for (lc.Segment.sections.items) |_, sect_id| {
5043 for (lc.segment.sections.items) |_, sect_id| {
50505044 const match = MatchingSection{
50515045 .seg = @intCast(u16, lc_id),
50525046 .sect = @intCast(u16, sect_id),
......@@ -5089,7 +5083,7 @@ fn writeDyldInfoData(self: *MachO) !void {
50895083
50905084 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
50915085
5092 const seg = self.load_commands.items[match.seg].Segment;
5086 const seg = self.load_commands.items[match.seg].segment;
50935087
50945088 while (true) {
50955089 const sym = self.locals.items[atom.local_sym_index];
......@@ -5159,7 +5153,7 @@ fn writeDyldInfoData(self: *MachO) !void {
51595153 {
51605154 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
51615155 log.debug("generating export trie", .{});
5162 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5156 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
51635157 const base_address = text_segment.inner.vmaddr;
51645158
51655159 for (self.globals.items) |sym| {
......@@ -5177,8 +5171,8 @@ fn writeDyldInfoData(self: *MachO) !void {
51775171 try trie.finalize(self.base.allocator);
51785172 }
51795173
5180 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5181 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
5174 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5175 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;
51825176 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
51835177 const bind_size = try bind.bindInfoSize(bind_pointers.items);
51845178 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
......@@ -5248,7 +5242,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
52485242 .sect = self.la_symbol_ptr_section_index.?,
52495243 }).?;
52505244 const base_addr = blk: {
5251 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5245 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
52525246 break :blk seg.inner.vmaddr;
52535247 };
52545248
......@@ -5312,7 +5306,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
53125306 }
53135307
53145308 const sect = blk: {
5315 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5309 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
53165310 break :blk seg.sections.items[self.stub_helper_section_index.?];
53175311 };
53185312 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
......@@ -5353,7 +5347,7 @@ fn writeFunctionStarts(self: *MachO) !void {
53535347 var offsets = std.ArrayList(u32).init(self.base.allocator);
53545348 defer offsets.deinit();
53555349
5356 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5350 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
53575351 var last_off: u32 = 0;
53585352
53595353 while (true) {
......@@ -5410,8 +5404,8 @@ fn writeFunctionStarts(self: *MachO) !void {
54105404 }
54115405
54125406 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64)));
5413 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5414 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].LinkeditData;
5407 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5408 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
54155409
54165410 fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
54175411 fn_cmd.datasize = needed_size;
......@@ -5444,7 +5438,7 @@ fn writeDices(self: *MachO) !void {
54445438 atom = prev;
54455439 }
54465440
5447 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5441 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
54485442 const text_sect = text_seg.sections.items[self.text_section_index.?];
54495443
54505444 while (true) {
......@@ -5468,8 +5462,8 @@ fn writeDices(self: *MachO) !void {
54685462 } else break;
54695463 }
54705464
5471 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5472 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
5465 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5466 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
54735467 const needed_size = @intCast(u32, buf.items.len);
54745468
54755469 dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
......@@ -5489,8 +5483,8 @@ fn writeSymbolTable(self: *MachO) !void {
54895483 const tracy = trace(@src());
54905484 defer tracy.end();
54915485
5492 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5493 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5486 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5487 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
54945488 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
54955489
54965490 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
......@@ -5594,18 +5588,18 @@ fn writeSymbolTable(self: *MachO) !void {
55945588 seg.inner.filesize += locals_size + exports_size + undefs_size;
55955589
55965590 // Update dynamic symbol table.
5597 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
5591 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
55985592 dysymtab.nlocalsym = @intCast(u32, nlocals);
55995593 dysymtab.iextdefsym = dysymtab.nlocalsym;
56005594 dysymtab.nextdefsym = @intCast(u32, nexports);
56015595 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
56025596 dysymtab.nundefsym = @intCast(u32, nundefs);
56035597
5604 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5598 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
56055599 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
5606 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
5600 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
56075601 const got = &data_const_segment.sections.items[self.got_section_index.?];
5608 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5602 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
56095603 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
56105604
56115605 const nstubs = @intCast(u32, self.stubs_map.keys().len);
......@@ -5668,8 +5662,8 @@ fn writeStringTable(self: *MachO) !void {
56685662 const tracy = trace(@src());
56695663 defer tracy.end();
56705664
5671 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5672 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5665 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5666 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
56735667 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
56745668 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
56755669 seg.inner.filesize += symtab.strsize;
......@@ -5689,7 +5683,7 @@ fn writeLinkeditSegment(self: *MachO) !void {
56895683 const tracy = trace(@src());
56905684 defer tracy.end();
56915685
5692 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5686 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
56935687 seg.inner.filesize = 0;
56945688
56955689 try self.writeDyldInfoData();
......@@ -5705,8 +5699,8 @@ fn writeCodeSignaturePadding(self: *MachO) !void {
57055699 const tracy = trace(@src());
57065700 defer tracy.end();
57075701
5708 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5709 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
5702 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5703 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
57105704 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
57115705 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
57125706 self.base.options.emit.?.sub_path,
......@@ -5732,8 +5726,8 @@ fn writeCodeSignature(self: *MachO) !void {
57325726 const tracy = trace(@src());
57335727 defer tracy.end();
57345728
5735 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5736 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
5729 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5730 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
57375731
57385732 var code_sig: CodeSignature = .{};
57395733 defer code_sig.deinit(self.base.allocator);
......@@ -5784,9 +5778,8 @@ fn writeLoadCommands(self: *MachO) !void {
57845778
57855779/// Writes Mach-O file header.
57865780fn writeHeader(self: *MachO) !void {
5787 var header = commands.emptyHeader(.{
5788 .flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL,
5789 });
5781 var header: macho.mach_header_64 = .{};
5782 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
57905783
57915784 switch (self.base.options.target.cpu.arch) {
57925785 .aarch64 => {
......@@ -5959,12 +5952,9 @@ fn snapshotState(self: *MachO) !void {
59595952 var nodes = std.ArrayList(Snapshot.Node).init(arena);
59605953
59615954 for (self.section_ordinals.keys()) |key| {
5962 const seg = self.load_commands.items[key.seg].Segment;
5955 const seg = self.load_commands.items[key.seg].segment;
59635956 const sect = seg.sections.items[key.sect];
5964 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{
5965 commands.segmentName(sect),
5966 commands.sectionName(sect),
5967 });
5957 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
59685958 try nodes.append(.{
59695959 .address = sect.addr,
59705960 .tag = .section_start,
......@@ -6035,12 +6025,12 @@ fn snapshotState(self: *MachO) !void {
60356025 const is_tlv = is_tlv: {
60366026 const source_sym = self.locals.items[atom.local_sym_index];
60376027 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
6038 const match_seg = self.load_commands.items[match.seg].Segment;
6028 const match_seg = self.load_commands.items[match.seg].segment;
60396029 const match_sect = match_seg.sections.items[match.sect];
6040 break :is_tlv commands.sectionType(match_sect) == macho.S_THREAD_LOCAL_VARIABLES;
6030 break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
60416031 };
60426032 if (is_tlv) {
6043 const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
6033 const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
60446034 const base_address = inner: {
60456035 if (self.tlv_data_section_index) |i| {
60466036 break :inner match_seg.sections.items[i].addr;
......@@ -6200,14 +6190,14 @@ fn logSymtab(self: MachO) void {
62006190
62016191fn logSectionOrdinals(self: MachO) void {
62026192 for (self.section_ordinals.keys()) |match, i| {
6203 const seg = self.load_commands.items[match.seg].Segment;
6193 const seg = self.load_commands.items[match.seg].segment;
62046194 const sect = seg.sections.items[match.sect];
62056195 log.debug("ord {d}: {d},{d} => {s},{s}", .{
62066196 i + 1,
62076197 match.seg,
62086198 match.sect,
6209 commands.segmentName(sect),
6210 commands.sectionName(sect),
6199 sect.segName(),
6200 sect.sectName(),
62116201 });
62126202 }
62136203}
src/link/MachO/Atom.zig+9-10
......@@ -4,7 +4,6 @@ const std = @import("std");
44const build_options = @import("build_options");
55const aarch64 = @import("../../arch/aarch64/bits.zig");
66const assert = std.debug.assert;
7const commands = @import("commands.zig");
87const log = std.log.scoped(.link);
98const macho = std.macho;
109const math = std.math;
......@@ -342,7 +341,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
342341 if (rel.r_extern == 0) {
343342 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
344343 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
345 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
344 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
346345 const sect = seg.sections.items[sect_id];
347346 const match = (try context.macho_file.getMatchingSection(sect)) orelse
348347 unreachable;
......@@ -398,7 +397,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
398397 else
399398 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
400399 if (rel.r_extern == 0) {
401 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
400 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
402401 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
403402 addend -= @intCast(i64, target_sect_base_addr);
404403 }
......@@ -425,7 +424,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
425424 else
426425 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
427426 if (rel.r_extern == 0) {
428 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
427 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
429428 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
430429 addend -= @intCast(i64, target_sect_base_addr);
431430 }
......@@ -447,7 +446,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
447446 if (rel.r_extern == 0) {
448447 // Note for the future self: when r_extern == 0, we should subtract correction from the
449448 // addend.
450 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
449 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
451450 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
452451 addend += @intCast(i64, context.base_addr + offset + 4) -
453452 @intCast(i64, target_sect_base_addr);
......@@ -490,9 +489,9 @@ fn addPtrBindingOrRebase(
490489 .local => {
491490 const source_sym = context.macho_file.locals.items[self.local_sym_index];
492491 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
493 const seg = context.macho_file.load_commands.items[match.seg].Segment;
492 const seg = context.macho_file.load_commands.items[match.seg].segment;
494493 const sect = seg.sections.items[match.sect];
495 const sect_type = commands.sectionType(sect);
494 const sect_type = sect.type_();
496495
497496 const should_rebase = rebase: {
498497 if (rel.r_length != 3) break :rebase false;
......@@ -705,9 +704,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
705704 const is_tlv = is_tlv: {
706705 const source_sym = macho_file.locals.items[self.local_sym_index];
707706 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
708 const seg = macho_file.load_commands.items[match.seg].Segment;
707 const seg = macho_file.load_commands.items[match.seg].segment;
709708 const sect = seg.sections.items[match.sect];
710 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
709 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
711710 };
712711 if (is_tlv) {
713712 // For TLV relocations, the value specified as a relocation is the displacement from the
......@@ -715,7 +714,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
715714 // defined TLV template init section in the following order:
716715 // * wrt to __thread_data if defined, then
717716 // * wrt to __thread_bss
718 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment;
717 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].segment;
719718 const base_address = inner: {
720719 if (macho_file.tlv_data_section_index) |i| {
721720 break :inner seg.sections.items[i].addr;
src/link/MachO/DebugSymbols.zig+87-54
......@@ -12,15 +12,12 @@ const leb = std.leb;
1212const Allocator = mem.Allocator;
1313
1414const build_options = @import("build_options");
15const commands = @import("commands.zig");
1615const trace = @import("../../tracy.zig").trace;
17const LoadCommand = commands.LoadCommand;
1816const Module = @import("../../Module.zig");
1917const Type = @import("../../type.zig").Type;
2018const link = @import("../../link.zig");
2119const MachO = @import("../MachO.zig");
2220const TextBlock = MachO.TextBlock;
23const SegmentCommand = commands.SegmentCommand;
2421const SrcFn = MachO.SrcFn;
2522const makeStaticString = MachO.makeStaticString;
2623const padToIdeal = MachO.padToIdeal;
......@@ -31,7 +28,7 @@ base: *MachO,
3128file: fs.File,
3229
3330/// Table of all load commands
34load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
31load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
3532/// __PAGEZERO segment
3633pagezero_segment_cmd_index: ?u16 = null,
3734/// __TEXT segment
......@@ -113,7 +110,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
113110 }
114111 if (self.symtab_cmd_index == null) {
115112 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
116 const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].Symtab;
113 const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].symtab;
117114 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
118115 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
119116
......@@ -124,7 +121,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
124121 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
125122
126123 try self.load_commands.append(allocator, .{
127 .Symtab = .{
124 .symtab = .{
128125 .cmd = macho.LC_SYMTAB,
129126 .cmdsize = @sizeOf(macho.symtab_command),
130127 .symoff = @intCast(u32, symtab_off),
......@@ -138,48 +135,48 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
138135 }
139136 if (self.pagezero_segment_cmd_index == null) {
140137 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
141 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
138 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
142139 const cmd = try self.copySegmentCommand(allocator, base_cmd);
143 try self.load_commands.append(allocator, .{ .Segment = cmd });
140 try self.load_commands.append(allocator, .{ .segment = cmd });
144141 self.load_commands_dirty = true;
145142 }
146143 if (self.text_segment_cmd_index == null) {
147144 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
148 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
145 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
149146 const cmd = try self.copySegmentCommand(allocator, base_cmd);
150 try self.load_commands.append(allocator, .{ .Segment = cmd });
147 try self.load_commands.append(allocator, .{ .segment = cmd });
151148 self.load_commands_dirty = true;
152149 }
153150 if (self.data_const_segment_cmd_index == null) outer: {
154151 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
155152 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
156 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].Segment;
153 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].segment;
157154 const cmd = try self.copySegmentCommand(allocator, base_cmd);
158 try self.load_commands.append(allocator, .{ .Segment = cmd });
155 try self.load_commands.append(allocator, .{ .segment = cmd });
159156 self.load_commands_dirty = true;
160157 }
161158 if (self.data_segment_cmd_index == null) outer: {
162159 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
163160 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
164 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
161 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].segment;
165162 const cmd = try self.copySegmentCommand(allocator, base_cmd);
166 try self.load_commands.append(allocator, .{ .Segment = cmd });
163 try self.load_commands.append(allocator, .{ .segment = cmd });
167164 self.load_commands_dirty = true;
168165 }
169166 if (self.linkedit_segment_cmd_index == null) {
170167 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
171 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].Segment;
168 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
172169 var cmd = try self.copySegmentCommand(allocator, base_cmd);
173170 cmd.inner.vmsize = self.linkedit_size;
174171 cmd.inner.fileoff = self.linkedit_off;
175172 cmd.inner.filesize = self.linkedit_size;
176 try self.load_commands.append(allocator, .{ .Segment = cmd });
173 try self.load_commands.append(allocator, .{ .segment = cmd });
177174 self.load_commands_dirty = true;
178175 }
179176 if (self.dwarf_segment_cmd_index == null) {
180177 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
181178
182 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
179 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
183180 const ideal_size: u16 = 200 + 128 + 160 + 250;
184181 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);
185182 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
......@@ -188,7 +185,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
188185 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
189186
190187 try self.load_commands.append(allocator, .{
191 .Segment = .{
188 .segment = .{
192189 .inner = .{
193190 .segname = makeStaticString("__DWARF"),
194191 .vmaddr = vmaddr,
......@@ -228,7 +225,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
228225}
229226
230227fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
231 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
228 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
232229 var sect = macho.section_64{
233230 .sectname = makeStaticString(sectname),
234231 .segname = seg.inner.segname,
......@@ -236,13 +233,13 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
236233 .@"align" = alignment,
237234 };
238235 const alignment_pow_2 = try math.powi(u32, 2, alignment);
239 const off = seg.findFreeSpace(size, alignment_pow_2, null);
236 const off = self.findFreeSpace(size, alignment_pow_2);
240237
241238 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
242239
243240 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
244 commands.segmentName(sect),
245 commands.sectionName(sect),
241 sect.segName(),
242 sect.sectName(),
246243 off,
247244 off + size,
248245 });
......@@ -268,6 +265,28 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
268265 return index;
269266}
270267
268fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
269 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
270 const end = start + padToIdeal(size);
271 for (seg.sections.items) |section| {
272 const increased_size = padToIdeal(section.size);
273 const test_end = section.offset + increased_size;
274 if (end > section.offset and start < test_end) {
275 return test_end;
276 }
277 }
278 return null;
279}
280
281fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
282 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
283 var offset: u64 = seg.inner.fileoff;
284 while (self.detectAllocCollision(offset, object_size)) |item_end| {
285 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
286 }
287 return offset;
288}
289
271290pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {
272291 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
273292 // Zig source code.
......@@ -275,7 +294,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
275294 const init_len_size: usize = 4;
276295
277296 if (self.debug_abbrev_section_dirty) {
278 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
297 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
279298 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
280299
281300 // These are LEB encoded but since the values are all less than 127
......@@ -320,10 +339,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
320339 };
321340
322341 const needed_size = abbrev_buf.len;
323 const allocated_size = dwarf_segment.allocatedSize(debug_abbrev_sect.offset);
342 const allocated_size = self.allocatedSize(debug_abbrev_sect.offset);
324343 if (needed_size > allocated_size) {
325344 debug_abbrev_sect.size = 0; // free the space
326 const offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
345 const offset = self.findFreeSpace(needed_size, 1);
327346 debug_abbrev_sect.offset = @intCast(u32, offset);
328347 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
329348 }
......@@ -345,7 +364,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
345364 // leave debug_info_header_dirty=true.
346365 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
347366 const last_dbg_info_decl = self.dbg_info_decl_last.?;
348 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
367 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
349368 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
350369
351370 // We have a function to compute the upper bound size, because it's needed
......@@ -372,7 +391,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
372391 const producer_strp = try self.makeDebugString(allocator, link.producer_string);
373392 // Currently only one compilation unit is supported, so the address range is simply
374393 // identical to the main program header virtual address and memory size.
375 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
394 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
376395 const text_section = text_segment.sections.items[self.text_section_index.?];
377396 const low_pc = text_section.addr;
378397 const high_pc = text_section.addr + text_section.size;
......@@ -399,7 +418,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
399418 }
400419
401420 if (self.debug_aranges_section_dirty) {
402 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
421 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
403422 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
404423
405424 // Enough for all the data without resizing. When support for more compilation units
......@@ -426,7 +445,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
426445
427446 // Currently only one compilation unit is supported, so the address range is simply
428447 // identical to the main program header virtual address and memory size.
429 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
448 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
430449 const text_section = text_segment.sections.items[self.text_section_index.?];
431450 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);
432451 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
......@@ -442,10 +461,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
442461 mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));
443462
444463 const needed_size = di_buf.items.len;
445 const allocated_size = dwarf_segment.allocatedSize(debug_aranges_sect.offset);
464 const allocated_size = self.allocatedSize(debug_aranges_sect.offset);
446465 if (needed_size > allocated_size) {
447466 debug_aranges_sect.size = 0; // free the space
448 const new_offset = dwarf_segment.findFreeSpace(needed_size, 16, null);
467 const new_offset = self.findFreeSpace(needed_size, 16);
449468 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
450469 debug_aranges_sect.offset = @intCast(u32, new_offset);
451470 }
......@@ -467,7 +486,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
467486 const dbg_line_prg_end = self.getDebugLineProgramEnd();
468487 assert(dbg_line_prg_end != 0);
469488
470 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
489 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
471490 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
472491
473492 // The size of this header is variable, depending on the number of directories,
......@@ -540,15 +559,15 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
540559 self.debug_line_header_dirty = false;
541560 }
542561 {
543 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
562 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
544563 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
545564 if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {
546 const allocated_size = dwarf_segment.allocatedSize(debug_strtab_sect.offset);
565 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
547566 const needed_size = self.debug_string_table.items.len;
548567
549568 if (needed_size > allocated_size) {
550569 debug_strtab_sect.size = 0; // free the space
551 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
570 const new_offset = self.findFreeSpace(needed_size, 1);
552571 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
553572 debug_strtab_sect.offset = @intCast(u32, new_offset);
554573 }
......@@ -588,8 +607,12 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
588607 self.file.close();
589608}
590609
591fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: SegmentCommand) !SegmentCommand {
592 var cmd = SegmentCommand{
610fn copySegmentCommand(
611 self: *DebugSymbols,
612 allocator: Allocator,
613 base_cmd: macho.SegmentCommand,
614) !macho.SegmentCommand {
615 var cmd = macho.SegmentCommand{
593616 .inner = .{
594617 .segname = undefined,
595618 .cmdsize = base_cmd.inner.cmdsize,
......@@ -633,7 +656,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: Segme
633656}
634657
635658fn updateDwarfSegment(self: *DebugSymbols) void {
636 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
659 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
637660 var file_size: u64 = 0;
638661 for (dwarf_segment.sections.items) |sect| {
639662 file_size += sect.size;
......@@ -670,9 +693,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
670693}
671694
672695fn writeHeader(self: *DebugSymbols) !void {
673 var header = commands.emptyHeader(.{
674 .filetype = macho.MH_DSYM,
675 });
696 var header: macho.mach_header_64 = .{};
697 header.filetype = macho.MH_DSYM;
676698
677699 switch (self.base.base.options.target.cpu.arch) {
678700 .aarch64 => {
......@@ -703,7 +725,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
703725 var min_pos: u64 = std.math.maxInt(u64);
704726
705727 if (self.symtab_cmd_index) |idx| {
706 const symtab = self.load_commands.items[idx].Symtab;
728 const symtab = self.load_commands.items[idx].symtab;
707729 if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
708730 if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
709731 }
......@@ -711,12 +733,23 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
711733 return min_pos - start;
712734}
713735
736fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
737 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
738 assert(start >= seg.inner.fileoff);
739 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
740 for (seg.sections.items) |section| {
741 if (section.offset <= start) continue;
742 if (section.offset < min_pos) min_pos = section.offset;
743 }
744 return min_pos - start;
745}
746
714747fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
715748 const end = start + padToIdeal(size);
716749
717750 if (self.symtab_cmd_index) |idx| outer: {
718751 if (self.load_commands.items.len == idx) break :outer;
719 const symtab = self.load_commands.items[idx].Symtab;
752 const symtab = self.load_commands.items[idx].symtab;
720753 {
721754 // Symbol table
722755 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
......@@ -748,7 +781,7 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
748781}
749782
750783fn relocateSymbolTable(self: *DebugSymbols) !void {
751 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
784 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
752785 const nlocals = self.base.locals.items.len;
753786 const nglobals = self.base.globals.items.len;
754787 const nsyms = nlocals + nglobals;
......@@ -781,7 +814,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
781814 const tracy = trace(@src());
782815 defer tracy.end();
783816 try self.relocateSymbolTable();
784 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
817 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
785818 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
786819 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
787820 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
......@@ -793,7 +826,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
793826 const tracy = trace(@src());
794827 defer tracy.end();
795828
796 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
829 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
797830 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
798831 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));
799832
......@@ -817,7 +850,7 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
817850 const func = decl.val.castTag(.function).?.data;
818851 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
819852
820 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
853 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
821854 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
822855 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
823856 var data: [4]u8 = undefined;
......@@ -983,7 +1016,7 @@ pub fn commitDeclDebugInfo(
9831016 // `TextBlock` and the .debug_info. If you are editing this logic, you
9841017 // probably need to edit that logic too.
9851018
986 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1019 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
9871020 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
9881021 const src_fn = &decl.fn_link.macho;
9891022 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
......@@ -1029,8 +1062,8 @@ pub fn commitDeclDebugInfo(
10291062 const last_src_fn = self.dbg_line_fn_last.?;
10301063 const needed_size = last_src_fn.off + last_src_fn.len;
10311064 if (needed_size != debug_line_sect.size) {
1032 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
1033 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1065 if (needed_size > self.allocatedSize(debug_line_sect.offset)) {
1066 const new_offset = self.findFreeSpace(needed_size, 1);
10341067 const existing_size = last_src_fn.off;
10351068
10361069 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
......@@ -1152,7 +1185,7 @@ fn updateDeclDebugInfoAllocation(
11521185 // `SrcFn` and the line number programs. If you are editing this logic, you
11531186 // probably need to edit that logic too.
11541187
1155 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1188 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
11561189 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
11571190 text_block.dbg_info_len = len;
11581191 if (self.dbg_info_decl_last) |last| blk: {
......@@ -1203,15 +1236,15 @@ fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf:
12031236 // `SrcFn` and the line number programs. If you are editing this logic, you
12041237 // probably need to edit that logic too.
12051238
1206 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1239 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
12071240 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
12081241
12091242 const last_decl = self.dbg_info_decl_last.?;
12101243 // +1 for a trailing zero to end the children of the decl tag.
12111244 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
12121245 if (needed_size != debug_info_sect.size) {
1213 if (needed_size > dwarf_segment.allocatedSize(debug_info_sect.offset)) {
1214 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1246 if (needed_size > self.allocatedSize(debug_info_sect.offset)) {
1247 const new_offset = self.findFreeSpace(needed_size, 1);
12151248 const existing_size = last_decl.dbg_info_off;
12161249
12171250 log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
src/link/MachO/Dylib.zig+6-8
......@@ -9,11 +9,9 @@ const macho = std.macho;
99const math = std.math;
1010const mem = std.mem;
1111const fat = @import("fat.zig");
12const commands = @import("commands.zig");
1312
1413const Allocator = mem.Allocator;
1514const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommand = commands.LoadCommand;
1715const MachO = @import("../MachO.zig");
1816
1917file: fs.File,
......@@ -25,7 +23,7 @@ header: ?macho.mach_header_64 = null,
2523// an offset within a file if we are linking against a fat lib
2624library_offset: u64 = 0,
2725
28load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
26load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
2927
3028symtab_cmd_index: ?u16 = null,
3129dysymtab_cmd_index: ?u16 = null,
......@@ -53,7 +51,7 @@ pub const Id = struct {
5351 };
5452 }
5553
56 pub fn fromLoadCommand(allocator: Allocator, lc: commands.GenericCommandWithData(macho.dylib_command)) !Id {
54 pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {
5755 const dylib = lc.inner.dylib;
5856 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
5957 const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));
......@@ -177,7 +175,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
177175
178176 var i: u16 = 0;
179177 while (i < self.header.?.ncmds) : (i += 1) {
180 var cmd = try LoadCommand.read(allocator, reader);
178 var cmd = try macho.LoadCommand.read(allocator, reader);
181179 switch (cmd.cmd()) {
182180 macho.LC_SYMTAB => {
183181 self.symtab_cmd_index = i;
......@@ -191,7 +189,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
191189 macho.LC_REEXPORT_DYLIB => {
192190 if (should_lookup_reexports) {
193191 // Parse install_name to dependent dylib.
194 var id = try Id.fromLoadCommand(allocator, cmd.Dylib);
192 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
195193 try dependent_libs.writeItem(id);
196194 }
197195 },
......@@ -209,12 +207,12 @@ fn parseId(self: *Dylib, allocator: Allocator) !void {
209207 self.id = try Id.default(allocator, self.name);
210208 return;
211209 };
212 self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].Dylib);
210 self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].dylib);
213211}
214212
215213fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
216214 const index = self.symtab_cmd_index orelse return;
217 const symtab_cmd = self.load_commands.items[index].Symtab;
215 const symtab_cmd = self.load_commands.items[index].symtab;
218216
219217 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
220218 defer allocator.free(symtab);
src/link/MachO/Object.zig+15-22
......@@ -11,14 +11,10 @@ const macho = std.macho;
1111const math = std.math;
1212const mem = std.mem;
1313const sort = std.sort;
14const commands = @import("commands.zig");
15const segmentName = commands.segmentName;
16const sectionName = commands.sectionName;
1714const trace = @import("../../tracy.zig").trace;
1815
1916const Allocator = mem.Allocator;
2017const Atom = @import("Atom.zig");
21const LoadCommand = commands.LoadCommand;
2218const MachO = @import("../MachO.zig");
2319
2420file: fs.File,
......@@ -28,7 +24,7 @@ file_offset: ?u32 = null,
2824
2925header: ?macho.mach_header_64 = null,
3026
31load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
27load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
3228
3329segment_cmd_index: ?u16 = null,
3430text_section_index: ?u16 = null,
......@@ -271,15 +267,15 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
271267
272268 var i: u16 = 0;
273269 while (i < header.ncmds) : (i += 1) {
274 var cmd = try LoadCommand.read(allocator, reader);
270 var cmd = try macho.LoadCommand.read(allocator, reader);
275271 switch (cmd.cmd()) {
276272 macho.LC_SEGMENT_64 => {
277273 self.segment_cmd_index = i;
278 var seg = cmd.Segment;
274 var seg = cmd.segment;
279275 for (seg.sections.items) |*sect, j| {
280276 const index = @intCast(u16, j);
281 const segname = segmentName(sect.*);
282 const sectname = sectionName(sect.*);
277 const segname = sect.segName();
278 const sectname = sect.sectName();
283279 if (mem.eql(u8, segname, "__DWARF")) {
284280 if (mem.eql(u8, sectname, "__debug_info")) {
285281 self.dwarf_debug_info_index = index;
......@@ -308,8 +304,8 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
308304 },
309305 macho.LC_SYMTAB => {
310306 self.symtab_cmd_index = i;
311 cmd.Symtab.symoff += offset;
312 cmd.Symtab.stroff += offset;
307 cmd.symtab.symoff += offset;
308 cmd.symtab.stroff += offset;
313309 },
314310 macho.LC_DYSYMTAB => {
315311 self.dysymtab_cmd_index = i;
......@@ -319,7 +315,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
319315 },
320316 macho.LC_DATA_IN_CODE => {
321317 self.data_in_code_cmd_index = i;
322 cmd.LinkeditData.dataoff += offset;
318 cmd.linkedit_data.dataoff += offset;
323319 },
324320 else => {
325321 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
......@@ -385,7 +381,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
385381 const tracy = trace(@src());
386382 defer tracy.end();
387383
388 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
384 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
389385
390386 log.debug("analysing {s}", .{self.name});
391387
......@@ -408,7 +404,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
408404 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
409405 // have to infer the start of undef section in the symtab ourselves.
410406 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
411 const dysymtab = self.load_commands.items[cmd_index].Dysymtab;
407 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
412408 break :blk dysymtab.iundefsym;
413409 } else blk: {
414410 var iundefsym: usize = sorted_all_nlists.items.len;
......@@ -424,10 +420,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
424420
425421 for (seg.sections.items) |sect, id| {
426422 const sect_id = @intCast(u8, id);
427 log.debug("putting section '{s},{s}' as an Atom", .{
428 segmentName(sect),
429 sectionName(sect),
430 });
423 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });
431424
432425 // Get matching segment/section in the final artifact.
433426 const match = (try macho_file.getMatchingSection(sect)) orelse {
......@@ -479,7 +472,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
479472 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");
480473
481474 const is_zerofill = blk: {
482 const section_type = commands.sectionType(sect);
475 const section_type = sect.type_();
483476 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
484477 };
485478 if (!is_zerofill) {
......@@ -559,7 +552,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
559552
560553fn parseSymtab(self: *Object, allocator: Allocator) !void {
561554 const index = self.symtab_cmd_index orelse return;
562 const symtab_cmd = self.load_commands.items[index].Symtab;
555 const symtab_cmd = self.load_commands.items[index].symtab;
563556
564557 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
565558 defer allocator.free(symtab);
......@@ -607,7 +600,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
607600
608601pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
609602 const index = self.data_in_code_cmd_index orelse return;
610 const data_in_code = self.load_commands.items[index].LinkeditData;
603 const data_in_code = self.load_commands.items[index].linkedit_data;
611604
612605 var buffer = try allocator.alloc(u8, data_in_code.datasize);
613606 defer allocator.free(buffer);
......@@ -626,7 +619,7 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
626619}
627620
628621fn readSection(self: Object, allocator: Allocator, index: u16) ![]u8 {
629 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
622 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
630623 const sect = seg.sections.items[index];
631624 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
632625 _ = try self.file.preadAll(buffer, sect.offset);
src/link/MachO/commands.zig deleted-523
......@@ -1,523 +0,0 @@
1const std = @import("std");
2const fs = std.fs;
3const io = std.io;
4const mem = std.mem;
5const meta = std.meta;
6const macho = std.macho;
7const testing = std.testing;
8const assert = std.debug.assert;
9
10const Allocator = std.mem.Allocator;
11const MachO = @import("../MachO.zig");
12const makeStaticString = MachO.makeStaticString;
13const padToIdeal = MachO.padToIdeal;
14
15pub const HeaderArgs = struct {
16 magic: u32 = macho.MH_MAGIC_64,
17 cputype: macho.cpu_type_t = 0,
18 cpusubtype: macho.cpu_subtype_t = 0,
19 filetype: u32 = 0,
20 flags: u32 = 0,
21 reserved: u32 = 0,
22};
23
24pub fn emptyHeader(args: HeaderArgs) macho.mach_header_64 {
25 return .{
26 .magic = args.magic,
27 .cputype = args.cputype,
28 .cpusubtype = args.cpusubtype,
29 .filetype = args.filetype,
30 .ncmds = 0,
31 .sizeofcmds = 0,
32 .flags = args.flags,
33 .reserved = args.reserved,
34 };
35}
36
37pub const LoadCommand = union(enum) {
38 Segment: SegmentCommand,
39 DyldInfoOnly: macho.dyld_info_command,
40 Symtab: macho.symtab_command,
41 Dysymtab: macho.dysymtab_command,
42 Dylinker: GenericCommandWithData(macho.dylinker_command),
43 Dylib: GenericCommandWithData(macho.dylib_command),
44 Main: macho.entry_point_command,
45 VersionMin: macho.version_min_command,
46 SourceVersion: macho.source_version_command,
47 BuildVersion: GenericCommandWithData(macho.build_version_command),
48 Uuid: macho.uuid_command,
49 LinkeditData: macho.linkedit_data_command,
50 Rpath: GenericCommandWithData(macho.rpath_command),
51 Unknown: GenericCommandWithData(macho.load_command),
52
53 pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
54 const header = try reader.readStruct(macho.load_command);
55 var buffer = try allocator.alloc(u8, header.cmdsize);
56 defer allocator.free(buffer);
57 mem.copy(u8, buffer, mem.asBytes(&header));
58 try reader.readNoEof(buffer[@sizeOf(macho.load_command)..]);
59 var stream = io.fixedBufferStream(buffer);
60
61 return switch (header.cmd) {
62 macho.LC_SEGMENT_64 => LoadCommand{
63 .Segment = try SegmentCommand.read(allocator, stream.reader()),
64 },
65 macho.LC_DYLD_INFO,
66 macho.LC_DYLD_INFO_ONLY,
67 => LoadCommand{
68 .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command),
69 },
70 macho.LC_SYMTAB => LoadCommand{
71 .Symtab = try stream.reader().readStruct(macho.symtab_command),
72 },
73 macho.LC_DYSYMTAB => LoadCommand{
74 .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command),
75 },
76 macho.LC_ID_DYLINKER,
77 macho.LC_LOAD_DYLINKER,
78 macho.LC_DYLD_ENVIRONMENT,
79 => LoadCommand{
80 .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()),
81 },
82 macho.LC_ID_DYLIB,
83 macho.LC_LOAD_WEAK_DYLIB,
84 macho.LC_LOAD_DYLIB,
85 macho.LC_REEXPORT_DYLIB,
86 => LoadCommand{
87 .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()),
88 },
89 macho.LC_MAIN => LoadCommand{
90 .Main = try stream.reader().readStruct(macho.entry_point_command),
91 },
92 macho.LC_VERSION_MIN_MACOSX,
93 macho.LC_VERSION_MIN_IPHONEOS,
94 macho.LC_VERSION_MIN_WATCHOS,
95 macho.LC_VERSION_MIN_TVOS,
96 => LoadCommand{
97 .VersionMin = try stream.reader().readStruct(macho.version_min_command),
98 },
99 macho.LC_SOURCE_VERSION => LoadCommand{
100 .SourceVersion = try stream.reader().readStruct(macho.source_version_command),
101 },
102 macho.LC_BUILD_VERSION => LoadCommand{
103 .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()),
104 },
105 macho.LC_UUID => LoadCommand{
106 .Uuid = try stream.reader().readStruct(macho.uuid_command),
107 },
108 macho.LC_FUNCTION_STARTS,
109 macho.LC_DATA_IN_CODE,
110 macho.LC_CODE_SIGNATURE,
111 => LoadCommand{
112 .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),
113 },
114 macho.LC_RPATH => LoadCommand{
115 .Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()),
116 },
117 else => LoadCommand{
118 .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()),
119 },
120 };
121 }
122
123 pub fn write(self: LoadCommand, writer: anytype) !void {
124 return switch (self) {
125 .DyldInfoOnly => |x| writeStruct(x, writer),
126 .Symtab => |x| writeStruct(x, writer),
127 .Dysymtab => |x| writeStruct(x, writer),
128 .Main => |x| writeStruct(x, writer),
129 .VersionMin => |x| writeStruct(x, writer),
130 .SourceVersion => |x| writeStruct(x, writer),
131 .Uuid => |x| writeStruct(x, writer),
132 .LinkeditData => |x| writeStruct(x, writer),
133 .Segment => |x| x.write(writer),
134 .Dylinker => |x| x.write(writer),
135 .Dylib => |x| x.write(writer),
136 .Rpath => |x| x.write(writer),
137 .BuildVersion => |x| x.write(writer),
138 .Unknown => |x| x.write(writer),
139 };
140 }
141
142 pub fn cmd(self: LoadCommand) u32 {
143 return switch (self) {
144 .DyldInfoOnly => |x| x.cmd,
145 .Symtab => |x| x.cmd,
146 .Dysymtab => |x| x.cmd,
147 .Main => |x| x.cmd,
148 .VersionMin => |x| x.cmd,
149 .SourceVersion => |x| x.cmd,
150 .Uuid => |x| x.cmd,
151 .LinkeditData => |x| x.cmd,
152 .Segment => |x| x.inner.cmd,
153 .Dylinker => |x| x.inner.cmd,
154 .Dylib => |x| x.inner.cmd,
155 .Rpath => |x| x.inner.cmd,
156 .BuildVersion => |x| x.inner.cmd,
157 .Unknown => |x| x.inner.cmd,
158 };
159 }
160
161 pub fn cmdsize(self: LoadCommand) u32 {
162 return switch (self) {
163 .DyldInfoOnly => |x| x.cmdsize,
164 .Symtab => |x| x.cmdsize,
165 .Dysymtab => |x| x.cmdsize,
166 .Main => |x| x.cmdsize,
167 .VersionMin => |x| x.cmdsize,
168 .SourceVersion => |x| x.cmdsize,
169 .LinkeditData => |x| x.cmdsize,
170 .Uuid => |x| x.cmdsize,
171 .Segment => |x| x.inner.cmdsize,
172 .Dylinker => |x| x.inner.cmdsize,
173 .Dylib => |x| x.inner.cmdsize,
174 .Rpath => |x| x.inner.cmdsize,
175 .BuildVersion => |x| x.inner.cmdsize,
176 .Unknown => |x| x.inner.cmdsize,
177 };
178 }
179
180 pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
181 return switch (self.*) {
182 .Segment => |*x| x.deinit(allocator),
183 .Dylinker => |*x| x.deinit(allocator),
184 .Dylib => |*x| x.deinit(allocator),
185 .Rpath => |*x| x.deinit(allocator),
186 .BuildVersion => |*x| x.deinit(allocator),
187 .Unknown => |*x| x.deinit(allocator),
188 else => {},
189 };
190 }
191
192 fn writeStruct(command: anytype, writer: anytype) !void {
193 return writer.writeAll(mem.asBytes(&command));
194 }
195
196 fn eql(self: LoadCommand, other: LoadCommand) bool {
197 if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
198 return switch (self) {
199 .DyldInfoOnly => |x| meta.eql(x, other.DyldInfoOnly),
200 .Symtab => |x| meta.eql(x, other.Symtab),
201 .Dysymtab => |x| meta.eql(x, other.Dysymtab),
202 .Main => |x| meta.eql(x, other.Main),
203 .VersionMin => |x| meta.eql(x, other.VersionMin),
204 .SourceVersion => |x| meta.eql(x, other.SourceVersion),
205 .BuildVersion => |x| x.eql(other.BuildVersion),
206 .Uuid => |x| meta.eql(x, other.Uuid),
207 .LinkeditData => |x| meta.eql(x, other.LinkeditData),
208 .Segment => |x| x.eql(other.Segment),
209 .Dylinker => |x| x.eql(other.Dylinker),
210 .Dylib => |x| x.eql(other.Dylib),
211 .Rpath => |x| x.eql(other.Rpath),
212 .Unknown => |x| x.eql(other.Unknown),
213 };
214 }
215};
216
217pub const SegmentCommand = struct {
218 inner: macho.segment_command_64,
219 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
220
221 pub fn read(alloc: Allocator, reader: anytype) !SegmentCommand {
222 const inner = try reader.readStruct(macho.segment_command_64);
223 var segment = SegmentCommand{
224 .inner = inner,
225 };
226 try segment.sections.ensureTotalCapacityPrecise(alloc, inner.nsects);
227
228 var i: usize = 0;
229 while (i < inner.nsects) : (i += 1) {
230 const section = try reader.readStruct(macho.section_64);
231 segment.sections.appendAssumeCapacity(section);
232 }
233
234 return segment;
235 }
236
237 pub fn write(self: SegmentCommand, writer: anytype) !void {
238 try writer.writeAll(mem.asBytes(&self.inner));
239 for (self.sections.items) |sect| {
240 try writer.writeAll(mem.asBytes(&sect));
241 }
242 }
243
244 pub fn deinit(self: *SegmentCommand, alloc: Allocator) void {
245 self.sections.deinit(alloc);
246 }
247
248 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
249 assert(start >= self.inner.fileoff);
250 var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
251 for (self.sections.items) |section| {
252 if (section.offset <= start) continue;
253 if (section.offset < min_pos) min_pos = section.offset;
254 }
255 return min_pos - start;
256 }
257
258 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
259 const end = start + padToIdeal(size);
260 for (self.sections.items) |section| {
261 const increased_size = padToIdeal(section.size);
262 const test_end = section.offset + increased_size;
263 if (end > section.offset and start < test_end) {
264 return test_end;
265 }
266 }
267 return null;
268 }
269
270 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
271 var offset: u64 = if (start) |v| v else self.inner.fileoff;
272 while (self.detectAllocCollision(offset, object_size)) |item_end| {
273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
274 }
275 return offset;
276 }
277
278 fn eql(self: SegmentCommand, other: SegmentCommand) bool {
279 if (!meta.eql(self.inner, other.inner)) return false;
280 const lhs = self.sections.items;
281 const rhs = other.sections.items;
282 var i: usize = 0;
283 while (i < self.inner.nsects) : (i += 1) {
284 if (!meta.eql(lhs[i], rhs[i])) return false;
285 }
286 return true;
287 }
288};
289
290pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
291 return .{ .inner = cmd };
292}
293
294pub fn GenericCommandWithData(comptime Cmd: type) type {
295 return struct {
296 inner: Cmd,
297 /// This field remains undefined until `read` is called.
298 data: []u8 = undefined,
299
300 const Self = @This();
301
302 pub fn read(allocator: Allocator, reader: anytype) !Self {
303 const inner = try reader.readStruct(Cmd);
304 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
305 errdefer allocator.free(data);
306 try reader.readNoEof(data);
307 return Self{
308 .inner = inner,
309 .data = data,
310 };
311 }
312
313 pub fn write(self: Self, writer: anytype) !void {
314 try writer.writeAll(mem.asBytes(&self.inner));
315 try writer.writeAll(self.data);
316 }
317
318 pub fn deinit(self: *Self, allocator: Allocator) void {
319 allocator.free(self.data);
320 }
321
322 fn eql(self: Self, other: Self) bool {
323 if (!meta.eql(self.inner, other.inner)) return false;
324 return mem.eql(u8, self.data, other.data);
325 }
326 };
327}
328
329pub fn createLoadDylibCommand(
330 allocator: Allocator,
331 name: []const u8,
332 timestamp: u32,
333 current_version: u32,
334 compatibility_version: u32,
335) !GenericCommandWithData(macho.dylib_command) {
336 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
337 u64,
338 @sizeOf(macho.dylib_command) + name.len + 1, // +1 for nul
339 @sizeOf(u64),
340 ));
341
342 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
343 .cmd = macho.LC_LOAD_DYLIB,
344 .cmdsize = cmdsize,
345 .dylib = .{
346 .name = @sizeOf(macho.dylib_command),
347 .timestamp = timestamp,
348 .current_version = current_version,
349 .compatibility_version = compatibility_version,
350 },
351 });
352 dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
353
354 mem.set(u8, dylib_cmd.data, 0);
355 mem.copy(u8, dylib_cmd.data, name);
356
357 return dylib_cmd;
358}
359
360fn parseName(name: *const [16]u8) []const u8 {
361 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
362 return name[0..len];
363}
364
365pub fn segmentName(sect: macho.section_64) []const u8 {
366 return parseName(&sect.segname);
367}
368
369pub fn sectionName(sect: macho.section_64) []const u8 {
370 return parseName(&sect.sectname);
371}
372
373pub fn sectionType(sect: macho.section_64) u8 {
374 return @truncate(u8, sect.flags & 0xff);
375}
376
377pub fn sectionAttrs(sect: macho.section_64) u32 {
378 return sect.flags & 0xffffff00;
379}
380
381pub fn sectionIsCode(sect: macho.section_64) bool {
382 const attr = sectionAttrs(sect);
383 return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0;
384}
385
386pub fn sectionIsDebug(sect: macho.section_64) bool {
387 return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0;
388}
389
390pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool {
391 return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0;
392}
393
394pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool {
395 return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0;
396}
397
398fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
399 var stream = io.fixedBufferStream(buffer);
400 var given = try LoadCommand.read(allocator, stream.reader());
401 defer given.deinit(allocator);
402 try testing.expect(expected.eql(given));
403}
404
405fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
406 var stream = io.fixedBufferStream(buffer);
407 try cmd.write(stream.writer());
408 try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
409}
410
411test "read-write segment command" {
412 var gpa = testing.allocator;
413 const in_buffer = &[_]u8{
414 0x19, 0x00, 0x00, 0x00, // cmd
415 0x98, 0x00, 0x00, 0x00, // cmdsize
416 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
417 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
418 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
420 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
421 0x07, 0x00, 0x00, 0x00, // maxprot
422 0x05, 0x00, 0x00, 0x00, // initprot
423 0x01, 0x00, 0x00, 0x00, // nsects
424 0x00, 0x00, 0x00, 0x00, // flags
425 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
426 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
427 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
428 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
429 0x00, 0x40, 0x00, 0x00, // offset
430 0x02, 0x00, 0x00, 0x00, // alignment
431 0x00, 0x00, 0x00, 0x00, // reloff
432 0x00, 0x00, 0x00, 0x00, // nreloc
433 0x00, 0x04, 0x00, 0x80, // flags
434 0x00, 0x00, 0x00, 0x00, // reserved1
435 0x00, 0x00, 0x00, 0x00, // reserved2
436 0x00, 0x00, 0x00, 0x00, // reserved3
437 };
438 var cmd = SegmentCommand{
439 .inner = .{
440 .cmdsize = 152,
441 .segname = makeStaticString("__TEXT"),
442 .vmaddr = 4294967296,
443 .vmsize = 294912,
444 .filesize = 294912,
445 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
446 .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
447 .nsects = 1,
448 },
449 };
450 try cmd.sections.append(gpa, .{
451 .sectname = makeStaticString("__text"),
452 .segname = makeStaticString("__TEXT"),
453 .addr = 4294983680,
454 .size = 448,
455 .offset = 16384,
456 .@"align" = 2,
457 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
458 });
459 defer cmd.deinit(gpa);
460 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
461
462 var out_buffer: [in_buffer.len]u8 = undefined;
463 try testWrite(&out_buffer, LoadCommand{ .Segment = cmd }, in_buffer);
464}
465
466test "read-write generic command with data" {
467 var gpa = testing.allocator;
468 const in_buffer = &[_]u8{
469 0x0c, 0x00, 0x00, 0x00, // cmd
470 0x20, 0x00, 0x00, 0x00, // cmdsize
471 0x18, 0x00, 0x00, 0x00, // name
472 0x02, 0x00, 0x00, 0x00, // timestamp
473 0x00, 0x00, 0x00, 0x00, // current_version
474 0x00, 0x00, 0x00, 0x00, // compatibility_version
475 0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
476 };
477 var cmd = GenericCommandWithData(macho.dylib_command){
478 .inner = .{
479 .cmd = macho.LC_LOAD_DYLIB,
480 .cmdsize = 32,
481 .dylib = .{
482 .name = 24,
483 .timestamp = 2,
484 .current_version = 0,
485 .compatibility_version = 0,
486 },
487 },
488 };
489 cmd.data = try gpa.alloc(u8, 8);
490 defer gpa.free(cmd.data);
491 cmd.data[0] = 0x2f;
492 cmd.data[1] = 0x75;
493 cmd.data[2] = 0x73;
494 cmd.data[3] = 0x72;
495 cmd.data[4] = 0x0;
496 cmd.data[5] = 0x0;
497 cmd.data[6] = 0x0;
498 cmd.data[7] = 0x0;
499 try testRead(gpa, in_buffer, LoadCommand{ .Dylib = cmd });
500
501 var out_buffer: [in_buffer.len]u8 = undefined;
502 try testWrite(&out_buffer, LoadCommand{ .Dylib = cmd }, in_buffer);
503}
504
505test "read-write C struct command" {
506 var gpa = testing.allocator;
507 const in_buffer = &[_]u8{
508 0x28, 0x00, 0x00, 0x80, // cmd
509 0x18, 0x00, 0x00, 0x00, // cmdsize
510 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
511 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
512 };
513 const cmd = .{
514 .cmd = macho.LC_MAIN,
515 .cmdsize = 24,
516 .entryoff = 16644,
517 .stacksize = 0,
518 };
519 try testRead(gpa, in_buffer, LoadCommand{ .Main = cmd });
520
521 var out_buffer: [in_buffer.len]u8 = undefined;
522 try testWrite(&out_buffer, LoadCommand{ .Main = cmd }, in_buffer);
523}