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...@@ -590,7 +590,6 @@ set(ZIG_STAGE2_SOURCES
590 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"590 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"591 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"592 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"593 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
595 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"594 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"595 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
lib/std/macho.zig+485-8
...@@ -1,3 +1,13 @@...@@ -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
1pub const mach_header = extern struct {11pub const mach_header = extern struct {
2 magic: u32,12 magic: u32,
3 cputype: cpu_type_t,13 cputype: cpu_type_t,
...@@ -9,14 +19,14 @@ pub const mach_header = extern struct {...@@ -9,14 +19,14 @@ pub const mach_header = extern struct {
9};19};
1020
11pub const mach_header_64 = extern struct {21pub const mach_header_64 = extern struct {
12 magic: u32,22 magic: u32 = MH_MAGIC_64,
13 cputype: cpu_type_t,23 cputype: cpu_type_t = 0,
14 cpusubtype: cpu_subtype_t,24 cpusubtype: cpu_subtype_t = 0,
15 filetype: u32,25 filetype: u32 = 0,
16 ncmds: u32,26 ncmds: u32 = 0,
17 sizeofcmds: u32,27 sizeofcmds: u32 = 0,
18 flags: u32,28 flags: u32 = 0,
19 reserved: u32,29 reserved: u32 = 0,
20};30};
2131
22pub const fat_header = extern struct {32pub const fat_header = extern struct {
...@@ -630,6 +640,10 @@ pub const segment_command_64 = extern struct {...@@ -630,6 +640,10 @@ pub const segment_command_64 = extern struct {
630 /// number of sections in segment640 /// number of sections in segment
631 nsects: u32 = 0,641 nsects: u32 = 0,
632 flags: u32 = 0,642 flags: u32 = 0,
643
644 pub fn segName(seg: segment_command_64) []const u8 {
645 return parseName(&seg.segname);
646 }
633};647};
634648
635/// A segment is made up of zero or more sections. Non-MH_OBJECT files have649/// 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 {...@@ -728,8 +742,46 @@ pub const section_64 = extern struct {
728742
729 /// reserved743 /// reserved
730 reserved3: u32 = 0,744 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 }
731};778};
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
733pub const nlist = extern struct {785pub const nlist = extern struct {
734 n_strx: u32,786 n_strx: u32,
735 n_type: u8,787 n_type: u8,
...@@ -1760,3 +1812,428 @@ pub const data_in_code_entry = extern struct {...@@ -1760,3 +1812,428 @@ pub const data_in_code_entry = extern struct {
1760 /// A DICE_KIND value.1812 /// A DICE_KIND value.
1761 kind: u16,1813 kind: u16,
1762};1814};
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;...@@ -15,7 +15,6 @@ const meta = std.meta;
15const aarch64 = @import("../arch/aarch64/bits.zig");15const aarch64 = @import("../arch/aarch64/bits.zig");
16const bind = @import("MachO/bind.zig");16const bind = @import("MachO/bind.zig");
17const codegen = @import("../codegen.zig");17const codegen = @import("../codegen.zig");
18const commands = @import("MachO/commands.zig");
19const link = @import("../link.zig");18const link = @import("../link.zig");
20const llvm_backend = @import("../codegen/llvm.zig");19const llvm_backend = @import("../codegen/llvm.zig");
21const target_util = @import("../target.zig");20const target_util = @import("../target.zig");
...@@ -35,9 +34,7 @@ const Object = @import("MachO/Object.zig");...@@ -35,9 +34,7 @@ const Object = @import("MachO/Object.zig");
35const LibStub = @import("tapi.zig").LibStub;34const LibStub = @import("tapi.zig").LibStub;
36const Liveness = @import("../Liveness.zig");35const Liveness = @import("../Liveness.zig");
37const LlvmObject = @import("../codegen/llvm.zig").Object;36const LlvmObject = @import("../codegen/llvm.zig").Object;
38const LoadCommand = commands.LoadCommand;
39const Module = @import("../Module.zig");37const Module = @import("../Module.zig");
40const SegmentCommand = commands.SegmentCommand;
41const StringIndexAdapter = std.hash_map.StringIndexAdapter;38const StringIndexAdapter = std.hash_map.StringIndexAdapter;
42const StringIndexContext = std.hash_map.StringIndexContext;39const StringIndexContext = std.hash_map.StringIndexContext;
43const Trie = @import("MachO/Trie.zig");40const Trie = @import("MachO/Trie.zig");
...@@ -83,7 +80,7 @@ dylibs: std.ArrayListUnmanaged(Dylib) = .{},...@@ -83,7 +80,7 @@ dylibs: std.ArrayListUnmanaged(Dylib) = .{},
83dylibs_map: std.StringHashMapUnmanaged(u16) = .{},80dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
84referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},81referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
8582
86load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},83load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
8784
88pagezero_segment_cmd_index: ?u16 = null,85pagezero_segment_cmd_index: ?u16 = null,
89text_segment_cmd_index: ?u16 = null,86text_segment_cmd_index: ?u16 = null,
...@@ -783,7 +780,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -783,7 +780,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
783 @sizeOf(macho.rpath_command) + rpath.len + 1,780 @sizeOf(macho.rpath_command) + rpath.len + 1,
784 @sizeOf(u64),781 @sizeOf(u64),
785 ));782 ));
786 var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{783 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
787 .cmd = macho.LC_RPATH,784 .cmd = macho.LC_RPATH,
788 .cmdsize = cmdsize,785 .cmdsize = cmdsize,
789 .path = @sizeOf(macho.rpath_command),786 .path = @sizeOf(macho.rpath_command),
...@@ -791,7 +788,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -791,7 +788,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
791 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);788 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
792 mem.set(u8, rpath_cmd.data, 0);789 mem.set(u8, rpath_cmd.data, 0);
793 mem.copy(u8, rpath_cmd.data, rpath);790 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 });
795 try rpath_table.putNoClobber(rpath, {});792 try rpath_table.putNoClobber(rpath, {});
796 self.load_commands_dirty = true;793 self.load_commands_dirty = true;
797 }794 }
...@@ -861,12 +858,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -861,12 +858,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
861 }858 }
862859
863 if (self.bss_section_index) |idx| {860 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;
865 const sect = &seg.sections.items[idx];862 const sect = &seg.sections.items[idx];
866 sect.offset = self.bss_file_offset;863 sect.offset = self.bss_file_offset;
867 }864 }
868 if (self.tlv_bss_section_index) |idx| {865 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;
870 const sect = &seg.sections.items[idx];867 const sect = &seg.sections.items[idx];
871 sect.offset = self.tlv_bss_file_offset;868 sect.offset = self.tlv_bss_file_offset;
872 }869 }
...@@ -942,13 +939,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -942,13 +939,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
942 }939 }
943940
944 if (self.bss_section_index) |idx| {941 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;
946 const sect = &seg.sections.items[idx];943 const sect = &seg.sections.items[idx];
947 self.bss_file_offset = sect.offset;944 self.bss_file_offset = sect.offset;
948 sect.offset = 0;945 sect.offset = 0;
949 }946 }
950 if (self.tlv_bss_section_index) |idx| {947 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;
952 const sect = &seg.sections.items[idx];949 const sect = &seg.sections.items[idx];
953 self.tlv_bss_file_offset = sect.offset;950 self.tlv_bss_file_offset = sect.offset;
954 sect.offset = 0;951 sect.offset = 0;
...@@ -1324,10 +1321,10 @@ pub const MatchingSection = struct {...@@ -1324,10 +1321,10 @@ pub const MatchingSection = struct {
1324};1321};
13251322
1326pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {1323pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
1327 const segname = commands.segmentName(sect);1324 const segname = sect.segName();
1328 const sectname = commands.sectionName(sect);1325 const sectname = sect.sectName();
1329 const res: ?MatchingSection = blk: {1326 const res: ?MatchingSection = blk: {
1330 switch (commands.sectionType(sect)) {1327 switch (sect.type_()) {
1331 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {1328 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
1332 if (self.text_const_section_index == null) {1329 if (self.text_const_section_index == null) {
1333 self.text_const_section_index = try self.initSection(1330 self.text_const_section_index = try self.initSection(
...@@ -1579,7 +1576,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1579,7 +1576,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1579 };1576 };
1580 },1577 },
1581 macho.S_REGULAR => {1578 macho.S_REGULAR => {
1582 if (commands.sectionIsCode(sect)) {1579 if (sect.isCode()) {
1583 if (self.text_section_index == null) {1580 if (self.text_section_index == null) {
1584 self.text_section_index = try self.initSection(1581 self.text_section_index = try self.initSection(
1585 self.text_segment_cmd_index.?,1582 self.text_segment_cmd_index.?,
...@@ -1599,7 +1596,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1599,7 +1596,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1599 .sect = self.text_section_index.?,1596 .sect = self.text_section_index.?,
1600 };1597 };
1601 }1598 }
1602 if (commands.sectionIsDebug(sect)) {1599 if (sect.isDebug()) {
1603 // TODO debug attributes1600 // TODO debug attributes
1604 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {1601 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1605 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{1602 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:...@@ -1865,7 +1862,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:
1865}1862}
18661863
1867pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {1864pub 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;
1869 const sect = seg.sections.items[match.sect];1866 const sect = seg.sections.items[match.sect];
1870 const sym = self.locals.items[atom.local_sym_index];1867 const sym = self.locals.items[atom.local_sym_index];
1871 const file_offset = sect.offset + sym.n_value - sect.addr;1868 const file_offset = sect.offset + sym.n_value - sect.addr;
...@@ -1885,14 +1882,11 @@ fn allocateLocals(self: *MachO) !void {...@@ -1885,14 +1882,11 @@ fn allocateLocals(self: *MachO) !void {
1885 }1882 }
18861883
1887 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);1884 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;
1889 const sect = seg.sections.items[match.sect];1886 const sect = seg.sections.items[match.sect];
1890 var base_vaddr = sect.addr;1887 var base_vaddr = sect.addr;
18911888
1892 log.debug("allocating local symbols in {s},{s}", .{1889 log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() });
1893 commands.segmentName(sect),
1894 commands.sectionName(sect),
1895 });
18961890
1897 while (true) {1891 while (true) {
1898 const alignment = try math.powi(u32, 2, atom.alignment);1892 const alignment = try math.powi(u32, 2, atom.alignment);
...@@ -1979,7 +1973,7 @@ fn writeAllAtoms(self: *MachO) !void {...@@ -1979,7 +1973,7 @@ fn writeAllAtoms(self: *MachO) !void {
1979 var it = self.atoms.iterator();1973 var it = self.atoms.iterator();
1980 while (it.next()) |entry| {1974 while (it.next()) |entry| {
1981 const match = entry.key_ptr.*;1975 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;
1983 const sect = seg.sections.items[match.sect];1977 const sect = seg.sections.items[match.sect];
1984 var atom: *Atom = entry.value_ptr.*;1978 var atom: *Atom = entry.value_ptr.*;
19851979
...@@ -1987,7 +1981,7 @@ fn writeAllAtoms(self: *MachO) !void {...@@ -1987,7 +1981,7 @@ fn writeAllAtoms(self: *MachO) !void {
1987 defer buffer.deinit();1981 defer buffer.deinit();
1988 try buffer.ensureTotalCapacity(try math.cast(usize, sect.size));1982 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
1992 while (atom.prev) |prev| {1986 while (atom.prev) |prev| {
1993 atom = prev;1987 atom = prev;
...@@ -2031,11 +2025,11 @@ fn writeAtoms(self: *MachO) !void {...@@ -2031,11 +2025,11 @@ fn writeAtoms(self: *MachO) !void {
2031 var it = self.atoms.iterator();2025 var it = self.atoms.iterator();
2032 while (it.next()) |entry| {2026 while (it.next()) |entry| {
2033 const match = entry.key_ptr.*;2027 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;
2035 const sect = seg.sections.items[match.sect];2029 const sect = seg.sections.items[match.sect];
2036 var atom: *Atom = entry.value_ptr.*;2030 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
2040 while (atom.prev) |prev| {2034 while (atom.prev) |prev| {
2041 atom = prev;2035 atom = prev;
...@@ -2995,7 +2989,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -2995,7 +2989,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
29952989
2996 const first_atom = atom;2990 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;
2999 const sect = seg.sections.items[match.sect];2993 const sect = seg.sections.items[match.sect];
3000 const metadata = try section_metadata.getOrPut(match);2994 const metadata = try section_metadata.getOrPut(match);
3001 if (!metadata.found_existing) {2995 if (!metadata.found_existing) {
...@@ -3005,7 +2999,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -3005,7 +2999,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
3005 };2999 };
3006 }3000 }
30073001
3008 log.debug("{s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });3002 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
30093003
3010 while (true) {3004 while (true) {
3011 const alignment = try math.powi(u32, 2, atom.alignment);3005 const alignment = try math.powi(u32, 2, atom.alignment);
...@@ -3046,11 +3040,11 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -3046,11 +3040,11 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
3046 while (it.next()) |entry| {3040 while (it.next()) |entry| {
3047 const match = entry.key_ptr.*;3041 const match = entry.key_ptr.*;
3048 const metadata = entry.value_ptr.*;3042 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;
3050 const sect = &seg.sections.items[match.sect];3044 const sect = &seg.sections.items[match.sect];
3051 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{3045 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
3052 commands.segmentName(sect.*),3046 sect.segName(),
3053 commands.sectionName(sect.*),3047 sect.sectName(),
3054 metadata.size,3048 metadata.size,
3055 metadata.alignment,3049 metadata.alignment,
3056 });3050 });
...@@ -3070,7 +3064,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -3070,7 +3064,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
3070 self.data_segment_cmd_index,3064 self.data_segment_cmd_index,
3071 }) |maybe_seg_id| {3065 }) |maybe_seg_id| {
3072 const seg_id = maybe_seg_id orelse continue;3066 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
3075 for (seg.sections.items) |sect, sect_id| {3069 for (seg.sections.items) |sect, sect_id| {
3076 const match = MatchingSection{3070 const match = MatchingSection{
...@@ -3140,7 +3134,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -3140,7 +3134,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
3140fn addLoadDylibLC(self: *MachO, id: u16) !void {3134fn addLoadDylibLC(self: *MachO, id: u16) !void {
3141 const dylib = self.dylibs.items[id];3135 const dylib = self.dylibs.items[id];
3142 const dylib_id = dylib.id orelse unreachable;3136 const dylib_id = dylib.id orelse unreachable;
3143 var dylib_cmd = try commands.createLoadDylibCommand(3137 var dylib_cmd = try macho.createLoadDylibCommand(
3144 self.base.allocator,3138 self.base.allocator,
3145 dylib_id.name,3139 dylib_id.name,
3146 dylib_id.timestamp,3140 dylib_id.timestamp,
...@@ -3148,7 +3142,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {...@@ -3148,7 +3142,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
3148 dylib_id.compatibility_version,3142 dylib_id.compatibility_version,
3149 );3143 );
3150 errdefer dylib_cmd.deinit(self.base.allocator);3144 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 });
3152 self.load_commands_dirty = true;3146 self.load_commands_dirty = true;
3153}3147}
31543148
...@@ -3156,7 +3150,7 @@ fn addCodeSignatureLC(self: *MachO) !void {...@@ -3156,7 +3150,7 @@ fn addCodeSignatureLC(self: *MachO) !void {
3156 if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;3150 if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;
3157 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);3151 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
3158 try self.load_commands.append(self.base.allocator, .{3152 try self.load_commands.append(self.base.allocator, .{
3159 .LinkeditData = .{3153 .linkedit_data = .{
3160 .cmd = macho.LC_CODE_SIGNATURE,3154 .cmd = macho.LC_CODE_SIGNATURE,
3161 .cmdsize = @sizeOf(macho.linkedit_data_command),3155 .cmdsize = @sizeOf(macho.linkedit_data_command),
3162 .dataoff = 0,3156 .dataoff = 0,
...@@ -3171,7 +3165,7 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3171,7 +3165,7 @@ fn setEntryPoint(self: *MachO) !void {
31713165
3172 // TODO we should respect the -entry flag passed in by the user to set a custom3166 // TODO we should respect the -entry flag passed in by the user to set a custom
3173 // entrypoint. For now, assume default of `_main`.3167 // 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;
3175 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{3169 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
3176 .bytes = &self.strtab,3170 .bytes = &self.strtab,
3177 }) orelse {3171 }) orelse {
...@@ -3181,7 +3175,7 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3181,7 +3175,7 @@ fn setEntryPoint(self: *MachO) !void {
3181 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;3175 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
3182 assert(resolv.where == .global);3176 assert(resolv.where == .global);
3183 const sym = self.globals.items[resolv.where_index];3177 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;
3185 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);3179 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
3186 ec.stacksize = self.base.options.stack_size_override orelse 0;3180 ec.stacksize = self.base.options.stack_size_override orelse 0;
3187 self.entry_addr = sym.n_value;3181 self.entry_addr = sym.n_value;
...@@ -3878,7 +3872,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -3878,7 +3872,7 @@ fn populateMissingMetadata(self: *MachO) !void {
3878 if (self.pagezero_segment_cmd_index == null) {3872 if (self.pagezero_segment_cmd_index == null) {
3879 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);3873 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3880 try self.load_commands.append(self.base.allocator, .{3874 try self.load_commands.append(self.base.allocator, .{
3881 .Segment = .{3875 .segment = .{
3882 .inner = .{3876 .inner = .{
3883 .segname = makeStaticString("__PAGEZERO"),3877 .segname = makeStaticString("__PAGEZERO"),
3884 .vmsize = pagezero_vmsize,3878 .vmsize = pagezero_vmsize,
...@@ -3899,7 +3893,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -3899,7 +3893,7 @@ fn populateMissingMetadata(self: *MachO) !void {
3899 break :blk needed_size;3893 break :blk needed_size;
3900 } else 0;3894 } else 0;
3901 try self.load_commands.append(self.base.allocator, .{3895 try self.load_commands.append(self.base.allocator, .{
3902 .Segment = .{3896 .segment = .{
3903 .inner = .{3897 .inner = .{
3904 .segname = makeStaticString("__TEXT"),3898 .segname = makeStaticString("__TEXT"),
3905 .vmaddr = pagezero_vmsize,3899 .vmaddr = pagezero_vmsize,
...@@ -4003,7 +3997,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4003,7 +3997,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4003 });3997 });
4004 }3998 }
4005 try self.load_commands.append(self.base.allocator, .{3999 try self.load_commands.append(self.base.allocator, .{
4006 .Segment = .{4000 .segment = .{
4007 .inner = .{4001 .inner = .{
4008 .segname = makeStaticString("__DATA_CONST"),4002 .segname = makeStaticString("__DATA_CONST"),
4009 .vmaddr = vmaddr,4003 .vmaddr = vmaddr,
...@@ -4052,7 +4046,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4052,7 +4046,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4052 });4046 });
4053 }4047 }
4054 try self.load_commands.append(self.base.allocator, .{4048 try self.load_commands.append(self.base.allocator, .{
4055 .Segment = .{4049 .segment = .{
4056 .inner = .{4050 .inner = .{
4057 .segname = makeStaticString("__DATA"),4051 .segname = makeStaticString("__DATA"),
4058 .vmaddr = vmaddr,4052 .vmaddr = vmaddr,
...@@ -4136,7 +4130,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4136,7 +4130,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4136 .flags = macho.S_THREAD_LOCAL_ZEROFILL,4130 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
4137 },4131 },
4138 );4132 );
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;
4140 const sect = seg.sections.items[self.tlv_bss_section_index.?];4134 const sect = seg.sections.items[self.tlv_bss_section_index.?];
4141 self.tlv_bss_file_offset = sect.offset;4135 self.tlv_bss_file_offset = sect.offset;
4142 }4136 }
...@@ -4153,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4153,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4153 .flags = macho.S_ZEROFILL,4147 .flags = macho.S_ZEROFILL,
4154 },4148 },
4155 );4149 );
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;
4157 const sect = seg.sections.items[self.bss_section_index.?];4151 const sect = seg.sections.items[self.bss_section_index.?];
4158 self.bss_file_offset = sect.offset;4152 self.bss_file_offset = sect.offset;
4159 }4153 }
...@@ -4169,7 +4163,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4169,7 +4163,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4169 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});4163 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
4170 }4164 }
4171 try self.load_commands.append(self.base.allocator, .{4165 try self.load_commands.append(self.base.allocator, .{
4172 .Segment = .{4166 .segment = .{
4173 .inner = .{4167 .inner = .{
4174 .segname = makeStaticString("__LINKEDIT"),4168 .segname = makeStaticString("__LINKEDIT"),
4175 .vmaddr = vmaddr,4169 .vmaddr = vmaddr,
...@@ -4185,7 +4179,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4185,7 +4179,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4185 if (self.dyld_info_cmd_index == null) {4179 if (self.dyld_info_cmd_index == null) {
4186 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);4180 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
4187 try self.load_commands.append(self.base.allocator, .{4181 try self.load_commands.append(self.base.allocator, .{
4188 .DyldInfoOnly = .{4182 .dyld_info_only = .{
4189 .cmd = macho.LC_DYLD_INFO_ONLY,4183 .cmd = macho.LC_DYLD_INFO_ONLY,
4190 .cmdsize = @sizeOf(macho.dyld_info_command),4184 .cmdsize = @sizeOf(macho.dyld_info_command),
4191 .rebase_off = 0,4185 .rebase_off = 0,
...@@ -4206,7 +4200,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4206,7 +4200,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4206 if (self.symtab_cmd_index == null) {4200 if (self.symtab_cmd_index == null) {
4207 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);4201 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4208 try self.load_commands.append(self.base.allocator, .{4202 try self.load_commands.append(self.base.allocator, .{
4209 .Symtab = .{4203 .symtab = .{
4210 .cmd = macho.LC_SYMTAB,4204 .cmd = macho.LC_SYMTAB,
4211 .cmdsize = @sizeOf(macho.symtab_command),4205 .cmdsize = @sizeOf(macho.symtab_command),
4212 .symoff = 0,4206 .symoff = 0,
...@@ -4221,7 +4215,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4221,7 +4215,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4221 if (self.dysymtab_cmd_index == null) {4215 if (self.dysymtab_cmd_index == null) {
4222 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);4216 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4223 try self.load_commands.append(self.base.allocator, .{4217 try self.load_commands.append(self.base.allocator, .{
4224 .Dysymtab = .{4218 .dysymtab = .{
4225 .cmd = macho.LC_DYSYMTAB,4219 .cmd = macho.LC_DYSYMTAB,
4226 .cmdsize = @sizeOf(macho.dysymtab_command),4220 .cmdsize = @sizeOf(macho.dysymtab_command),
4227 .ilocalsym = 0,4221 .ilocalsym = 0,
...@@ -4254,7 +4248,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4254,7 +4248,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4254 @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,4248 @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,
4255 @sizeOf(u64),4249 @sizeOf(u64),
4256 ));4250 ));
4257 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{4251 var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
4258 .cmd = macho.LC_LOAD_DYLINKER,4252 .cmd = macho.LC_LOAD_DYLINKER,
4259 .cmdsize = cmdsize,4253 .cmdsize = cmdsize,
4260 .name = @sizeOf(macho.dylinker_command),4254 .name = @sizeOf(macho.dylinker_command),
...@@ -4262,14 +4256,14 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4262,14 +4256,14 @@ fn populateMissingMetadata(self: *MachO) !void {
4262 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);4256 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
4263 mem.set(u8, dylinker_cmd.data, 0);4257 mem.set(u8, dylinker_cmd.data, 0);
4264 mem.copy(u8, dylinker_cmd.data, mem.sliceTo(default_dyld_path, 0));4258 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 });
4266 self.load_commands_dirty = true;4260 self.load_commands_dirty = true;
4267 }4261 }
42684262
4269 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {4263 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
4270 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);4264 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
4271 try self.load_commands.append(self.base.allocator, .{4265 try self.load_commands.append(self.base.allocator, .{
4272 .Main = .{4266 .main = .{
4273 .cmd = macho.LC_MAIN,4267 .cmd = macho.LC_MAIN,
4274 .cmdsize = @sizeOf(macho.entry_point_command),4268 .cmdsize = @sizeOf(macho.entry_point_command),
4275 .entryoff = 0x0,4269 .entryoff = 0x0,
...@@ -4289,7 +4283,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4289,7 +4283,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4289 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4283 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4290 const compat_version = self.base.options.compatibility_version orelse4284 const compat_version = self.base.options.compatibility_version orelse
4291 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4285 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4292 var dylib_cmd = try commands.createLoadDylibCommand(4286 var dylib_cmd = try macho.createLoadDylibCommand(
4293 self.base.allocator,4287 self.base.allocator,
4294 install_name,4288 install_name,
4295 2,4289 2,
...@@ -4298,14 +4292,14 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4298,14 +4292,14 @@ fn populateMissingMetadata(self: *MachO) !void {
4298 );4292 );
4299 errdefer dylib_cmd.deinit(self.base.allocator);4293 errdefer dylib_cmd.deinit(self.base.allocator);
4300 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;4294 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 });
4302 self.load_commands_dirty = true;4296 self.load_commands_dirty = true;
4303 }4297 }
43044298
4305 if (self.source_version_cmd_index == null) {4299 if (self.source_version_cmd_index == null) {
4306 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);4300 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4307 try self.load_commands.append(self.base.allocator, .{4301 try self.load_commands.append(self.base.allocator, .{
4308 .SourceVersion = .{4302 .source_version = .{
4309 .cmd = macho.LC_SOURCE_VERSION,4303 .cmd = macho.LC_SOURCE_VERSION,
4310 .cmdsize = @sizeOf(macho.source_version_command),4304 .cmdsize = @sizeOf(macho.source_version_command),
4311 .version = 0x0,4305 .version = 0x0,
...@@ -4332,7 +4326,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4332,7 +4326,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4332 break :blk sdk_version;4326 break :blk sdk_version;
4333 } else platform_version;4327 } else platform_version;
4334 const is_simulator_abi = self.base.options.target.abi == .simulator;4328 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{
4336 .cmd = macho.LC_BUILD_VERSION,4330 .cmd = macho.LC_BUILD_VERSION,
4337 .cmdsize = cmdsize,4331 .cmdsize = cmdsize,
4338 .platform = switch (self.base.options.target.os.tag) {4332 .platform = switch (self.base.options.target.os.tag) {
...@@ -4353,7 +4347,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4353,7 +4347,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4353 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));4347 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4354 mem.set(u8, cmd.data, 0);4348 mem.set(u8, cmd.data, 0);
4355 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));4349 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 });
4357 self.load_commands_dirty = true;4351 self.load_commands_dirty = true;
4358 }4352 }
43594353
...@@ -4365,14 +4359,14 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4365,14 +4359,14 @@ fn populateMissingMetadata(self: *MachO) !void {
4365 .uuid = undefined,4359 .uuid = undefined,
4366 };4360 };
4367 std.crypto.random.bytes(&uuid_cmd.uuid);4361 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 });
4369 self.load_commands_dirty = true;4363 self.load_commands_dirty = true;
4370 }4364 }
43714365
4372 if (self.function_starts_cmd_index == null) {4366 if (self.function_starts_cmd_index == null) {
4373 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);4367 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
4374 try self.load_commands.append(self.base.allocator, .{4368 try self.load_commands.append(self.base.allocator, .{
4375 .LinkeditData = .{4369 .linkedit_data = .{
4376 .cmd = macho.LC_FUNCTION_STARTS,4370 .cmd = macho.LC_FUNCTION_STARTS,
4377 .cmdsize = @sizeOf(macho.linkedit_data_command),4371 .cmdsize = @sizeOf(macho.linkedit_data_command),
4378 .dataoff = 0,4372 .dataoff = 0,
...@@ -4385,7 +4379,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4385,7 +4379,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4385 if (self.data_in_code_cmd_index == null) {4379 if (self.data_in_code_cmd_index == null) {
4386 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);4380 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
4387 try self.load_commands.append(self.base.allocator, .{4381 try self.load_commands.append(self.base.allocator, .{
4388 .LinkeditData = .{4382 .linkedit_data = .{
4389 .cmd = macho.LC_DATA_IN_CODE,4383 .cmd = macho.LC_DATA_IN_CODE,
4390 .cmdsize = @sizeOf(macho.linkedit_data_command),4384 .cmdsize = @sizeOf(macho.linkedit_data_command),
4391 .dataoff = 0,4385 .dataoff = 0,
...@@ -4399,8 +4393,8 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4399,8 +4393,8 @@ fn populateMissingMetadata(self: *MachO) !void {
4399}4393}
44004394
4401fn allocateTextSegment(self: *MachO) !void {4395fn allocateTextSegment(self: *MachO) !void {
4402 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;4396 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;4397 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].segment.inner.vmsize;
4404 seg.inner.fileoff = 0;4398 seg.inner.fileoff = 0;
4405 seg.inner.vmaddr = base_vmaddr;4399 seg.inner.vmaddr = base_vmaddr;
44064400
...@@ -4436,30 +4430,30 @@ fn allocateTextSegment(self: *MachO) !void {...@@ -4436,30 +4430,30 @@ fn allocateTextSegment(self: *MachO) !void {
4436}4430}
44374431
4438fn allocateDataConstSegment(self: *MachO) !void {4432fn allocateDataConstSegment(self: *MachO) !void {
4439 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;4433 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;4434 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
4441 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;4435 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
4442 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;4436 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
4443 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);4437 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
4444}4438}
44454439
4446fn allocateDataSegment(self: *MachO) !void {4440fn allocateDataSegment(self: *MachO) !void {
4447 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;4441 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;4442 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
4449 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;4443 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
4450 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;4444 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
4451 try self.allocateSegment(self.data_segment_cmd_index.?, 0);4445 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
4452}4446}
44534447
4454fn allocateLinkeditSegment(self: *MachO) void {4448fn allocateLinkeditSegment(self: *MachO) void {
4455 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;4449 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;4450 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
4457 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;4451 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
4458 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;4452 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
4459}4453}
44604454
4461fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {4455fn 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
4464 // Allocate the sections according to their alignment at the beginning of the segment.4458 // Allocate the sections according to their alignment at the beginning of the segment.
4465 var start: u64 = offset;4459 var start: u64 = offset;
...@@ -4491,7 +4485,7 @@ fn initSection(...@@ -4491,7 +4485,7 @@ fn initSection(
4491 alignment: u32,4485 alignment: u32,
4492 opts: InitSectionOpts,4486 opts: InitSectionOpts,
4493) !u16 {4487) !u16 {
4494 const seg = &self.load_commands.items[segment_id].Segment;4488 const seg = &self.load_commands.items[segment_id].segment;
4495 var sect = macho.section_64{4489 var sect = macho.section_64{
4496 .sectname = makeStaticString(sectname),4490 .sectname = makeStaticString(sectname),
4497 .segname = seg.inner.segname,4491 .segname = seg.inner.segname,
...@@ -4507,8 +4501,8 @@ fn initSection(...@@ -4507,8 +4501,8 @@ fn initSection(
4507 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;4501 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
4508 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);4502 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
4509 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{4503 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4510 commands.segmentName(sect),4504 sect.segName(),
4511 commands.sectionName(sect),4505 sect.sectName(),
4512 off,4506 off,
4513 off + size,4507 off + size,
4514 });4508 });
...@@ -4535,7 +4529,7 @@ fn initSection(...@@ -4535,7 +4529,7 @@ fn initSection(
4535}4529}
45364530
4537fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {4531fn 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;
4539 if (seg.sections.items.len == 0) {4533 if (seg.sections.items.len == 0) {
4540 return if (start) |v| v else seg.inner.fileoff;4534 return if (start) |v| v else seg.inner.fileoff;
4541 }4535 }
...@@ -4545,7 +4539,7 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64...@@ -4545,7 +4539,7 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64
4545}4539}
45464540
4547fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {4541fn 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;
4549 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);4543 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
4550 assert(new_seg_size > seg.inner.filesize);4544 assert(new_seg_size > seg.inner.filesize);
4551 const offset_amt = new_seg_size - seg.inner.filesize;4545 const offset_amt = new_seg_size - seg.inner.filesize;
...@@ -4567,13 +4561,13 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {...@@ -4567,13 +4561,13 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
4567 // TODO We should probably nop the expanded by distance, or put 0s.4561 // TODO We should probably nop the expanded by distance, or put 0s.
45684562
4569 // TODO copyRangeAll doesn't automatically extend the file on macOS.4563 // 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;
4571 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;4565 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
4572 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);4566 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
45734567
4574 var next: usize = seg_id + 1;4568 var next: usize = seg_id + 1;
4575 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {4569 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;
4577 _ = try self.base.file.?.copyRangeAll(4571 _ = try self.base.file.?.copyRangeAll(
4578 next_seg.inner.fileoff,4572 next_seg.inner.fileoff,
4579 self.base.file.?,4573 self.base.file.?,
...@@ -4596,8 +4590,8 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {...@@ -4596,8 +4590,8 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
4596 moved_sect.addr += offset_amt;4590 moved_sect.addr += offset_amt;
45974591
4598 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4592 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.*),4593 moved_sect.segName(),
4600 commands.sectionName(moved_sect.*),4594 moved_sect.sectName(),
4601 moved_sect.offset,4595 moved_sect.offset,
4602 moved_sect.offset + moved_sect.size,4596 moved_sect.offset + moved_sect.size,
4603 moved_sect.addr,4597 moved_sect.addr,
...@@ -4616,7 +4610,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -4616,7 +4610,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4616 const tracy = trace(@src());4610 const tracy = trace(@src());
4617 defer tracy.end();4611 defer tracy.end();
46184612
4619 const seg = &self.load_commands.items[match.seg].Segment;4613 const seg = &self.load_commands.items[match.seg].segment;
4620 const sect = &seg.sections.items[match.sect];4614 const sect = &seg.sections.items[match.sect];
46214615
4622 const alignment = try math.powi(u32, 2, sect.@"align");4616 const alignment = try math.powi(u32, 2, sect.@"align");
...@@ -4670,8 +4664,8 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -4670,8 +4664,8 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4670 moved_sect.addr += offset_amt;4664 moved_sect.addr += offset_amt;
46714665
4672 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4666 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.*),4667 moved_sect.segName(),
4674 commands.sectionName(moved_sect.*),4668 moved_sect.sectName(),
4675 moved_sect.offset,4669 moved_sect.offset,
4676 moved_sect.offset + moved_sect.size,4670 moved_sect.offset + moved_sect.size,
4677 moved_sect.addr,4671 moved_sect.addr,
...@@ -4687,7 +4681,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -4687,7 +4681,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4687}4681}
46884682
4689fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {4683fn 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;
4691 assert(start >= seg.inner.fileoff);4685 assert(start >= seg.inner.fileoff);
4692 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;4686 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
4693 if (start > min_pos) return 0;4687 if (start > min_pos) return 0;
...@@ -4699,7 +4693,7 @@ fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {...@@ -4699,7 +4693,7 @@ fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
4699}4693}
47004694
4701fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {4695fn 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;
4703 var max_alignment: u32 = 1;4697 var max_alignment: u32 = 1;
4704 var next = start_sect_id;4698 var next = start_sect_id;
4705 while (next < seg.sections.items.len) : (next += 1) {4699 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...@@ -4714,7 +4708,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
4714 const tracy = trace(@src());4708 const tracy = trace(@src());
4715 defer tracy.end();4709 defer tracy.end();
47164710
4717 const seg = &self.load_commands.items[match.seg].Segment;4711 const seg = &self.load_commands.items[match.seg].segment;
4718 const sect = &seg.sections.items[match.sect];4712 const sect = &seg.sections.items[match.sect];
4719 var free_list = self.atom_free_lists.get(match).?;4713 var free_list = self.atom_free_lists.get(match).?;
4720 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;4714 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...@@ -4818,7 +4812,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
4818}4812}
48194813
4820fn addAtomAndBumpSectionSize(self: *MachO, atom: *Atom, match: MatchingSection) !void {4814fn 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;
4822 const sect = &seg.sections.items[match.sect];4816 const sect = &seg.sections.items[match.sect];
4823 const alignment = try math.powi(u32, 2, atom.alignment);4817 const alignment = try math.powi(u32, 2, atom.alignment);
4824 sect.size = mem.alignForwardGeneric(u64, sect.size, alignment) + atom.size;4818 sect.size = mem.alignForwardGeneric(u64, sect.size, alignment) + atom.size;
...@@ -4865,11 +4859,11 @@ const NextSegmentAddressAndOffset = struct {...@@ -4865,11 +4859,11 @@ const NextSegmentAddressAndOffset = struct {
4865fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {4859fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
4866 var prev_segment_idx: ?usize = null; // We use optional here for safety.4860 var prev_segment_idx: ?usize = null; // We use optional here for safety.
4867 for (self.load_commands.items) |cmd, i| {4861 for (self.load_commands.items) |cmd, i| {
4868 if (cmd == .Segment) {4862 if (cmd == .segment) {
4869 prev_segment_idx = i;4863 prev_segment_idx = i;
4870 }4864 }
4871 }4865 }
4872 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;4866 const prev_segment = self.load_commands.items[prev_segment_idx.?].segment;
4873 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;4867 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
4874 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;4868 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
4875 return .{4869 return .{
...@@ -4888,7 +4882,7 @@ fn sortSections(self: *MachO) !void {...@@ -4888,7 +4882,7 @@ fn sortSections(self: *MachO) !void {
48884882
4889 {4883 {
4890 // __TEXT segment4884 // __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;
4892 var sections = seg.sections.toOwnedSlice(self.base.allocator);4886 var sections = seg.sections.toOwnedSlice(self.base.allocator);
4893 defer self.base.allocator.free(sections);4887 defer self.base.allocator.free(sections);
4894 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);4888 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
...@@ -4920,7 +4914,7 @@ fn sortSections(self: *MachO) !void {...@@ -4920,7 +4914,7 @@ fn sortSections(self: *MachO) !void {
49204914
4921 {4915 {
4922 // __DATA_CONST segment4916 // __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;
4924 var sections = seg.sections.toOwnedSlice(self.base.allocator);4918 var sections = seg.sections.toOwnedSlice(self.base.allocator);
4925 defer self.base.allocator.free(sections);4919 defer self.base.allocator.free(sections);
4926 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);4920 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
...@@ -4947,7 +4941,7 @@ fn sortSections(self: *MachO) !void {...@@ -4947,7 +4941,7 @@ fn sortSections(self: *MachO) !void {
49474941
4948 {4942 {
4949 // __DATA segment4943 // __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;
4951 var sections = seg.sections.toOwnedSlice(self.base.allocator);4945 var sections = seg.sections.toOwnedSlice(self.base.allocator);
4952 defer self.base.allocator.free(sections);4946 defer self.base.allocator.free(sections);
4953 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);4947 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
...@@ -5003,7 +4997,7 @@ fn sortSections(self: *MachO) !void {...@@ -5003,7 +4997,7 @@ fn sortSections(self: *MachO) !void {
5003 {4997 {
5004 // Create new section ordinals.4998 // Create new section ordinals.
5005 self.section_ordinals.clearRetainingCapacity();4999 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;
5007 for (text_seg.sections.items) |_, sect_id| {5001 for (text_seg.sections.items) |_, sect_id| {
5008 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5002 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5009 .seg = self.text_segment_cmd_index.?,5003 .seg = self.text_segment_cmd_index.?,
...@@ -5011,7 +5005,7 @@ fn sortSections(self: *MachO) !void {...@@ -5011,7 +5005,7 @@ fn sortSections(self: *MachO) !void {
5011 });5005 });
5012 assert(!res.found_existing);5006 assert(!res.found_existing);
5013 }5007 }
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;
5015 for (data_const_seg.sections.items) |_, sect_id| {5009 for (data_const_seg.sections.items) |_, sect_id| {
5016 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5010 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5017 .seg = self.data_const_segment_cmd_index.?,5011 .seg = self.data_const_segment_cmd_index.?,
...@@ -5019,7 +5013,7 @@ fn sortSections(self: *MachO) !void {...@@ -5019,7 +5013,7 @@ fn sortSections(self: *MachO) !void {
5019 });5013 });
5020 assert(!res.found_existing);5014 assert(!res.found_existing);
5021 }5015 }
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;
5023 for (data_seg.sections.items) |_, sect_id| {5017 for (data_seg.sections.items) |_, sect_id| {
5024 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5018 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5025 .seg = self.data_segment_cmd_index.?,5019 .seg = self.data_segment_cmd_index.?,
...@@ -5044,9 +5038,9 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5044,9 +5038,9 @@ fn updateSectionOrdinals(self: *MachO) !void {
50445038
5045 var new_ordinal: u8 = 0;5039 var new_ordinal: u8 = 0;
5046 for (self.load_commands.items) |lc, lc_id| {5040 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| {
5050 const match = MatchingSection{5044 const match = MatchingSection{
5051 .seg = @intCast(u16, lc_id),5045 .seg = @intCast(u16, lc_id),
5052 .sect = @intCast(u16, sect_id),5046 .sect = @intCast(u16, sect_id),
...@@ -5089,7 +5083,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5089,7 +5083,7 @@ fn writeDyldInfoData(self: *MachO) !void {
50895083
5090 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable5084 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
5094 while (true) {5088 while (true) {
5095 const sym = self.locals.items[atom.local_sym_index];5089 const sym = self.locals.items[atom.local_sym_index];
...@@ -5159,7 +5153,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5159,7 +5153,7 @@ fn writeDyldInfoData(self: *MachO) !void {
5159 {5153 {
5160 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5154 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5161 log.debug("generating export trie", .{});5155 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;
5163 const base_address = text_segment.inner.vmaddr;5157 const base_address = text_segment.inner.vmaddr;
51645158
5165 for (self.globals.items) |sym| {5159 for (self.globals.items) |sym| {
...@@ -5177,8 +5171,8 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5177,8 +5171,8 @@ fn writeDyldInfoData(self: *MachO) !void {
5177 try trie.finalize(self.base.allocator);5171 try trie.finalize(self.base.allocator);
5178 }5172 }
51795173
5180 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;5174 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;5175 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;
5182 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);5176 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5183 const bind_size = try bind.bindInfoSize(bind_pointers.items);5177 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5184 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);5178 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
...@@ -5248,7 +5242,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5248,7 +5242,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5248 .sect = self.la_symbol_ptr_section_index.?,5242 .sect = self.la_symbol_ptr_section_index.?,
5249 }).?;5243 }).?;
5250 const base_addr = blk: {5244 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;
5252 break :blk seg.inner.vmaddr;5246 break :blk seg.inner.vmaddr;
5253 };5247 };
52545248
...@@ -5312,7 +5306,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5312,7 +5306,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5312 }5306 }
53135307
5314 const sect = blk: {5308 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;
5316 break :blk seg.sections.items[self.stub_helper_section_index.?];5310 break :blk seg.sections.items[self.stub_helper_section_index.?];
5317 };5311 };
5318 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {5312 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
...@@ -5353,7 +5347,7 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5353,7 +5347,7 @@ fn writeFunctionStarts(self: *MachO) !void {
5353 var offsets = std.ArrayList(u32).init(self.base.allocator);5347 var offsets = std.ArrayList(u32).init(self.base.allocator);
5354 defer offsets.deinit();5348 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;
5357 var last_off: u32 = 0;5351 var last_off: u32 = 0;
53585352
5359 while (true) {5353 while (true) {
...@@ -5410,8 +5404,8 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5410,8 +5404,8 @@ fn writeFunctionStarts(self: *MachO) !void {
5410 }5404 }
54115405
5412 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64)));5406 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;5407 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;5408 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
54155409
5416 fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);5410 fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
5417 fn_cmd.datasize = needed_size;5411 fn_cmd.datasize = needed_size;
...@@ -5444,7 +5438,7 @@ fn writeDices(self: *MachO) !void {...@@ -5444,7 +5438,7 @@ fn writeDices(self: *MachO) !void {
5444 atom = prev;5438 atom = prev;
5445 }5439 }
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;
5448 const text_sect = text_seg.sections.items[self.text_section_index.?];5442 const text_sect = text_seg.sections.items[self.text_section_index.?];
54495443
5450 while (true) {5444 while (true) {
...@@ -5468,8 +5462,8 @@ fn writeDices(self: *MachO) !void {...@@ -5468,8 +5462,8 @@ fn writeDices(self: *MachO) !void {
5468 } else break;5462 } else break;
5469 }5463 }
54705464
5471 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;5465 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;5466 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
5473 const needed_size = @intCast(u32, buf.items.len);5467 const needed_size = @intCast(u32, buf.items.len);
54745468
5475 dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);5469 dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
...@@ -5489,8 +5483,8 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -5489,8 +5483,8 @@ fn writeSymbolTable(self: *MachO) !void {
5489 const tracy = trace(@src());5483 const tracy = trace(@src());
5490 defer tracy.end();5484 defer tracy.end();
54915485
5492 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;5486 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5493 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;5487 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
5494 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);5488 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
54955489
5496 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);5490 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
...@@ -5594,18 +5588,18 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -5594,18 +5588,18 @@ fn writeSymbolTable(self: *MachO) !void {
5594 seg.inner.filesize += locals_size + exports_size + undefs_size;5588 seg.inner.filesize += locals_size + exports_size + undefs_size;
55955589
5596 // Update dynamic symbol table.5590 // 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;
5598 dysymtab.nlocalsym = @intCast(u32, nlocals);5592 dysymtab.nlocalsym = @intCast(u32, nlocals);
5599 dysymtab.iextdefsym = dysymtab.nlocalsym;5593 dysymtab.iextdefsym = dysymtab.nlocalsym;
5600 dysymtab.nextdefsym = @intCast(u32, nexports);5594 dysymtab.nextdefsym = @intCast(u32, nexports);
5601 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;5595 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
5602 dysymtab.nundefsym = @intCast(u32, nundefs);5596 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;
5605 const stubs = &text_segment.sections.items[self.stubs_section_index.?];5599 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;
5607 const got = &data_const_segment.sections.items[self.got_section_index.?];5601 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;
5609 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];5603 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
56105604
5611 const nstubs = @intCast(u32, self.stubs_map.keys().len);5605 const nstubs = @intCast(u32, self.stubs_map.keys().len);
...@@ -5668,8 +5662,8 @@ fn writeStringTable(self: *MachO) !void {...@@ -5668,8 +5662,8 @@ fn writeStringTable(self: *MachO) !void {
5668 const tracy = trace(@src());5662 const tracy = trace(@src());
5669 defer tracy.end();5663 defer tracy.end();
56705664
5671 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;5665 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5672 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;5666 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
5673 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);5667 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
5674 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));5668 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
5675 seg.inner.filesize += symtab.strsize;5669 seg.inner.filesize += symtab.strsize;
...@@ -5689,7 +5683,7 @@ fn writeLinkeditSegment(self: *MachO) !void {...@@ -5689,7 +5683,7 @@ fn writeLinkeditSegment(self: *MachO) !void {
5689 const tracy = trace(@src());5683 const tracy = trace(@src());
5690 defer tracy.end();5684 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;
5693 seg.inner.filesize = 0;5687 seg.inner.filesize = 0;
56945688
5695 try self.writeDyldInfoData();5689 try self.writeDyldInfoData();
...@@ -5705,8 +5699,8 @@ fn writeCodeSignaturePadding(self: *MachO) !void {...@@ -5705,8 +5699,8 @@ fn writeCodeSignaturePadding(self: *MachO) !void {
5705 const tracy = trace(@src());5699 const tracy = trace(@src());
5706 defer tracy.end();5700 defer tracy.end();
57075701
5708 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;5702 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;5703 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
5710 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;5704 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
5711 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(5705 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
5712 self.base.options.emit.?.sub_path,5706 self.base.options.emit.?.sub_path,
...@@ -5732,8 +5726,8 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -5732,8 +5726,8 @@ fn writeCodeSignature(self: *MachO) !void {
5732 const tracy = trace(@src());5726 const tracy = trace(@src());
5733 defer tracy.end();5727 defer tracy.end();
57345728
5735 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;5729 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;5730 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
57375731
5738 var code_sig: CodeSignature = .{};5732 var code_sig: CodeSignature = .{};
5739 defer code_sig.deinit(self.base.allocator);5733 defer code_sig.deinit(self.base.allocator);
...@@ -5784,9 +5778,8 @@ fn writeLoadCommands(self: *MachO) !void {...@@ -5784,9 +5778,8 @@ fn writeLoadCommands(self: *MachO) !void {
57845778
5785/// Writes Mach-O file header.5779/// Writes Mach-O file header.
5786fn writeHeader(self: *MachO) !void {5780fn writeHeader(self: *MachO) !void {
5787 var header = commands.emptyHeader(.{5781 var header: macho.mach_header_64 = .{};
5788 .flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL,5782 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
5789 });
57905783
5791 switch (self.base.options.target.cpu.arch) {5784 switch (self.base.options.target.cpu.arch) {
5792 .aarch64 => {5785 .aarch64 => {
...@@ -5959,12 +5952,9 @@ fn snapshotState(self: *MachO) !void {...@@ -5959,12 +5952,9 @@ fn snapshotState(self: *MachO) !void {
5959 var nodes = std.ArrayList(Snapshot.Node).init(arena);5952 var nodes = std.ArrayList(Snapshot.Node).init(arena);
59605953
5961 for (self.section_ordinals.keys()) |key| {5954 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;
5963 const sect = seg.sections.items[key.sect];5956 const sect = seg.sections.items[key.sect];
5964 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{5957 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
5965 commands.segmentName(sect),
5966 commands.sectionName(sect),
5967 });
5968 try nodes.append(.{5958 try nodes.append(.{
5969 .address = sect.addr,5959 .address = sect.addr,
5970 .tag = .section_start,5960 .tag = .section_start,
...@@ -6035,12 +6025,12 @@ fn snapshotState(self: *MachO) !void {...@@ -6035,12 +6025,12 @@ fn snapshotState(self: *MachO) !void {
6035 const is_tlv = is_tlv: {6025 const is_tlv = is_tlv: {
6036 const source_sym = self.locals.items[atom.local_sym_index];6026 const source_sym = self.locals.items[atom.local_sym_index];
6037 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];6027 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;
6039 const match_sect = match_seg.sections.items[match.sect];6029 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;
6041 };6031 };
6042 if (is_tlv) {6032 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;
6044 const base_address = inner: {6034 const base_address = inner: {
6045 if (self.tlv_data_section_index) |i| {6035 if (self.tlv_data_section_index) |i| {
6046 break :inner match_seg.sections.items[i].addr;6036 break :inner match_seg.sections.items[i].addr;
...@@ -6200,14 +6190,14 @@ fn logSymtab(self: MachO) void {...@@ -6200,14 +6190,14 @@ fn logSymtab(self: MachO) void {
62006190
6201fn logSectionOrdinals(self: MachO) void {6191fn logSectionOrdinals(self: MachO) void {
6202 for (self.section_ordinals.keys()) |match, i| {6192 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;
6204 const sect = seg.sections.items[match.sect];6194 const sect = seg.sections.items[match.sect];
6205 log.debug("ord {d}: {d},{d} => {s},{s}", .{6195 log.debug("ord {d}: {d},{d} => {s},{s}", .{
6206 i + 1,6196 i + 1,
6207 match.seg,6197 match.seg,
6208 match.sect,6198 match.sect,
6209 commands.segmentName(sect),6199 sect.segName(),
6210 commands.sectionName(sect),6200 sect.sectName(),
6211 });6201 });
6212 }6202 }
6213}6203}
src/link/MachO/Atom.zig+9-10
...@@ -4,7 +4,6 @@ const std = @import("std");...@@ -4,7 +4,6 @@ const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const aarch64 = @import("../../arch/aarch64/bits.zig");5const aarch64 = @import("../../arch/aarch64/bits.zig");
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const commands = @import("commands.zig");
8const log = std.log.scoped(.link);7const log = std.log.scoped(.link);
9const macho = std.macho;8const macho = std.macho;
10const math = std.math;9const math = std.math;
...@@ -342,7 +341,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -342,7 +341,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
342 if (rel.r_extern == 0) {341 if (rel.r_extern == 0) {
343 const sect_id = @intCast(u16, rel.r_symbolnum - 1);342 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
344 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {343 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;
346 const sect = seg.sections.items[sect_id];345 const sect = seg.sections.items[sect_id];
347 const match = (try context.macho_file.getMatchingSection(sect)) orelse346 const match = (try context.macho_file.getMatchingSection(sect)) orelse
348 unreachable;347 unreachable;
...@@ -398,7 +397,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -398,7 +397,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
398 else397 else
399 mem.readIntLittle(i32, self.code.items[offset..][0..4]);398 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
400 if (rel.r_extern == 0) {399 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;
402 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;401 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
403 addend -= @intCast(i64, target_sect_base_addr);402 addend -= @intCast(i64, target_sect_base_addr);
404 }403 }
...@@ -425,7 +424,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -425,7 +424,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
425 else424 else
426 mem.readIntLittle(i32, self.code.items[offset..][0..4]);425 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
427 if (rel.r_extern == 0) {426 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;
429 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;428 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
430 addend -= @intCast(i64, target_sect_base_addr);429 addend -= @intCast(i64, target_sect_base_addr);
431 }430 }
...@@ -447,7 +446,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -447,7 +446,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
447 if (rel.r_extern == 0) {446 if (rel.r_extern == 0) {
448 // Note for the future self: when r_extern == 0, we should subtract correction from the447 // Note for the future self: when r_extern == 0, we should subtract correction from the
449 // addend.448 // 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;
451 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;450 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
452 addend += @intCast(i64, context.base_addr + offset + 4) -451 addend += @intCast(i64, context.base_addr + offset + 4) -
453 @intCast(i64, target_sect_base_addr);452 @intCast(i64, target_sect_base_addr);
...@@ -490,9 +489,9 @@ fn addPtrBindingOrRebase(...@@ -490,9 +489,9 @@ fn addPtrBindingOrRebase(
490 .local => {489 .local => {
491 const source_sym = context.macho_file.locals.items[self.local_sym_index];490 const source_sym = context.macho_file.locals.items[self.local_sym_index];
492 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];491 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;
494 const sect = seg.sections.items[match.sect];493 const sect = seg.sections.items[match.sect];
495 const sect_type = commands.sectionType(sect);494 const sect_type = sect.type_();
496495
497 const should_rebase = rebase: {496 const should_rebase = rebase: {
498 if (rel.r_length != 3) break :rebase false;497 if (rel.r_length != 3) break :rebase false;
...@@ -705,9 +704,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -705,9 +704,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
705 const is_tlv = is_tlv: {704 const is_tlv = is_tlv: {
706 const source_sym = macho_file.locals.items[self.local_sym_index];705 const source_sym = macho_file.locals.items[self.local_sym_index];
707 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];706 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;
709 const sect = seg.sections.items[match.sect];708 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;
711 };710 };
712 if (is_tlv) {711 if (is_tlv) {
713 // For TLV relocations, the value specified as a relocation is the displacement from the712 // 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 {...@@ -715,7 +714,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
715 // defined TLV template init section in the following order:714 // defined TLV template init section in the following order:
716 // * wrt to __thread_data if defined, then715 // * wrt to __thread_data if defined, then
717 // * wrt to __thread_bss716 // * 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;
719 const base_address = inner: {718 const base_address = inner: {
720 if (macho_file.tlv_data_section_index) |i| {719 if (macho_file.tlv_data_section_index) |i| {
721 break :inner seg.sections.items[i].addr;720 break :inner seg.sections.items[i].addr;
src/link/MachO/DebugSymbols.zig+87-54
...@@ -12,15 +12,12 @@ const leb = std.leb;...@@ -12,15 +12,12 @@ const leb = std.leb;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
1313
14const build_options = @import("build_options");14const build_options = @import("build_options");
15const commands = @import("commands.zig");
16const trace = @import("../../tracy.zig").trace;15const trace = @import("../../tracy.zig").trace;
17const LoadCommand = commands.LoadCommand;
18const Module = @import("../../Module.zig");16const Module = @import("../../Module.zig");
19const Type = @import("../../type.zig").Type;17const Type = @import("../../type.zig").Type;
20const link = @import("../../link.zig");18const link = @import("../../link.zig");
21const MachO = @import("../MachO.zig");19const MachO = @import("../MachO.zig");
22const TextBlock = MachO.TextBlock;20const TextBlock = MachO.TextBlock;
23const SegmentCommand = commands.SegmentCommand;
24const SrcFn = MachO.SrcFn;21const SrcFn = MachO.SrcFn;
25const makeStaticString = MachO.makeStaticString;22const makeStaticString = MachO.makeStaticString;
26const padToIdeal = MachO.padToIdeal;23const padToIdeal = MachO.padToIdeal;
...@@ -31,7 +28,7 @@ base: *MachO,...@@ -31,7 +28,7 @@ base: *MachO,
31file: fs.File,28file: fs.File,
3229
33/// Table of all load commands30/// Table of all load commands
34load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},31load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
35/// __PAGEZERO segment32/// __PAGEZERO segment
36pagezero_segment_cmd_index: ?u16 = null,33pagezero_segment_cmd_index: ?u16 = null,
37/// __TEXT segment34/// __TEXT segment
...@@ -113,7 +110,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -113,7 +110,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
113 }110 }
114 if (self.symtab_cmd_index == null) {111 if (self.symtab_cmd_index == null) {
115 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);112 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;
117 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);114 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
118 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));115 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
119116
...@@ -124,7 +121,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -124,7 +121,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
124 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });121 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
125122
126 try self.load_commands.append(allocator, .{123 try self.load_commands.append(allocator, .{
127 .Symtab = .{124 .symtab = .{
128 .cmd = macho.LC_SYMTAB,125 .cmd = macho.LC_SYMTAB,
129 .cmdsize = @sizeOf(macho.symtab_command),126 .cmdsize = @sizeOf(macho.symtab_command),
130 .symoff = @intCast(u32, symtab_off),127 .symoff = @intCast(u32, symtab_off),
...@@ -138,48 +135,48 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -138,48 +135,48 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
138 }135 }
139 if (self.pagezero_segment_cmd_index == null) {136 if (self.pagezero_segment_cmd_index == null) {
140 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);137 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;
142 const cmd = try self.copySegmentCommand(allocator, base_cmd);139 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 });
144 self.load_commands_dirty = true;141 self.load_commands_dirty = true;
145 }142 }
146 if (self.text_segment_cmd_index == null) {143 if (self.text_segment_cmd_index == null) {
147 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);144 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;
149 const cmd = try self.copySegmentCommand(allocator, base_cmd);146 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 });
151 self.load_commands_dirty = true;148 self.load_commands_dirty = true;
152 }149 }
153 if (self.data_const_segment_cmd_index == null) outer: {150 if (self.data_const_segment_cmd_index == null) outer: {
154 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional151 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
155 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);152 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;
157 const cmd = try self.copySegmentCommand(allocator, base_cmd);154 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 });
159 self.load_commands_dirty = true;156 self.load_commands_dirty = true;
160 }157 }
161 if (self.data_segment_cmd_index == null) outer: {158 if (self.data_segment_cmd_index == null) outer: {
162 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional159 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
163 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);160 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;
165 const cmd = try self.copySegmentCommand(allocator, base_cmd);162 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 });
167 self.load_commands_dirty = true;164 self.load_commands_dirty = true;
168 }165 }
169 if (self.linkedit_segment_cmd_index == null) {166 if (self.linkedit_segment_cmd_index == null) {
170 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);167 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;
172 var cmd = try self.copySegmentCommand(allocator, base_cmd);169 var cmd = try self.copySegmentCommand(allocator, base_cmd);
173 cmd.inner.vmsize = self.linkedit_size;170 cmd.inner.vmsize = self.linkedit_size;
174 cmd.inner.fileoff = self.linkedit_off;171 cmd.inner.fileoff = self.linkedit_off;
175 cmd.inner.filesize = self.linkedit_size;172 cmd.inner.filesize = self.linkedit_size;
176 try self.load_commands.append(allocator, .{ .Segment = cmd });173 try self.load_commands.append(allocator, .{ .segment = cmd });
177 self.load_commands_dirty = true;174 self.load_commands_dirty = true;
178 }175 }
179 if (self.dwarf_segment_cmd_index == null) {176 if (self.dwarf_segment_cmd_index == null) {
180 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);177 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;
183 const ideal_size: u16 = 200 + 128 + 160 + 250;180 const ideal_size: u16 = 200 + 128 + 160 + 250;
184 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);181 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);
185 const off = linkedit.inner.fileoff + linkedit.inner.filesize;182 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
...@@ -188,7 +185,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -188,7 +185,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
188 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });185 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
189186
190 try self.load_commands.append(allocator, .{187 try self.load_commands.append(allocator, .{
191 .Segment = .{188 .segment = .{
192 .inner = .{189 .inner = .{
193 .segname = makeStaticString("__DWARF"),190 .segname = makeStaticString("__DWARF"),
194 .vmaddr = vmaddr,191 .vmaddr = vmaddr,
...@@ -228,7 +225,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -228,7 +225,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
228}225}
229226
230fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {227fn 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;
232 var sect = macho.section_64{229 var sect = macho.section_64{
233 .sectname = makeStaticString(sectname),230 .sectname = makeStaticString(sectname),
234 .segname = seg.inner.segname,231 .segname = seg.inner.segname,
...@@ -236,13 +233,13 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -236,13 +233,13 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
236 .@"align" = alignment,233 .@"align" = alignment,
237 };234 };
238 const alignment_pow_2 = try math.powi(u32, 2, alignment);235 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
241 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand238 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
242239
243 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{240 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
244 commands.segmentName(sect),241 sect.segName(),
245 commands.sectionName(sect),242 sect.sectName(),
246 off,243 off,
247 off + size,244 off + size,
248 });245 });
...@@ -268,6 +265,28 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -268,6 +265,28 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
268 return index;265 return index;
269}266}
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
271pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {290pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {
272 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the291 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
273 // Zig source code.292 // Zig source code.
...@@ -275,7 +294,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -275,7 +294,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
275 const init_len_size: usize = 4;294 const init_len_size: usize = 4;
276295
277 if (self.debug_abbrev_section_dirty) {296 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;
279 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];298 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
280299
281 // These are LEB encoded but since the values are all less than 127300 // 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...@@ -320,10 +339,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
320 };339 };
321340
322 const needed_size = abbrev_buf.len;341 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);
324 if (needed_size > allocated_size) {343 if (needed_size > allocated_size) {
325 debug_abbrev_sect.size = 0; // free the space344 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);
327 debug_abbrev_sect.offset = @intCast(u32, offset);346 debug_abbrev_sect.offset = @intCast(u32, offset);
328 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;347 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
329 }348 }
...@@ -345,7 +364,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -345,7 +364,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
345 // leave debug_info_header_dirty=true.364 // leave debug_info_header_dirty=true.
346 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;365 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
347 const last_dbg_info_decl = self.dbg_info_decl_last.?;366 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;
349 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];368 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
350369
351 // We have a function to compute the upper bound size, because it's needed370 // 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...@@ -372,7 +391,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
372 const producer_strp = try self.makeDebugString(allocator, link.producer_string);391 const producer_strp = try self.makeDebugString(allocator, link.producer_string);
373 // Currently only one compilation unit is supported, so the address range is simply392 // Currently only one compilation unit is supported, so the address range is simply
374 // identical to the main program header virtual address and memory size.393 // 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;
376 const text_section = text_segment.sections.items[self.text_section_index.?];395 const text_section = text_segment.sections.items[self.text_section_index.?];
377 const low_pc = text_section.addr;396 const low_pc = text_section.addr;
378 const high_pc = text_section.addr + text_section.size;397 const high_pc = text_section.addr + text_section.size;
...@@ -399,7 +418,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -399,7 +418,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
399 }418 }
400419
401 if (self.debug_aranges_section_dirty) {420 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;
403 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];422 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
404423
405 // Enough for all the data without resizing. When support for more compilation units424 // 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...@@ -426,7 +445,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
426445
427 // Currently only one compilation unit is supported, so the address range is simply446 // Currently only one compilation unit is supported, so the address range is simply
428 // identical to the main program header virtual address and memory size.447 // 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;
430 const text_section = text_segment.sections.items[self.text_section_index.?];449 const text_section = text_segment.sections.items[self.text_section_index.?];
431 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);450 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);
432 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);451 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
...@@ -442,10 +461,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -442,10 +461,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
442 mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));461 mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));
443462
444 const needed_size = di_buf.items.len;463 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);
446 if (needed_size > allocated_size) {465 if (needed_size > allocated_size) {
447 debug_aranges_sect.size = 0; // free the space466 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);
449 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;468 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
450 debug_aranges_sect.offset = @intCast(u32, new_offset);469 debug_aranges_sect.offset = @intCast(u32, new_offset);
451 }470 }
...@@ -467,7 +486,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -467,7 +486,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
467 const dbg_line_prg_end = self.getDebugLineProgramEnd();486 const dbg_line_prg_end = self.getDebugLineProgramEnd();
468 assert(dbg_line_prg_end != 0);487 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;
471 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];490 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
472491
473 // The size of this header is variable, depending on the number of directories,492 // 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...@@ -540,15 +559,15 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
540 self.debug_line_header_dirty = false;559 self.debug_line_header_dirty = false;
541 }560 }
542 {561 {
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;
544 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];563 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
545 if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {564 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);
547 const needed_size = self.debug_string_table.items.len;566 const needed_size = self.debug_string_table.items.len;
548567
549 if (needed_size > allocated_size) {568 if (needed_size > allocated_size) {
550 debug_strtab_sect.size = 0; // free the space569 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);
552 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;571 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
553 debug_strtab_sect.offset = @intCast(u32, new_offset);572 debug_strtab_sect.offset = @intCast(u32, new_offset);
554 }573 }
...@@ -588,8 +607,12 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {...@@ -588,8 +607,12 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
588 self.file.close();607 self.file.close();
589}608}
590609
591fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: SegmentCommand) !SegmentCommand {610fn copySegmentCommand(
592 var cmd = SegmentCommand{611 self: *DebugSymbols,
612 allocator: Allocator,
613 base_cmd: macho.SegmentCommand,
614) !macho.SegmentCommand {
615 var cmd = macho.SegmentCommand{
593 .inner = .{616 .inner = .{
594 .segname = undefined,617 .segname = undefined,
595 .cmdsize = base_cmd.inner.cmdsize,618 .cmdsize = base_cmd.inner.cmdsize,
...@@ -633,7 +656,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: Segme...@@ -633,7 +656,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: Segme
633}656}
634657
635fn updateDwarfSegment(self: *DebugSymbols) void {658fn 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;
637 var file_size: u64 = 0;660 var file_size: u64 = 0;
638 for (dwarf_segment.sections.items) |sect| {661 for (dwarf_segment.sections.items) |sect| {
639 file_size += sect.size;662 file_size += sect.size;
...@@ -670,9 +693,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {...@@ -670,9 +693,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
670}693}
671694
672fn writeHeader(self: *DebugSymbols) !void {695fn writeHeader(self: *DebugSymbols) !void {
673 var header = commands.emptyHeader(.{696 var header: macho.mach_header_64 = .{};
674 .filetype = macho.MH_DSYM,697 header.filetype = macho.MH_DSYM;
675 });
676698
677 switch (self.base.base.options.target.cpu.arch) {699 switch (self.base.base.options.target.cpu.arch) {
678 .aarch64 => {700 .aarch64 => {
...@@ -703,7 +725,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {...@@ -703,7 +725,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
703 var min_pos: u64 = std.math.maxInt(u64);725 var min_pos: u64 = std.math.maxInt(u64);
704726
705 if (self.symtab_cmd_index) |idx| {727 if (self.symtab_cmd_index) |idx| {
706 const symtab = self.load_commands.items[idx].Symtab;728 const symtab = self.load_commands.items[idx].symtab;
707 if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;729 if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
708 if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;730 if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
709 }731 }
...@@ -711,12 +733,23 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {...@@ -711,12 +733,23 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
711 return min_pos - start;733 return min_pos - start;
712}734}
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
714fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {747fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
715 const end = start + padToIdeal(size);748 const end = start + padToIdeal(size);
716749
717 if (self.symtab_cmd_index) |idx| outer: {750 if (self.symtab_cmd_index) |idx| outer: {
718 if (self.load_commands.items.len == idx) break :outer;751 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;
720 {753 {
721 // Symbol table754 // Symbol table
722 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);755 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
...@@ -748,7 +781,7 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u...@@ -748,7 +781,7 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
748}781}
749782
750fn relocateSymbolTable(self: *DebugSymbols) !void {783fn 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;
752 const nlocals = self.base.locals.items.len;785 const nlocals = self.base.locals.items.len;
753 const nglobals = self.base.globals.items.len;786 const nglobals = self.base.globals.items.len;
754 const nsyms = nlocals + nglobals;787 const nsyms = nlocals + nglobals;
...@@ -781,7 +814,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {...@@ -781,7 +814,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
781 const tracy = trace(@src());814 const tracy = trace(@src());
782 defer tracy.end();815 defer tracy.end();
783 try self.relocateSymbolTable();816 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;
785 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;818 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
786 log.debug("writing local symbol {} at 0x{x}", .{ index, off });819 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
787 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);820 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
...@@ -793,7 +826,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -793,7 +826,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
793 const tracy = trace(@src());826 const tracy = trace(@src());
794 defer tracy.end();827 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;
797 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);830 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
798 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));831 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...@@ -817,7 +850,7 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
817 const func = decl.val.castTag(.function).?.data;850 const func = decl.val.castTag(.function).?.data;
818 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);851 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;
821 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];854 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
822 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();855 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
823 var data: [4]u8 = undefined;856 var data: [4]u8 = undefined;
...@@ -983,7 +1016,7 @@ pub fn commitDeclDebugInfo(...@@ -983,7 +1016,7 @@ pub fn commitDeclDebugInfo(
983 // `TextBlock` and the .debug_info. If you are editing this logic, you1016 // `TextBlock` and the .debug_info. If you are editing this logic, you
984 // probably need to edit that logic too.1017 // 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;
987 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];1020 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
988 const src_fn = &decl.fn_link.macho;1021 const src_fn = &decl.fn_link.macho;
989 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);1022 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
...@@ -1029,8 +1062,8 @@ pub fn commitDeclDebugInfo(...@@ -1029,8 +1062,8 @@ pub fn commitDeclDebugInfo(
1029 const last_src_fn = self.dbg_line_fn_last.?;1062 const last_src_fn = self.dbg_line_fn_last.?;
1030 const needed_size = last_src_fn.off + last_src_fn.len;1063 const needed_size = last_src_fn.off + last_src_fn.len;
1031 if (needed_size != debug_line_sect.size) {1064 if (needed_size != debug_line_sect.size) {
1032 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {1065 if (needed_size > self.allocatedSize(debug_line_sect.offset)) {
1033 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);1066 const new_offset = self.findFreeSpace(needed_size, 1);
1034 const existing_size = last_src_fn.off;1067 const existing_size = last_src_fn.off;
10351068
1036 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{1069 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
...@@ -1152,7 +1185,7 @@ fn updateDeclDebugInfoAllocation(...@@ -1152,7 +1185,7 @@ fn updateDeclDebugInfoAllocation(
1152 // `SrcFn` and the line number programs. If you are editing this logic, you1185 // `SrcFn` and the line number programs. If you are editing this logic, you
1153 // probably need to edit that logic too.1186 // 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;
1156 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];1189 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
1157 text_block.dbg_info_len = len;1190 text_block.dbg_info_len = len;
1158 if (self.dbg_info_decl_last) |last| blk: {1191 if (self.dbg_info_decl_last) |last| blk: {
...@@ -1203,15 +1236,15 @@ fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf:...@@ -1203,15 +1236,15 @@ fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf:
1203 // `SrcFn` and the line number programs. If you are editing this logic, you1236 // `SrcFn` and the line number programs. If you are editing this logic, you
1204 // probably need to edit that logic too.1237 // 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;
1207 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];1240 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
12081241
1209 const last_decl = self.dbg_info_decl_last.?;1242 const last_decl = self.dbg_info_decl_last.?;
1210 // +1 for a trailing zero to end the children of the decl tag.1243 // +1 for a trailing zero to end the children of the decl tag.
1211 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;1244 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
1212 if (needed_size != debug_info_sect.size) {1245 if (needed_size != debug_info_sect.size) {
1213 if (needed_size > dwarf_segment.allocatedSize(debug_info_sect.offset)) {1246 if (needed_size > self.allocatedSize(debug_info_sect.offset)) {
1214 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);1247 const new_offset = self.findFreeSpace(needed_size, 1);
1215 const existing_size = last_decl.dbg_info_off;1248 const existing_size = last_decl.dbg_info_off;
12161249
1217 log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{1250 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;...@@ -9,11 +9,9 @@ const macho = std.macho;
9const math = std.math;9const math = std.math;
10const mem = std.mem;10const mem = std.mem;
11const fat = @import("fat.zig");11const fat = @import("fat.zig");
12const commands = @import("commands.zig");
1312
14const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
15const LibStub = @import("../tapi.zig").LibStub;14const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommand = commands.LoadCommand;
17const MachO = @import("../MachO.zig");15const MachO = @import("../MachO.zig");
1816
19file: fs.File,17file: fs.File,
...@@ -25,7 +23,7 @@ header: ?macho.mach_header_64 = null,...@@ -25,7 +23,7 @@ header: ?macho.mach_header_64 = null,
25// an offset within a file if we are linking against a fat lib23// an offset within a file if we are linking against a fat lib
26library_offset: u64 = 0,24library_offset: u64 = 0,
2725
28load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},26load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
2927
30symtab_cmd_index: ?u16 = null,28symtab_cmd_index: ?u16 = null,
31dysymtab_cmd_index: ?u16 = null,29dysymtab_cmd_index: ?u16 = null,
...@@ -53,7 +51,7 @@ pub const Id = struct {...@@ -53,7 +51,7 @@ pub const Id = struct {
53 };51 };
54 }52 }
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 {
57 const dylib = lc.inner.dylib;55 const dylib = lc.inner.dylib;
58 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);56 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
59 const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));57 const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));
...@@ -177,7 +175,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende...@@ -177,7 +175,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
177175
178 var i: u16 = 0;176 var i: u16 = 0;
179 while (i < self.header.?.ncmds) : (i += 1) {177 while (i < self.header.?.ncmds) : (i += 1) {
180 var cmd = try LoadCommand.read(allocator, reader);178 var cmd = try macho.LoadCommand.read(allocator, reader);
181 switch (cmd.cmd()) {179 switch (cmd.cmd()) {
182 macho.LC_SYMTAB => {180 macho.LC_SYMTAB => {
183 self.symtab_cmd_index = i;181 self.symtab_cmd_index = i;
...@@ -191,7 +189,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende...@@ -191,7 +189,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
191 macho.LC_REEXPORT_DYLIB => {189 macho.LC_REEXPORT_DYLIB => {
192 if (should_lookup_reexports) {190 if (should_lookup_reexports) {
193 // Parse install_name to dependent dylib.191 // Parse install_name to dependent dylib.
194 var id = try Id.fromLoadCommand(allocator, cmd.Dylib);192 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
195 try dependent_libs.writeItem(id);193 try dependent_libs.writeItem(id);
196 }194 }
197 },195 },
...@@ -209,12 +207,12 @@ fn parseId(self: *Dylib, allocator: Allocator) !void {...@@ -209,12 +207,12 @@ fn parseId(self: *Dylib, allocator: Allocator) !void {
209 self.id = try Id.default(allocator, self.name);207 self.id = try Id.default(allocator, self.name);
210 return;208 return;
211 };209 };
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);
213}211}
214212
215fn parseSymbols(self: *Dylib, allocator: Allocator) !void {213fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
216 const index = self.symtab_cmd_index orelse return;214 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
219 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);217 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
220 defer allocator.free(symtab);218 defer allocator.free(symtab);
src/link/MachO/Object.zig+15-22
...@@ -11,14 +11,10 @@ const macho = std.macho;...@@ -11,14 +11,10 @@ const macho = std.macho;
11const math = std.math;11const math = std.math;
12const mem = std.mem;12const mem = std.mem;
13const sort = std.sort;13const sort = std.sort;
14const commands = @import("commands.zig");
15const segmentName = commands.segmentName;
16const sectionName = commands.sectionName;
17const trace = @import("../../tracy.zig").trace;14const trace = @import("../../tracy.zig").trace;
1815
19const Allocator = mem.Allocator;16const Allocator = mem.Allocator;
20const Atom = @import("Atom.zig");17const Atom = @import("Atom.zig");
21const LoadCommand = commands.LoadCommand;
22const MachO = @import("../MachO.zig");18const MachO = @import("../MachO.zig");
2319
24file: fs.File,20file: fs.File,
...@@ -28,7 +24,7 @@ file_offset: ?u32 = null,...@@ -28,7 +24,7 @@ file_offset: ?u32 = null,
2824
29header: ?macho.mach_header_64 = null,25header: ?macho.mach_header_64 = null,
3026
31load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},27load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
3228
33segment_cmd_index: ?u16 = null,29segment_cmd_index: ?u16 = null,
34text_section_index: ?u16 = null,30text_section_index: ?u16 = null,
...@@ -271,15 +267,15 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -271,15 +267,15 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
271267
272 var i: u16 = 0;268 var i: u16 = 0;
273 while (i < header.ncmds) : (i += 1) {269 while (i < header.ncmds) : (i += 1) {
274 var cmd = try LoadCommand.read(allocator, reader);270 var cmd = try macho.LoadCommand.read(allocator, reader);
275 switch (cmd.cmd()) {271 switch (cmd.cmd()) {
276 macho.LC_SEGMENT_64 => {272 macho.LC_SEGMENT_64 => {
277 self.segment_cmd_index = i;273 self.segment_cmd_index = i;
278 var seg = cmd.Segment;274 var seg = cmd.segment;
279 for (seg.sections.items) |*sect, j| {275 for (seg.sections.items) |*sect, j| {
280 const index = @intCast(u16, j);276 const index = @intCast(u16, j);
281 const segname = segmentName(sect.*);277 const segname = sect.segName();
282 const sectname = sectionName(sect.*);278 const sectname = sect.sectName();
283 if (mem.eql(u8, segname, "__DWARF")) {279 if (mem.eql(u8, segname, "__DWARF")) {
284 if (mem.eql(u8, sectname, "__debug_info")) {280 if (mem.eql(u8, sectname, "__debug_info")) {
285 self.dwarf_debug_info_index = index;281 self.dwarf_debug_info_index = index;
...@@ -308,8 +304,8 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -308,8 +304,8 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
308 },304 },
309 macho.LC_SYMTAB => {305 macho.LC_SYMTAB => {
310 self.symtab_cmd_index = i;306 self.symtab_cmd_index = i;
311 cmd.Symtab.symoff += offset;307 cmd.symtab.symoff += offset;
312 cmd.Symtab.stroff += offset;308 cmd.symtab.stroff += offset;
313 },309 },
314 macho.LC_DYSYMTAB => {310 macho.LC_DYSYMTAB => {
315 self.dysymtab_cmd_index = i;311 self.dysymtab_cmd_index = i;
...@@ -319,7 +315,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -319,7 +315,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
319 },315 },
320 macho.LC_DATA_IN_CODE => {316 macho.LC_DATA_IN_CODE => {
321 self.data_in_code_cmd_index = i;317 self.data_in_code_cmd_index = i;
322 cmd.LinkeditData.dataoff += offset;318 cmd.linkedit_data.dataoff += offset;
323 },319 },
324 else => {320 else => {
325 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});321 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
...@@ -385,7 +381,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -385,7 +381,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
385 const tracy = trace(@src());381 const tracy = trace(@src());
386 defer tracy.end();382 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
390 log.debug("analysing {s}", .{self.name});386 log.debug("analysing {s}", .{self.name});
391387
...@@ -408,7 +404,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -408,7 +404,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
408 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we404 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
409 // have to infer the start of undef section in the symtab ourselves.405 // have to infer the start of undef section in the symtab ourselves.
410 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {406 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;
412 break :blk dysymtab.iundefsym;408 break :blk dysymtab.iundefsym;
413 } else blk: {409 } else blk: {
414 var iundefsym: usize = sorted_all_nlists.items.len;410 var iundefsym: usize = sorted_all_nlists.items.len;
...@@ -424,10 +420,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -424,10 +420,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
424420
425 for (seg.sections.items) |sect, id| {421 for (seg.sections.items) |sect, id| {
426 const sect_id = @intCast(u8, id);422 const sect_id = @intCast(u8, id);
427 log.debug("putting section '{s},{s}' as an Atom", .{423 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });
428 segmentName(sect),
429 sectionName(sect),
430 });
431424
432 // Get matching segment/section in the final artifact.425 // Get matching segment/section in the final artifact.
433 const match = (try macho_file.getMatchingSection(sect)) orelse {426 const match = (try macho_file.getMatchingSection(sect)) orelse {
...@@ -479,7 +472,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -479,7 +472,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
479 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");472 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");
480473
481 const is_zerofill = blk: {474 const is_zerofill = blk: {
482 const section_type = commands.sectionType(sect);475 const section_type = sect.type_();
483 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;476 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
484 };477 };
485 if (!is_zerofill) {478 if (!is_zerofill) {
...@@ -559,7 +552,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -559,7 +552,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
559552
560fn parseSymtab(self: *Object, allocator: Allocator) !void {553fn parseSymtab(self: *Object, allocator: Allocator) !void {
561 const index = self.symtab_cmd_index orelse return;554 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
564 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);557 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
565 defer allocator.free(symtab);558 defer allocator.free(symtab);
...@@ -607,7 +600,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {...@@ -607,7 +600,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
607600
608pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {601pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
609 const index = self.data_in_code_cmd_index orelse return;602 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
612 var buffer = try allocator.alloc(u8, data_in_code.datasize);605 var buffer = try allocator.alloc(u8, data_in_code.datasize);
613 defer allocator.free(buffer);606 defer allocator.free(buffer);
...@@ -626,7 +619,7 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {...@@ -626,7 +619,7 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
626}619}
627620
628fn readSection(self: Object, allocator: Allocator, index: u16) ![]u8 {621fn 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;
630 const sect = seg.sections.items[index];623 const sect = seg.sections.items[index];
631 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));624 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
632 _ = try self.file.preadAll(buffer, sect.offset);625 _ = 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}