authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-31 18:19:17+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-03 21:19:41+02:00
logf26d5ee7ea97c8fd6e5b2655f845be7e4293930e
treefab17016b079fcd7aaef84672feb469136dcc646
parent4c750016eb9b1c0831cbb0398a4d6ee9dbdc932e

macho: sync with zld

gitrev a2c32e972f8c5adfcda8ed2d99379ae868f59c24 https://github.com/kubkon/zld/commit/a2c32e972f8c5adfcda8ed2d99379ae868f59c24

12 files changed, 2124 insertions(+), 3073 deletions(-)

lib/std/build/CheckObjectStep.zig+47-42
......@@ -283,7 +283,14 @@ fn make(step: *Step) !void {
283283
284284 const gpa = self.builder.allocator;
285285 const src_path = self.source.getPath(self.builder);
286 const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
286 const contents = try fs.cwd().readFileAllocOptions(
287 gpa,
288 src_path,
289 self.max_bytes,
290 null,
291 @alignOf(u64),
292 null,
293 );
287294
288295 const output = switch (self.obj_format) {
289296 .macho => try MachODumper.parseAndDump(contents, .{
......@@ -370,9 +377,10 @@ const Opts = struct {
370377};
371378
372379const MachODumper = struct {
380 const LoadCommandIterator = macho.LoadCommandIterator;
373381 const symtab_label = "symtab";
374382
375 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
383 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
376384 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
377385 var stream = std.io.fixedBufferStream(bytes);
378386 const reader = stream.reader();
......@@ -385,55 +393,54 @@ const MachODumper = struct {
385393 var output = std.ArrayList(u8).init(gpa);
386394 const writer = output.writer();
387395
388 var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
389 try load_commands.ensureTotalCapacity(hdr.ncmds);
390
391 var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
392 var imports = std.ArrayList(u16).init(gpa);
393
394 var symtab_cmd: ?u16 = null;
395 var i: u16 = 0;
396 while (i < hdr.ncmds) : (i += 1) {
397 var cmd = try macho.LoadCommand.read(gpa, reader);
398 load_commands.appendAssumeCapacity(cmd);
396 var symtab: []const macho.nlist_64 = undefined;
397 var strtab: []const u8 = undefined;
398 var sections = std.ArrayList(macho.section_64).init(gpa);
399 var imports = std.ArrayList([]const u8).init(gpa);
399400
401 var it = LoadCommandIterator{
402 .ncmds = hdr.ncmds,
403 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
404 };
405 var i: usize = 0;
406 while (it.next()) |cmd| {
400407 switch (cmd.cmd()) {
401408 .SEGMENT_64 => {
402 const seg = cmd.segment;
403 for (seg.sections.items) |_, j| {
404 try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
409 const seg = cmd.cast(macho.segment_command_64).?;
410 try sections.ensureUnusedCapacity(seg.nsects);
411 for (cmd.getSections()) |sect| {
412 sections.appendAssumeCapacity(sect);
405413 }
406414 },
407 .SYMTAB => {
408 symtab_cmd = i;
415 .SYMTAB => if (opts.dump_symtab) {
416 const lc = cmd.cast(macho.symtab_command).?;
417 symtab = @ptrCast(
418 [*]const macho.nlist_64,
419 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
420 )[0..lc.nsyms];
421 strtab = bytes[lc.stroff..][0..lc.strsize];
409422 },
410423 .LOAD_DYLIB,
411424 .LOAD_WEAK_DYLIB,
412425 .REEXPORT_DYLIB,
413426 => {
414 try imports.append(i);
427 try imports.append(cmd.getDylibPathName());
415428 },
416429 else => {},
417430 }
418431
419432 try dumpLoadCommand(cmd, i, writer);
420433 try writer.writeByte('\n');
434
435 i += 1;
421436 }
422437
423438 if (opts.dump_symtab) {
424 const cmd = load_commands.items[symtab_cmd.?].symtab;
425 try writer.writeAll(symtab_label ++ "\n");
426 const strtab = bytes[cmd.stroff..][0..cmd.strsize];
427 const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
428 const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab);
429
430439 for (symtab) |sym| {
431440 if (sym.stab()) continue;
432441 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
433442 if (sym.sect()) {
434 const map = sections.items[sym.n_sect - 1];
435 const seg = load_commands.items[map.seg].segment;
436 const sect = seg.sections.items[map.sect];
443 const sect = sections.items[sym.n_sect - 1];
437444 try writer.print("{x} ({s},{s})", .{
438445 sym.n_value,
439446 sect.segName(),
......@@ -455,9 +462,7 @@ const MachODumper = struct {
455462 break :blk "flat lookup";
456463 unreachable;
457464 }
458 const import_id = imports.items[@bitCast(u16, ordinal) - 1];
459 const import = load_commands.items[import_id].dylib;
460 const full_path = mem.sliceTo(import.data, 0);
465 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
461466 const basename = fs.path.basename(full_path);
462467 assert(basename.len > 0);
463468 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
......@@ -481,7 +486,7 @@ const MachODumper = struct {
481486 return output.toOwnedSlice();
482487 }
483488
484 fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {
489 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
485490 // print header first
486491 try writer.print(
487492 \\LC {d}
......@@ -491,8 +496,7 @@ const MachODumper = struct {
491496
492497 switch (lc.cmd()) {
493498 .SEGMENT_64 => {
494 // TODO dump section headers
495 const seg = lc.segment.inner;
499 const seg = lc.cast(macho.segment_command_64).?;
496500 try writer.writeByte('\n');
497501 try writer.print(
498502 \\segname {s}
......@@ -508,7 +512,7 @@ const MachODumper = struct {
508512 seg.filesize,
509513 });
510514
511 for (lc.segment.sections.items) |sect| {
515 for (lc.getSections()) |sect| {
512516 try writer.writeByte('\n');
513517 try writer.print(
514518 \\sectname {s}
......@@ -531,7 +535,7 @@ const MachODumper = struct {
531535 .LOAD_WEAK_DYLIB,
532536 .REEXPORT_DYLIB,
533537 => {
534 const dylib = lc.dylib.inner.dylib;
538 const dylib = lc.cast(macho.dylib_command).?;
535539 try writer.writeByte('\n');
536540 try writer.print(
537541 \\name {s}
......@@ -539,19 +543,20 @@ const MachODumper = struct {
539543 \\current version {x}
540544 \\compatibility version {x}
541545 , .{
542 mem.sliceTo(lc.dylib.data, 0),
543 dylib.timestamp,
544 dylib.current_version,
545 dylib.compatibility_version,
546 lc.getDylibPathName(),
547 dylib.dylib.timestamp,
548 dylib.dylib.current_version,
549 dylib.dylib.compatibility_version,
546550 });
547551 },
548552
549553 .MAIN => {
554 const main = lc.cast(macho.entry_point_command).?;
550555 try writer.writeByte('\n');
551556 try writer.print(
552557 \\entryoff {x}
553558 \\stacksize {x}
554 , .{ lc.main.entryoff, lc.main.stacksize });
559 , .{ main.entryoff, main.stacksize });
555560 },
556561
557562 .RPATH => {
......@@ -559,7 +564,7 @@ const MachODumper = struct {
559564 try writer.print(
560565 \\path {s}
561566 , .{
562 mem.sliceTo(lc.rpath.data, 0),
567 lc.getRpathPathName(),
563568 });
564569 },
565570
lib/std/macho.zig+49-408
......@@ -1835,429 +1835,70 @@ pub const data_in_code_entry = extern struct {
18351835 kind: u16,
18361836};
18371837
1838/// A Zig wrapper for all known MachO load commands.
1839/// Provides interface to read and write the load command data to a buffer.
1840pub const LoadCommand = union(enum) {
1841 segment: SegmentCommand,
1842 dyld_info_only: dyld_info_command,
1843 symtab: symtab_command,
1844 dysymtab: dysymtab_command,
1845 dylinker: GenericCommandWithData(dylinker_command),
1846 dylib: GenericCommandWithData(dylib_command),
1847 main: entry_point_command,
1848 version_min: version_min_command,
1849 source_version: source_version_command,
1850 build_version: GenericCommandWithData(build_version_command),
1851 uuid: uuid_command,
1852 linkedit_data: linkedit_data_command,
1853 rpath: GenericCommandWithData(rpath_command),
1854 unknown: GenericCommandWithData(load_command),
1855
1856 pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
1857 const header = try reader.readStruct(load_command);
1858 var buffer = try allocator.alloc(u8, header.cmdsize);
1859 defer allocator.free(buffer);
1860 mem.copy(u8, buffer, mem.asBytes(&header));
1861 try reader.readNoEof(buffer[@sizeOf(load_command)..]);
1862 var stream = io.fixedBufferStream(buffer);
1863
1864 return switch (header.cmd) {
1865 .SEGMENT_64 => LoadCommand{
1866 .segment = try SegmentCommand.read(allocator, stream.reader()),
1867 },
1868 .DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
1869 .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
1870 },
1871 .SYMTAB => LoadCommand{
1872 .symtab = try stream.reader().readStruct(symtab_command),
1873 },
1874 .DYSYMTAB => LoadCommand{
1875 .dysymtab = try stream.reader().readStruct(dysymtab_command),
1876 },
1877 .ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
1878 .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
1879 },
1880 .ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
1881 .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
1882 },
1883 .MAIN => LoadCommand{
1884 .main = try stream.reader().readStruct(entry_point_command),
1885 },
1886 .VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
1887 .version_min = try stream.reader().readStruct(version_min_command),
1888 },
1889 .SOURCE_VERSION => LoadCommand{
1890 .source_version = try stream.reader().readStruct(source_version_command),
1891 },
1892 .BUILD_VERSION => LoadCommand{
1893 .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
1894 },
1895 .UUID => LoadCommand{
1896 .uuid = try stream.reader().readStruct(uuid_command),
1897 },
1898 .FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
1899 .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
1900 },
1901 .RPATH => LoadCommand{
1902 .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
1903 },
1904 else => LoadCommand{
1905 .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
1906 },
1907 };
1908 }
1909
1910 pub fn write(self: LoadCommand, writer: anytype) !void {
1911 return switch (self) {
1912 .dyld_info_only => |x| writeStruct(x, writer),
1913 .symtab => |x| writeStruct(x, writer),
1914 .dysymtab => |x| writeStruct(x, writer),
1915 .main => |x| writeStruct(x, writer),
1916 .version_min => |x| writeStruct(x, writer),
1917 .source_version => |x| writeStruct(x, writer),
1918 .uuid => |x| writeStruct(x, writer),
1919 .linkedit_data => |x| writeStruct(x, writer),
1920 .segment => |x| x.write(writer),
1921 .dylinker => |x| x.write(writer),
1922 .dylib => |x| x.write(writer),
1923 .rpath => |x| x.write(writer),
1924 .build_version => |x| x.write(writer),
1925 .unknown => |x| x.write(writer),
1926 };
1927 }
1928
1929 pub fn cmd(self: LoadCommand) LC {
1930 return switch (self) {
1931 .dyld_info_only => |x| x.cmd,
1932 .symtab => |x| x.cmd,
1933 .dysymtab => |x| x.cmd,
1934 .main => |x| x.cmd,
1935 .version_min => |x| x.cmd,
1936 .source_version => |x| x.cmd,
1937 .uuid => |x| x.cmd,
1938 .linkedit_data => |x| x.cmd,
1939 .segment => |x| x.inner.cmd,
1940 .dylinker => |x| x.inner.cmd,
1941 .dylib => |x| x.inner.cmd,
1942 .rpath => |x| x.inner.cmd,
1943 .build_version => |x| x.inner.cmd,
1944 .unknown => |x| x.inner.cmd,
1945 };
1946 }
1838pub const LoadCommandIterator = struct {
1839 ncmds: usize,
1840 buffer: []align(@alignOf(u64)) const u8,
1841 index: usize = 0,
19471842
1948 pub fn cmdsize(self: LoadCommand) u32 {
1949 return switch (self) {
1950 .dyld_info_only => |x| x.cmdsize,
1951 .symtab => |x| x.cmdsize,
1952 .dysymtab => |x| x.cmdsize,
1953 .main => |x| x.cmdsize,
1954 .version_min => |x| x.cmdsize,
1955 .source_version => |x| x.cmdsize,
1956 .linkedit_data => |x| x.cmdsize,
1957 .uuid => |x| x.cmdsize,
1958 .segment => |x| x.inner.cmdsize,
1959 .dylinker => |x| x.inner.cmdsize,
1960 .dylib => |x| x.inner.cmdsize,
1961 .rpath => |x| x.inner.cmdsize,
1962 .build_version => |x| x.inner.cmdsize,
1963 .unknown => |x| x.inner.cmdsize,
1964 };
1965 }
1966
1967 pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
1968 return switch (self.*) {
1969 .segment => |*x| x.deinit(allocator),
1970 .dylinker => |*x| x.deinit(allocator),
1971 .dylib => |*x| x.deinit(allocator),
1972 .rpath => |*x| x.deinit(allocator),
1973 .build_version => |*x| x.deinit(allocator),
1974 .unknown => |*x| x.deinit(allocator),
1975 else => {},
1976 };
1977 }
1978
1979 fn writeStruct(command: anytype, writer: anytype) !void {
1980 return writer.writeAll(mem.asBytes(&command));
1981 }
1843 pub const LoadCommand = struct {
1844 hdr: load_command,
1845 data: []const u8,
19821846
1983 pub fn eql(self: LoadCommand, other: LoadCommand) bool {
1984 if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
1985 return switch (self) {
1986 .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
1987 .symtab => |x| meta.eql(x, other.symtab),
1988 .dysymtab => |x| meta.eql(x, other.dysymtab),
1989 .main => |x| meta.eql(x, other.main),
1990 .version_min => |x| meta.eql(x, other.version_min),
1991 .source_version => |x| meta.eql(x, other.source_version),
1992 .build_version => |x| x.eql(other.build_version),
1993 .uuid => |x| meta.eql(x, other.uuid),
1994 .linkedit_data => |x| meta.eql(x, other.linkedit_data),
1995 .segment => |x| x.eql(other.segment),
1996 .dylinker => |x| x.eql(other.dylinker),
1997 .dylib => |x| x.eql(other.dylib),
1998 .rpath => |x| x.eql(other.rpath),
1999 .unknown => |x| x.eql(other.unknown),
2000 };
2001 }
2002};
2003
2004/// A Zig wrapper for segment_command_64.
2005/// Encloses the extern struct together with a list of sections for this segment.
2006pub const SegmentCommand = struct {
2007 inner: segment_command_64,
2008 sections: std.ArrayListUnmanaged(section_64) = .{},
2009
2010 pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
2011 const inner = try reader.readStruct(segment_command_64);
2012 var segment = SegmentCommand{
2013 .inner = inner,
2014 };
2015 try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
2016
2017 var i: usize = 0;
2018 while (i < inner.nsects) : (i += 1) {
2019 const sect = try reader.readStruct(section_64);
2020 segment.sections.appendAssumeCapacity(sect);
1847 pub fn cmd(lc: LoadCommand) LC {
1848 return lc.hdr.cmd;
20211849 }
20221850
2023 return segment;
2024 }
2025
2026 pub fn write(self: SegmentCommand, writer: anytype) !void {
2027 try writer.writeAll(mem.asBytes(&self.inner));
2028 for (self.sections.items) |sect| {
2029 try writer.writeAll(mem.asBytes(&sect));
2030 }
2031 }
2032
2033 pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
2034 self.sections.deinit(allocator);
2035 }
2036
2037 pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
2038 if (!meta.eql(self.inner, other.inner)) return false;
2039 const lhs = self.sections.items;
2040 const rhs = other.sections.items;
2041 var i: usize = 0;
2042 while (i < self.inner.nsects) : (i += 1) {
2043 if (!meta.eql(lhs[i], rhs[i])) return false;
1851 pub fn cmdsize(lc: LoadCommand) u32 {
1852 return lc.hdr.cmdsize;
20441853 }
2045 return true;
2046 }
2047};
2048
2049pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
2050 return .{ .inner = cmd };
2051}
20521854
2053/// A Zig wrapper for a generic load command with variable-length data.
2054pub fn GenericCommandWithData(comptime Cmd: type) type {
2055 return struct {
2056 inner: Cmd,
2057 /// This field remains undefined until `read` is called.
2058 data: []u8 = undefined,
2059
2060 const Self = @This();
2061
2062 pub fn read(allocator: Allocator, reader: anytype) !Self {
2063 const inner = try reader.readStruct(Cmd);
2064 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
2065 errdefer allocator.free(data);
2066 try reader.readNoEof(data);
2067 return Self{
2068 .inner = inner,
2069 .data = data,
2070 };
1855 pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
1856 if (lc.data.len < @sizeOf(Cmd)) return null;
1857 return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
20711858 }
20721859
2073 pub fn write(self: Self, writer: anytype) !void {
2074 try writer.writeAll(mem.asBytes(&self.inner));
2075 try writer.writeAll(self.data);
1860 /// Asserts LoadCommand is of type segment_command_64.
1861 pub fn getSections(lc: LoadCommand) []const section_64 {
1862 const segment_lc = lc.cast(segment_command_64).?;
1863 if (segment_lc.nsects == 0) return &[0]section_64{};
1864 const data = lc.data[@sizeOf(segment_command_64)..];
1865 const sections = @ptrCast(
1866 [*]const section_64,
1867 @alignCast(@alignOf(section_64), &data[0]),
1868 )[0..segment_lc.nsects];
1869 return sections;
20761870 }
20771871
2078 pub fn deinit(self: *Self, allocator: Allocator) void {
2079 allocator.free(self.data);
1872 /// Asserts LoadCommand is of type dylib_command.
1873 pub fn getDylibPathName(lc: LoadCommand) []const u8 {
1874 const dylib_lc = lc.cast(dylib_command).?;
1875 const data = lc.data[dylib_lc.dylib.name..];
1876 return mem.sliceTo(data, 0);
20801877 }
20811878
2082 pub fn eql(self: Self, other: Self) bool {
2083 if (!meta.eql(self.inner, other.inner)) return false;
2084 return mem.eql(u8, self.data, other.data);
1879 /// Asserts LoadCommand is of type rpath_command.
1880 pub fn getRpathPathName(lc: LoadCommand) []const u8 {
1881 const rpath_lc = lc.cast(rpath_command).?;
1882 const data = lc.data[rpath_lc.path..];
1883 return mem.sliceTo(data, 0);
20851884 }
20861885 };
2087}
20881886
2089pub fn createLoadDylibCommand(
2090 allocator: Allocator,
2091 cmd_id: LC,
2092 name: []const u8,
2093 timestamp: u32,
2094 current_version: u32,
2095 compatibility_version: u32,
2096) !GenericCommandWithData(dylib_command) {
2097 assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
2098 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2099 u64,
2100 @sizeOf(dylib_command) + name.len + 1, // +1 for nul
2101 @sizeOf(u64),
2102 ));
2103
2104 var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2105 .cmd = cmd_id,
2106 .cmdsize = cmdsize,
2107 .dylib = .{
2108 .name = @sizeOf(dylib_command),
2109 .timestamp = timestamp,
2110 .current_version = current_version,
2111 .compatibility_version = compatibility_version,
2112 },
2113 });
2114 dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2115
2116 mem.set(u8, dylib_cmd.data, 0);
2117 mem.copy(u8, dylib_cmd.data, name);
2118
2119 return dylib_cmd;
2120}
2121
2122fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
2123 var stream = io.fixedBufferStream(buffer);
2124 var given = try LoadCommand.read(allocator, stream.reader());
2125 defer given.deinit(allocator);
2126 try testing.expect(expected.eql(given));
2127}
2128
2129fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
2130 var stream = io.fixedBufferStream(buffer);
2131 try cmd.write(stream.writer());
2132 try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
2133}
2134
2135fn makeStaticString(bytes: []const u8) [16]u8 {
2136 var buf = [_]u8{0} ** 16;
2137 assert(bytes.len <= buf.len);
2138 mem.copy(u8, &buf, bytes);
2139 return buf;
2140}
1887 pub fn next(it: *LoadCommandIterator) ?LoadCommand {
1888 if (it.index >= it.ncmds) return null;
21411889
2142test "read-write segment command" {
2143 // TODO compiling for macOS from big-endian arch
2144 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2145
2146 var gpa = testing.allocator;
2147 const in_buffer = &[_]u8{
2148 0x19, 0x00, 0x00, 0x00, // cmd
2149 0x98, 0x00, 0x00, 0x00, // cmdsize
2150 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2151 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
2152 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
2153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
2154 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
2155 0x07, 0x00, 0x00, 0x00, // maxprot
2156 0x05, 0x00, 0x00, 0x00, // initprot
2157 0x01, 0x00, 0x00, 0x00, // nsects
2158 0x00, 0x00, 0x00, 0x00, // flags
2159 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
2160 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2161 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
2162 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
2163 0x00, 0x40, 0x00, 0x00, // offset
2164 0x02, 0x00, 0x00, 0x00, // alignment
2165 0x00, 0x00, 0x00, 0x00, // reloff
2166 0x00, 0x00, 0x00, 0x00, // nreloc
2167 0x00, 0x04, 0x00, 0x80, // flags
2168 0x00, 0x00, 0x00, 0x00, // reserved1
2169 0x00, 0x00, 0x00, 0x00, // reserved2
2170 0x00, 0x00, 0x00, 0x00, // reserved3
2171 };
2172 var cmd = SegmentCommand{
2173 .inner = .{
2174 .cmdsize = 152,
2175 .segname = makeStaticString("__TEXT"),
2176 .vmaddr = 4294967296,
2177 .vmsize = 294912,
2178 .filesize = 294912,
2179 .maxprot = PROT.READ | PROT.WRITE | PROT.EXEC,
2180 .initprot = PROT.EXEC | PROT.READ,
2181 .nsects = 1,
2182 },
2183 };
2184 try cmd.sections.append(gpa, .{
2185 .sectname = makeStaticString("__text"),
2186 .segname = makeStaticString("__TEXT"),
2187 .addr = 4294983680,
2188 .size = 448,
2189 .offset = 16384,
2190 .@"align" = 2,
2191 .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
2192 });
2193 defer cmd.deinit(gpa);
2194 try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
2195
2196 var out_buffer: [in_buffer.len]u8 = undefined;
2197 try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
2198}
2199
2200test "read-write generic command with data" {
2201 // TODO compiling for macOS from big-endian arch
2202 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2203
2204 var gpa = testing.allocator;
2205 const in_buffer = &[_]u8{
2206 0x0c, 0x00, 0x00, 0x00, // cmd
2207 0x20, 0x00, 0x00, 0x00, // cmdsize
2208 0x18, 0x00, 0x00, 0x00, // name
2209 0x02, 0x00, 0x00, 0x00, // timestamp
2210 0x00, 0x00, 0x00, 0x00, // current_version
2211 0x00, 0x00, 0x00, 0x00, // compatibility_version
2212 0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
2213 };
2214 var cmd = GenericCommandWithData(dylib_command){
2215 .inner = .{
2216 .cmd = .LOAD_DYLIB,
2217 .cmdsize = 32,
2218 .dylib = .{
2219 .name = 24,
2220 .timestamp = 2,
2221 .current_version = 0,
2222 .compatibility_version = 0,
2223 },
2224 },
2225 };
2226 cmd.data = try gpa.alloc(u8, 8);
2227 defer gpa.free(cmd.data);
2228 cmd.data[0] = 0x2f;
2229 cmd.data[1] = 0x75;
2230 cmd.data[2] = 0x73;
2231 cmd.data[3] = 0x72;
2232 cmd.data[4] = 0x0;
2233 cmd.data[5] = 0x0;
2234 cmd.data[6] = 0x0;
2235 cmd.data[7] = 0x0;
2236 try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
2237
2238 var out_buffer: [in_buffer.len]u8 = undefined;
2239 try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
2240}
2241
2242test "read-write C struct command" {
2243 // TODO compiling for macOS from big-endian arch
2244 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
1890 const hdr = @ptrCast(
1891 *const load_command,
1892 @alignCast(@alignOf(load_command), &it.buffer[0]),
1893 ).*;
1894 const cmd = LoadCommand{
1895 .hdr = hdr,
1896 .data = it.buffer[0..hdr.cmdsize],
1897 };
22451898
2246 var gpa = testing.allocator;
2247 const in_buffer = &[_]u8{
2248 0x28, 0x00, 0x00, 0x80, // cmd
2249 0x18, 0x00, 0x00, 0x00, // cmdsize
2250 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
2251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
2252 };
2253 const cmd = .{
2254 .cmd = .MAIN,
2255 .cmdsize = 24,
2256 .entryoff = 16644,
2257 .stacksize = 0,
2258 };
2259 try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
1899 it.buffer = it.buffer[hdr.cmdsize..];
1900 it.index += 1;
22601901
2261 var out_buffer: [in_buffer.len]u8 = undefined;
2262 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
2263}
1902 return cmd;
1903 }
1904};
src/link/Dwarf.zig+18-26
......@@ -853,8 +853,7 @@ pub fn commitDeclState(
853853 .macho => {
854854 const macho_file = file.cast(File.MachO).?;
855855 const d_sym = &macho_file.d_sym.?;
856 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
857 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
856 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
858857 const file_pos = debug_line_sect.offset + src_fn.off;
859858 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
860859 },
......@@ -933,8 +932,8 @@ pub fn commitDeclState(
933932 .macho => {
934933 const macho_file = file.cast(File.MachO).?;
935934 const d_sym = &macho_file.d_sym.?;
936 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
937 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
935 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
936 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
938937 if (needed_size != debug_line_sect.size) {
939938 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
940939 const new_offset = d_sym.findFreeSpace(needed_size, 1);
......@@ -955,10 +954,9 @@ pub fn commitDeclState(
955954 );
956955
957956 debug_line_sect.offset = @intCast(u32, new_offset);
958 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
957 debug_line_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
959958 }
960959 debug_line_sect.size = needed_size;
961 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
962960 d_sym.debug_line_header_dirty = true;
963961 }
964962 const file_pos = debug_line_sect.offset + src_fn.off;
......@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
11371135 .macho => {
11381136 const macho_file = file.cast(File.MachO).?;
11391137 const d_sym = &macho_file.d_sym.?;
1140 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1141 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1138 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
11421139 const file_pos = debug_info_sect.offset + atom.off;
11431140 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
11441141 },
......@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12351232 .macho => {
12361233 const macho_file = file.cast(File.MachO).?;
12371234 const d_sym = &macho_file.d_sym.?;
1238 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1239 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1235 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1236 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
12401237 if (needed_size != debug_info_sect.size) {
12411238 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
12421239 const new_offset = d_sym.findFreeSpace(needed_size, 1);
......@@ -1257,10 +1254,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12571254 );
12581255
12591256 debug_info_sect.offset = @intCast(u32, new_offset);
1260 debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1257 debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
12611258 }
12621259 debug_info_sect.size = needed_size;
1263 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
12641260 d_sym.debug_line_header_dirty = true;
12651261 }
12661262 const file_pos = debug_info_sect.offset + atom.off;
......@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
13301326 .macho => {
13311327 const macho_file = file.cast(File.MachO).?;
13321328 const d_sym = macho_file.d_sym.?;
1333 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1334 const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
1329 const sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
13351330 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();
13361331 try d_sym.file.pwriteAll(&data, file_pos);
13371332 },
......@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
15571552 .macho => {
15581553 const macho_file = file.cast(File.MachO).?;
15591554 const d_sym = &macho_file.d_sym.?;
1560 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1561 const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?];
1555 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1556 const debug_abbrev_sect = &d_sym.sections.items[d_sym.debug_abbrev_section_index.?];
15621557 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);
15631558 if (needed_size > allocated_size) {
15641559 debug_abbrev_sect.size = 0; // free the space
15651560 const offset = d_sym.findFreeSpace(needed_size, 1);
15661561 debug_abbrev_sect.offset = @intCast(u32, offset);
1567 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
1562 debug_abbrev_sect.addr = dwarf_segment.vmaddr + offset - dwarf_segment.fileoff;
15681563 }
15691564 debug_abbrev_sect.size = needed_size;
15701565 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
......@@ -1681,8 +1676,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
16811676 .macho => {
16821677 const macho_file = file.cast(File.MachO).?;
16831678 const d_sym = &macho_file.d_sym.?;
1684 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1685 const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?];
1679 const debug_info_sect = d_sym.sections.items[d_sym.debug_info_section_index.?];
16861680 const file_pos = debug_info_sect.offset;
16871681 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
16881682 },
......@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
19981992 .macho => {
19991993 const macho_file = file.cast(File.MachO).?;
20001994 const d_sym = &macho_file.d_sym.?;
2001 const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2002 const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?];
1995 const dwarf_seg = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1996 const debug_aranges_sect = &d_sym.sections.items[d_sym.debug_aranges_section_index.?];
20031997 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);
20041998 if (needed_size > allocated_size) {
20051999 debug_aranges_sect.size = 0; // free the space
20062000 const new_offset = d_sym.findFreeSpace(needed_size, 16);
2007 debug_aranges_sect.addr = dwarf_seg.inner.vmaddr + new_offset - dwarf_seg.inner.fileoff;
2001 debug_aranges_sect.addr = dwarf_seg.vmaddr + new_offset - dwarf_seg.fileoff;
20082002 debug_aranges_sect.offset = @intCast(u32, new_offset);
20092003 }
20102004 debug_aranges_sect.size = needed_size;
......@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
21342128 .macho => {
21352129 const macho_file = file.cast(File.MachO).?;
21362130 const d_sym = &macho_file.d_sym.?;
2137 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2138 const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
2131 const debug_line_sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
21392132 const file_pos = debug_line_sect.offset;
21402133 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
21412134 },
......@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
22642257 .macho => {
22652258 const macho_file = file.cast(File.MachO).?;
22662259 const d_sym = &macho_file.d_sym.?;
2267 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2268 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
2260 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
22692261 break :blk debug_info_sect.offset;
22702262 },
22712263 // for wasm, the offset is always 0 as we write to memory first
src/link/MachO.zig+1563-1912
......@@ -17,6 +17,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig");
1717const bind = @import("MachO/bind.zig");
1818const codegen = @import("../codegen.zig");
1919const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");
2021const link = @import("../link.zig");
2122const llvm_backend = @import("../codegen/llvm.zig");
2223const target_util = @import("../target.zig");
......@@ -60,6 +61,29 @@ const SystemLib = struct {
6061 weak: bool = false,
6162};
6263
64const Section = struct {
65 header: macho.section_64,
66 segment_index: u8,
67 last_atom: ?*Atom = null, // TODO temporary hack; we really should shrink section to 0
68
69 /// A list of atoms that have surplus capacity. This list can have false
70 /// positives, as functions grow and shrink over time, only sometimes being added
71 /// or removed from the freelist.
72 ///
73 /// An atom has surplus capacity when its overcapacity value is greater than
74 /// padToIdeal(minimum_atom_size). That is, when it has so
75 /// much extra capacity, that we could fit a small new symbol in it, itself with
76 /// ideal_capacity or more.
77 ///
78 /// Ideal capacity is defined by size + (size / ideal_factor).
79 ///
80 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
81 /// overcapacity can be negative. A simple way to have negative overcapacity is to
82 /// allocate a fresh atom, which will have ideal capacity, and then grow it
83 /// by 1 byte. It will then have -1 overcapacity.
84 free_list: std.ArrayListUnmanaged(*Atom) = .{},
85};
86
6387base: File,
6488
6589/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -77,80 +101,67 @@ page_size: u16,
77101/// fashion (default for LLVM backend).
78102mode: enum { incremental, one_shot },
79103
80/// The absolute address of the entry point.
81entry_addr: ?u64 = null,
82
83/// Code signature (if any)
84code_signature: ?CodeSignature = null,
104uuid: macho.uuid_command = .{
105 .cmdsize = @sizeOf(macho.uuid_command),
106 .uuid = undefined,
107},
85108
86109objects: std.ArrayListUnmanaged(Object) = .{},
87110archives: std.ArrayListUnmanaged(Archive) = .{},
88
89111dylibs: std.ArrayListUnmanaged(Dylib) = .{},
90112dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
91113referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
92114
93load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
94
95pagezero_segment_cmd_index: ?u16 = null,
96text_segment_cmd_index: ?u16 = null,
97data_const_segment_cmd_index: ?u16 = null,
98data_segment_cmd_index: ?u16 = null,
99linkedit_segment_cmd_index: ?u16 = null,
100dyld_info_cmd_index: ?u16 = null,
101symtab_cmd_index: ?u16 = null,
102dysymtab_cmd_index: ?u16 = null,
103dylinker_cmd_index: ?u16 = null,
104data_in_code_cmd_index: ?u16 = null,
105function_starts_cmd_index: ?u16 = null,
106main_cmd_index: ?u16 = null,
107dylib_id_cmd_index: ?u16 = null,
108source_version_cmd_index: ?u16 = null,
109build_version_cmd_index: ?u16 = null,
110uuid_cmd_index: ?u16 = null,
111code_signature_cmd_index: ?u16 = null,
115segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
116sections: std.MultiArrayList(Section) = .{},
117
118pagezero_segment_cmd_index: ?u8 = null,
119text_segment_cmd_index: ?u8 = null,
120data_const_segment_cmd_index: ?u8 = null,
121data_segment_cmd_index: ?u8 = null,
122linkedit_segment_cmd_index: ?u8 = null,
112123
113124// __TEXT segment sections
114text_section_index: ?u16 = null,
115stubs_section_index: ?u16 = null,
116stub_helper_section_index: ?u16 = null,
117text_const_section_index: ?u16 = null,
118cstring_section_index: ?u16 = null,
119ustring_section_index: ?u16 = null,
120gcc_except_tab_section_index: ?u16 = null,
121unwind_info_section_index: ?u16 = null,
122eh_frame_section_index: ?u16 = null,
123
124objc_methlist_section_index: ?u16 = null,
125objc_methname_section_index: ?u16 = null,
126objc_methtype_section_index: ?u16 = null,
127objc_classname_section_index: ?u16 = null,
125text_section_index: ?u8 = null,
126stubs_section_index: ?u8 = null,
127stub_helper_section_index: ?u8 = null,
128text_const_section_index: ?u8 = null,
129cstring_section_index: ?u8 = null,
130ustring_section_index: ?u8 = null,
131gcc_except_tab_section_index: ?u8 = null,
132unwind_info_section_index: ?u8 = null,
133eh_frame_section_index: ?u8 = null,
134
135objc_methlist_section_index: ?u8 = null,
136objc_methname_section_index: ?u8 = null,
137objc_methtype_section_index: ?u8 = null,
138objc_classname_section_index: ?u8 = null,
128139
129140// __DATA_CONST segment sections
130got_section_index: ?u16 = null,
131mod_init_func_section_index: ?u16 = null,
132mod_term_func_section_index: ?u16 = null,
133data_const_section_index: ?u16 = null,
141got_section_index: ?u8 = null,
142mod_init_func_section_index: ?u8 = null,
143mod_term_func_section_index: ?u8 = null,
144data_const_section_index: ?u8 = null,
134145
135objc_cfstring_section_index: ?u16 = null,
136objc_classlist_section_index: ?u16 = null,
137objc_imageinfo_section_index: ?u16 = null,
146objc_cfstring_section_index: ?u8 = null,
147objc_classlist_section_index: ?u8 = null,
148objc_imageinfo_section_index: ?u8 = null,
138149
139150// __DATA segment sections
140tlv_section_index: ?u16 = null,
141tlv_data_section_index: ?u16 = null,
142tlv_bss_section_index: ?u16 = null,
143tlv_ptrs_section_index: ?u16 = null,
144la_symbol_ptr_section_index: ?u16 = null,
145data_section_index: ?u16 = null,
146bss_section_index: ?u16 = null,
147
148objc_const_section_index: ?u16 = null,
149objc_selrefs_section_index: ?u16 = null,
150objc_classrefs_section_index: ?u16 = null,
151objc_data_section_index: ?u16 = null,
152
153rustc_section_index: ?u16 = null,
151tlv_section_index: ?u8 = null,
152tlv_data_section_index: ?u8 = null,
153tlv_bss_section_index: ?u8 = null,
154tlv_ptrs_section_index: ?u8 = null,
155la_symbol_ptr_section_index: ?u8 = null,
156data_section_index: ?u8 = null,
157bss_section_index: ?u8 = null,
158
159objc_const_section_index: ?u8 = null,
160objc_selrefs_section_index: ?u8 = null,
161objc_classrefs_section_index: ?u8 = null,
162objc_data_section_index: ?u8 = null,
163
164rustc_section_index: ?u8 = null,
154165rustc_section_size: u64 = 0,
155166
156167locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
......@@ -188,37 +199,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188199
189200error_flags: File.ErrorFlags = File.ErrorFlags{},
190201
191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,
193
194202/// A helper var to indicate if we are at the start of the incremental updates, or
195203/// already somewhere further along the update-and-run chain.
196204/// TODO once we add opening a prelinked output binary from file, this will become
197205/// obsolete as we will carry on where we left off.
198206cold_start: bool = true,
199207
200section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
201
202/// A list of atoms that have surplus capacity. This list can have false
203/// positives, as functions grow and shrink over time, only sometimes being added
204/// or removed from the freelist.
205///
206/// An atom has surplus capacity when its overcapacity value is greater than
207/// padToIdeal(minimum_atom_size). That is, when it has so
208/// much extra capacity, that we could fit a small new symbol in it, itself with
209/// ideal_capacity or more.
210///
211/// Ideal capacity is defined by size + (size / ideal_factor).
212///
213/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
214/// overcapacity can be negative. A simple way to have negative overcapacity is to
215/// allocate a fresh atom, which will have ideal capacity, and then grow it
216/// by 1 byte. It will then have -1 overcapacity.
217atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanaged(*Atom)) = .{},
218
219/// Pointer to the last allocated atom
220atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
221
222208/// List of atoms that are either synthetic or map directly to the Zig source program.
223209managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224210
......@@ -250,7 +236,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},
250236/// We store them here so that we can properly dispose of any allocated
251237/// memory within the atom in the incremental linker.
252238/// TODO consolidate this.
253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
239decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
254240
255241const Entry = struct {
256242 target: SymbolWithLoc,
......@@ -408,12 +394,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
408394
409395pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
410396 const cpu_arch = options.target.cpu.arch;
411 const os_tag = options.target.os.tag;
412 const abi = options.target.abi;
413397 const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;
414 // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator
415 // ABI such as aarch64-ios-simulator, etc.
416 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
417398 const use_llvm = build_options.have_llvm and options.use_llvm;
418399 const use_stage1 = build_options.is_stage1 and options.use_stage1;
419400
......@@ -428,10 +409,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
428409 .file = null,
429410 },
430411 .page_size = page_size,
431 .code_signature = if (requires_adhoc_codesig)
432 CodeSignature.init(page_size)
433 else
434 null,
435412 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436413 .one_shot
437414 else
......@@ -562,8 +539,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
562539 var dependent_libs = std.fifo.LinearFifo(struct {
563540 id: Dylib.Id,
564541 parent: u16,
565 }, .Dynamic).init(self.base.allocator);
566 defer dependent_libs.deinit();
542 }, .Dynamic).init(arena);
543
567544 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568545 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569546 }
......@@ -573,7 +550,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
573550 try self.createDyldPrivateAtom();
574551 try self.createStubHelperPreambleAtom();
575552 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577553
578554 if (self.unresolved.count() > 0) {
579555 return error.UndefinedSymbolReference;
......@@ -583,67 +559,91 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
583559
584560 if (build_options.enable_logging) {
585561 self.logSymtab();
586 self.logSectionOrdinals();
587562 self.logAtoms();
588563 }
589564
590565 try self.writeAtomsIncremental();
591566
592 try self.setEntryPoint();
593 try self.updateSectionOrdinals();
594 try self.writeLinkeditSegment();
567 var lc_buffer = std.ArrayList(u8).init(arena);
568 const lc_writer = lc_buffer.writer();
569 var ncmds: u32 = 0;
595570
596 if (self.d_sym) |*d_sym| {
597 // Flush debug symbols bundle.
598 try d_sym.flushModule(self.base.allocator, self.base.options);
571 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
572 try writeDylinkerLC(&ncmds, lc_writer);
573
574 self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {
575 error.MissingMainEntrypoint => {
576 self.error_flags.no_entry_point_found = true;
577 },
578 else => |e| return e,
579 };
580
581 try self.writeDylibIdLC(&ncmds, lc_writer);
582 try self.writeRpathLCs(&ncmds, lc_writer);
583
584 {
585 try lc_writer.writeStruct(macho.source_version_command{
586 .cmdsize = @sizeOf(macho.source_version_command),
587 .version = 0x0,
588 });
589 ncmds += 1;
599590 }
600591
601 // code signature and entitlements
602 if (self.base.options.entitlements) |path| {
603 if (self.code_signature) |*csig| {
604 try csig.addEntitlements(self.base.allocator, path);
605 csig.code_directory.ident = self.base.options.emit.?.sub_path;
606 } else {
607 var csig = CodeSignature.init(self.page_size);
608 try csig.addEntitlements(self.base.allocator, path);
609 csig.code_directory.ident = self.base.options.emit.?.sub_path;
610 self.code_signature = csig;
611 }
592 try self.writeBuildVersionLC(&ncmds, lc_writer);
593
594 {
595 std.crypto.random.bytes(&self.uuid.uuid);
596 try lc_writer.writeStruct(self.uuid);
597 ncmds += 1;
612598 }
613599
614 if (self.code_signature) |*csig| {
615 csig.clear(self.base.allocator);
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;
600 try self.writeLoadDylibLCs(&ncmds, lc_writer);
601
602 const target = self.base.options.target;
603 const requires_codesig = blk: {
604 if (self.base.options.entitlements) |_| break :blk true;
605 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
606 break :blk true;
607 break :blk false;
608 };
609 var codesig_offset: ?u32 = null;
610 var codesig: ?CodeSignature = if (requires_codesig) blk: {
617611 // Preallocate space for the code signature.
618612 // We need to do this at this stage so that we have the load commands with proper values
619613 // written out to the file.
620614 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621615 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);
623 }
616 var codesig = CodeSignature.init(self.page_size);
617 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
618 if (self.base.options.entitlements) |path| {
619 try codesig.addEntitlements(arena, path);
620 }
621 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
622 break :blk codesig;
623 } else null;
624624
625 try self.writeLoadCommands();
626 try self.writeHeader();
625 var headers_buf = std.ArrayList(u8).init(arena);
626 try self.writeSegmentHeaders(0, self.segments.items.len, &ncmds, headers_buf.writer());
627627
628 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
629 log.debug("flushing. no_entry_point_found = true", .{});
630 self.error_flags.no_entry_point_found = true;
631 } else {
632 log.debug("flushing. no_entry_point_found = false", .{});
633 self.error_flags.no_entry_point_found = false;
634 }
628 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
629 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
635630
636 assert(!self.load_commands_dirty);
631 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
637632
638 if (self.code_signature) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last
633 if (codesig) |*csig| {
634 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
640635 }
641636
642 if (build_options.enable_link_snapshots) {
643 if (self.base.options.enable_link_snapshots)
644 try self.snapshotState();
637 if (self.d_sym) |*d_sym| {
638 // Flush debug symbols bundle.
639 try d_sym.flushModule(self.base.allocator, self.base.options);
645640 }
646641
642 // if (build_options.enable_link_snapshots) {
643 // if (self.base.options.enable_link_snapshots)
644 // try self.snapshotState();
645 // }
646
647647 if (cache_miss) {
648648 // Update the file with the digest. If it fails we can continue; it only
649649 // means that the next invocation will have an unnecessary cache miss.
......@@ -708,6 +708,9 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
708708 sub_prog_node.context.refresh();
709709 defer sub_prog_node.end();
710710
711 const cpu_arch = self.base.options.target.cpu.arch;
712 const os_tag = self.base.options.target.os.tag;
713 const abi = self.base.options.target.abi;
711714 const is_lib = self.base.options.output_mode == .Lib;
712715 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
713716 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
......@@ -990,40 +993,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
990993 }
991994 }
992995
993 // rpaths
994 var rpath_table = std.StringArrayHashMap(void).init(arena);
995 for (self.base.options.rpath_list) |rpath| {
996 if (rpath_table.contains(rpath)) continue;
997 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
998 u64,
999 @sizeOf(macho.rpath_command) + rpath.len + 1,
1000 @sizeOf(u64),
1001 ));
1002 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
1003 .cmdsize = cmdsize,
1004 .path = @sizeOf(macho.rpath_command),
1005 });
1006 rpath_cmd.data = try gpa.alloc(u8, cmdsize - rpath_cmd.inner.path);
1007 mem.set(u8, rpath_cmd.data, 0);
1008 mem.copy(u8, rpath_cmd.data, rpath);
1009 try self.load_commands.append(gpa, .{ .rpath = rpath_cmd });
1010 try rpath_table.putNoClobber(rpath, {});
1011 self.load_commands_dirty = true;
1012 }
1013
1014 // code signature and entitlements
1015 if (self.base.options.entitlements) |path| {
1016 if (self.code_signature) |*csig| {
1017 try csig.addEntitlements(gpa, path);
1018 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1019 } else {
1020 var csig = CodeSignature.init(self.page_size);
1021 try csig.addEntitlements(gpa, path);
1022 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1023 self.code_signature = csig;
1024 }
1025 }
1026
1027996 if (self.base.options.verbose_link) {
1028997 var argv = std.ArrayList([]const u8).init(arena);
1029998
......@@ -1048,7 +1017,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
10481017 try argv.append(syslibroot);
10491018 }
10501019
1051 for (rpath_table.keys()) |rpath| {
1020 for (self.base.options.rpath_list) |rpath| {
10521021 try argv.append("-rpath");
10531022 try argv.append(rpath);
10541023 }
......@@ -1157,15 +1126,15 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11571126 var dependent_libs = std.fifo.LinearFifo(struct {
11581127 id: Dylib.Id,
11591128 parent: u16,
1160 }, .Dynamic).init(gpa);
1161 defer dependent_libs.deinit();
1129 }, .Dynamic).init(arena);
1130
11621131 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
11631132 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
11641133 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
11651134 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
11661135
1167 for (self.objects.items) |*object, object_id| {
1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1136 for (self.objects.items) |_, object_id| {
1137 try self.resolveSymbolsInObject(@intCast(u16, object_id));
11691138 }
11701139
11711140 try self.resolveSymbolsInArchives();
......@@ -1175,7 +1144,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11751144 try self.resolveSymbolsInDylibs();
11761145 try self.createMhExecuteHeaderSymbol();
11771146 try self.createDsoHandleSymbol();
1178 try self.addCodeSignatureLC();
11791147 try self.resolveSymbolsAtLoading();
11801148
11811149 if (self.unresolved.count() > 0) {
......@@ -1206,41 +1174,79 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
12061174
12071175 if (build_options.enable_logging) {
12081176 self.logSymtab();
1209 self.logSectionOrdinals();
12101177 self.logAtoms();
12111178 }
12121179
12131180 try self.writeAtomsOneShot();
12141181
12151182 if (self.rustc_section_index) |id| {
1216 const sect = self.getSectionPtr(.{
1217 .seg = self.data_segment_cmd_index.?,
1218 .sect = id,
1183 const header = &self.sections.items(.header)[id];
1184 header.size = self.rustc_section_size;
1185 }
1186
1187 var lc_buffer = std.ArrayList(u8).init(arena);
1188 const lc_writer = lc_buffer.writer();
1189 var ncmds: u32 = 0;
1190
1191 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1192 try writeDylinkerLC(&ncmds, lc_writer);
1193 try self.writeMainLC(&ncmds, lc_writer);
1194 try self.writeDylibIdLC(&ncmds, lc_writer);
1195 try self.writeRpathLCs(&ncmds, lc_writer);
1196
1197 {
1198 try lc_writer.writeStruct(macho.source_version_command{
1199 .cmdsize = @sizeOf(macho.source_version_command),
1200 .version = 0x0,
12191201 });
1220 sect.size = self.rustc_section_size;
1202 ncmds += 1;
1203 }
1204
1205 try self.writeBuildVersionLC(&ncmds, lc_writer);
1206
1207 {
1208 var uuid_lc = macho.uuid_command{
1209 .cmdsize = @sizeOf(macho.uuid_command),
1210 .uuid = undefined,
1211 };
1212 std.crypto.random.bytes(&uuid_lc.uuid);
1213 try lc_writer.writeStruct(uuid_lc);
1214 ncmds += 1;
12211215 }
12221216
1223 try self.setEntryPoint();
1224 try self.writeLinkeditSegment();
1217 try self.writeLoadDylibLCs(&ncmds, lc_writer);
12251218
1226 if (self.code_signature) |*csig| {
1227 csig.clear(gpa);
1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1219 const requires_codesig = blk: {
1220 if (self.base.options.entitlements) |_| break :blk true;
1221 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
1222 break :blk false;
1223 };
1224 var codesig_offset: ?u32 = null;
1225 var codesig: ?CodeSignature = if (requires_codesig) blk: {
12291226 // Preallocate space for the code signature.
12301227 // We need to do this at this stage so that we have the load commands with proper values
12311228 // written out to the file.
12321229 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
12331230 // where the code signature goes into.
1234 try self.writeCodeSignaturePadding(csig);
1235 }
1231 var codesig = CodeSignature.init(self.page_size);
1232 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
1233 if (self.base.options.entitlements) |path| {
1234 try codesig.addEntitlements(arena, path);
1235 }
1236 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
1237 break :blk codesig;
1238 } else null;
12361239
1237 try self.writeLoadCommands();
1238 try self.writeHeader();
1240 var headers_buf = std.ArrayList(u8).init(arena);
1241 try self.writeSegmentHeaders(0, self.segments.items.len, &ncmds, headers_buf.writer());
12391242
1240 assert(!self.load_commands_dirty);
1243 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1244 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
12411245
1242 if (self.code_signature) |*csig| {
1243 try self.writeCodeSignature(csig); // code signing always comes last
1246 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
1247
1248 if (codesig) |*csig| {
1249 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
12441250 }
12451251 }
12461252
......@@ -1395,66 +1401,77 @@ fn resolveFramework(
13951401}
13961402
13971403fn parseObject(self: *MachO, path: []const u8) !bool {
1404 const gpa = self.base.allocator;
13981405 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
13991406 error.FileNotFound => return false,
14001407 else => |e| return e,
14011408 };
1402 errdefer file.close();
1403
1404 const name = try self.base.allocator.dupe(u8, path);
1405 errdefer self.base.allocator.free(name);
1409 defer file.close();
14061410
1411 const name = try gpa.dupe(u8, path);
1412 errdefer gpa.free(name);
1413 const cpu_arch = self.base.options.target.cpu.arch;
14071414 const mtime: u64 = mtime: {
14081415 const stat = file.stat() catch break :mtime 0;
14091416 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
14101417 };
1418 const file_stat = try file.stat();
1419 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
1420 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
14111421
14121422 var object = Object{
14131423 .name = name,
1414 .file = file,
14151424 .mtime = mtime,
1425 .contents = contents,
14161426 };
14171427
1418 object.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1428 object.parse(gpa, cpu_arch) catch |err| switch (err) {
14191429 error.EndOfStream, error.NotObject => {
1420 object.deinit(self.base.allocator);
1430 object.deinit(gpa);
14211431 return false;
14221432 },
14231433 else => |e| return e,
14241434 };
14251435
1426 try self.objects.append(self.base.allocator, object);
1436 try self.objects.append(gpa, object);
14271437
14281438 return true;
14291439}
14301440
14311441fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1442 const gpa = self.base.allocator;
14321443 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
14331444 error.FileNotFound => return false,
14341445 else => |e| return e,
14351446 };
14361447 errdefer file.close();
14371448
1438 const name = try self.base.allocator.dupe(u8, path);
1439 errdefer self.base.allocator.free(name);
1449 const name = try gpa.dupe(u8, path);
1450 errdefer gpa.free(name);
1451 const cpu_arch = self.base.options.target.cpu.arch;
1452 const reader = file.reader();
1453 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
1454 try reader.context.seekTo(fat_offset);
14401455
14411456 var archive = Archive{
14421457 .name = name,
1458 .fat_offset = fat_offset,
14431459 .file = file,
14441460 };
14451461
1446 archive.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1462 archive.parse(gpa, reader) catch |err| switch (err) {
14471463 error.EndOfStream, error.NotArchive => {
1448 archive.deinit(self.base.allocator);
1464 archive.deinit(gpa);
14491465 return false;
14501466 },
14511467 else => |e| return e,
14521468 };
14531469
14541470 if (force_load) {
1455 defer archive.deinit(self.base.allocator);
1471 defer archive.deinit(gpa);
1472 defer file.close();
14561473 // Get all offsets from the ToC
1457 var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator);
1474 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
14581475 defer offsets.deinit();
14591476 for (archive.toc.values()) |offs| {
14601477 for (offs.items) |off| {
......@@ -1462,15 +1479,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
14621479 }
14631480 }
14641481 for (offsets.keys()) |off| {
1465 const object = try self.objects.addOne(self.base.allocator);
1466 object.* = try archive.parseObject(
1467 self.base.allocator,
1468 self.base.options.target.cpu.arch,
1469 off,
1470 );
1482 const object = try archive.parseObject(gpa, cpu_arch, off);
1483 try self.objects.append(gpa, object);
14711484 }
14721485 } else {
1473 try self.archives.append(self.base.allocator, archive);
1486 try self.archives.append(gpa, archive);
14741487 }
14751488
14761489 return true;
......@@ -1481,6 +1494,7 @@ const ParseDylibError = error{
14811494 EmptyStubFile,
14821495 MismatchedCpuArchitecture,
14831496 UnsupportedCpuArchitecture,
1497 EndOfStream,
14841498} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
14851499
14861500const DylibCreateOpts = struct {
......@@ -1497,43 +1511,52 @@ pub fn parseDylib(
14971511 dependent_libs: anytype,
14981512 opts: DylibCreateOpts,
14991513) ParseDylibError!bool {
1514 const gpa = self.base.allocator;
15001515 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
15011516 error.FileNotFound => return false,
15021517 else => |e| return e,
15031518 };
1504 errdefer file.close();
1519 defer file.close();
1520
1521 const cpu_arch = self.base.options.target.cpu.arch;
1522 const file_stat = try file.stat();
1523 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
1524
1525 const reader = file.reader();
1526 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
1527 try file.seekTo(fat_offset);
1528 file_size -= fat_offset;
15051529
1506 const name = try self.base.allocator.dupe(u8, path);
1507 errdefer self.base.allocator.free(name);
1530 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
1531 defer gpa.free(contents);
15081532
15091533 const dylib_id = @intCast(u16, self.dylibs.items.len);
1510 var dylib = Dylib{
1511 .name = name,
1512 .file = file,
1513 .weak = opts.weak,
1514 };
1534 var dylib = Dylib{ .weak = opts.weak };
15151535
1516 dylib.parse(
1517 self.base.allocator,
1518 self.base.options.target.cpu.arch,
1536 dylib.parseFromBinary(
1537 gpa,
1538 cpu_arch,
15191539 dylib_id,
15201540 dependent_libs,
1541 path,
1542 contents,
15211543 ) catch |err| switch (err) {
15221544 error.EndOfStream, error.NotDylib => {
15231545 try file.seekTo(0);
15241546
1525 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
1526 dylib.deinit(self.base.allocator);
1547 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
1548 dylib.deinit(gpa);
15271549 return false;
15281550 };
15291551 defer lib_stub.deinit();
15301552
15311553 try dylib.parseFromStub(
1532 self.base.allocator,
1554 gpa,
15331555 self.base.options.target,
15341556 lib_stub,
15351557 dylib_id,
15361558 dependent_libs,
1559 path,
15371560 );
15381561 },
15391562 else => |e| return e,
......@@ -1547,13 +1570,13 @@ pub fn parseDylib(
15471570 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
15481571
15491572 // TODO maybe this should be an error and facilitate auto-cleanup?
1550 dylib.deinit(self.base.allocator);
1573 dylib.deinit(gpa);
15511574 return false;
15521575 }
15531576 }
15541577
1555 try self.dylibs.append(self.base.allocator, dylib);
1556 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
1578 try self.dylibs.append(gpa, dylib);
1579 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
15571580
15581581 const should_link_dylib_even_if_unreachable = blk: {
15591582 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
......@@ -1561,8 +1584,7 @@ pub fn parseDylib(
15611584 };
15621585
15631586 if (should_link_dylib_even_if_unreachable) {
1564 try self.addLoadDylibLC(dylib_id);
1565 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1587 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
15661588 }
15671589
15681590 return true;
......@@ -1572,10 +1594,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
15721594 for (files) |file_name| {
15731595 const full_path = full_path: {
15741596 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1575 const path = try fs.realpath(file_name, &buffer);
1576 break :full_path try self.base.allocator.dupe(u8, path);
1597 break :full_path try fs.realpath(file_name, &buffer);
15771598 };
1578 defer self.base.allocator.free(full_path);
15791599 log.debug("parsing input file path '{s}'", .{full_path});
15801600
15811601 if (try self.parseObject(full_path)) continue;
......@@ -1592,10 +1612,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
15921612 for (files) |file_name| {
15931613 const full_path = full_path: {
15941614 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1595 const path = try fs.realpath(file_name, &buffer);
1596 break :full_path try self.base.allocator.dupe(u8, path);
1615 break :full_path try fs.realpath(file_name, &buffer);
15971616 };
1598 defer self.base.allocator.free(full_path);
15991617 log.debug("parsing and force loading static archive '{s}'", .{full_path});
16001618
16011619 if (try self.parseArchive(full_path, true)) continue;
......@@ -1669,24 +1687,10 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
16691687 }
16701688}
16711689
1672pub const MatchingSection = struct {
1673 seg: u16,
1674 sect: u16,
1675
1676 pub fn eql(this: MatchingSection, other: struct {
1677 seg: ?u16,
1678 sect: ?u16,
1679 }) bool {
1680 const seg = other.seg orelse return false;
1681 const sect = other.sect orelse return false;
1682 return this.seg == seg and this.sect == sect;
1683 }
1684};
1685
1686pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
1690pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
16871691 const segname = sect.segName();
16881692 const sectname = sect.sectName();
1689 const res: ?MatchingSection = blk: {
1693 const res: ?u8 = blk: {
16901694 switch (sect.type_()) {
16911695 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
16921696 if (self.text_const_section_index == null) {
......@@ -1698,11 +1702,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
16981702 .{},
16991703 );
17001704 }
1701
1702 break :blk .{
1703 .seg = self.text_segment_cmd_index.?,
1704 .sect = self.text_const_section_index.?,
1705 };
1705 break :blk self.text_const_section_index.?;
17061706 },
17071707 macho.S_CSTRING_LITERALS => {
17081708 if (mem.eql(u8, sectname, "__objc_methname")) {
......@@ -1717,11 +1717,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
17171717 .{},
17181718 );
17191719 }
1720
1721 break :blk .{
1722 .seg = self.text_segment_cmd_index.?,
1723 .sect = self.objc_methname_section_index.?,
1724 };
1720 break :blk self.objc_methname_section_index.?;
17251721 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
17261722 if (self.objc_methtype_section_index == null) {
17271723 self.objc_methtype_section_index = try self.initSection(
......@@ -1732,11 +1728,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
17321728 .{},
17331729 );
17341730 }
1735
1736 break :blk .{
1737 .seg = self.text_segment_cmd_index.?,
1738 .sect = self.objc_methtype_section_index.?,
1739 };
1731 break :blk self.objc_methtype_section_index.?;
17401732 } else if (mem.eql(u8, sectname, "__objc_classname")) {
17411733 if (self.objc_classname_section_index == null) {
17421734 self.objc_classname_section_index = try self.initSection(
......@@ -1747,11 +1739,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
17471739 .{},
17481740 );
17491741 }
1750
1751 break :blk .{
1752 .seg = self.text_segment_cmd_index.?,
1753 .sect = self.objc_classname_section_index.?,
1754 };
1742 break :blk self.objc_classname_section_index.?;
17551743 }
17561744
17571745 if (self.cstring_section_index == null) {
......@@ -1765,11 +1753,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
17651753 },
17661754 );
17671755 }
1768
1769 break :blk .{
1770 .seg = self.text_segment_cmd_index.?,
1771 .sect = self.cstring_section_index.?,
1772 };
1756 break :blk self.cstring_section_index.?;
17731757 },
17741758 macho.S_LITERAL_POINTERS => {
17751759 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
......@@ -1784,11 +1768,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
17841768 },
17851769 );
17861770 }
1787
1788 break :blk .{
1789 .seg = self.data_segment_cmd_index.?,
1790 .sect = self.objc_selrefs_section_index.?,
1791 };
1771 break :blk self.objc_selrefs_section_index.?;
17921772 } else {
17931773 // TODO investigate
17941774 break :blk null;
......@@ -1806,11 +1786,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18061786 },
18071787 );
18081788 }
1809
1810 break :blk .{
1811 .seg = self.data_const_segment_cmd_index.?,
1812 .sect = self.mod_init_func_section_index.?,
1813 };
1789 break :blk self.mod_init_func_section_index.?;
18141790 },
18151791 macho.S_MOD_TERM_FUNC_POINTERS => {
18161792 if (self.mod_term_func_section_index == null) {
......@@ -1824,11 +1800,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18241800 },
18251801 );
18261802 }
1827
1828 break :blk .{
1829 .seg = self.data_const_segment_cmd_index.?,
1830 .sect = self.mod_term_func_section_index.?,
1831 };
1803 break :blk self.mod_term_func_section_index.?;
18321804 },
18331805 macho.S_ZEROFILL => {
18341806 if (self.bss_section_index == null) {
......@@ -1842,11 +1814,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18421814 },
18431815 );
18441816 }
1845
1846 break :blk .{
1847 .seg = self.data_segment_cmd_index.?,
1848 .sect = self.bss_section_index.?,
1849 };
1817 break :blk self.bss_section_index.?;
18501818 },
18511819 macho.S_THREAD_LOCAL_VARIABLES => {
18521820 if (self.tlv_section_index == null) {
......@@ -1860,11 +1828,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18601828 },
18611829 );
18621830 }
1863
1864 break :blk .{
1865 .seg = self.data_segment_cmd_index.?,
1866 .sect = self.tlv_section_index.?,
1867 };
1831 break :blk self.tlv_section_index.?;
18681832 },
18691833 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
18701834 if (self.tlv_ptrs_section_index == null) {
......@@ -1878,11 +1842,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18781842 },
18791843 );
18801844 }
1881
1882 break :blk .{
1883 .seg = self.data_segment_cmd_index.?,
1884 .sect = self.tlv_ptrs_section_index.?,
1885 };
1845 break :blk self.tlv_ptrs_section_index.?;
18861846 },
18871847 macho.S_THREAD_LOCAL_REGULAR => {
18881848 if (self.tlv_data_section_index == null) {
......@@ -1896,11 +1856,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
18961856 },
18971857 );
18981858 }
1899
1900 break :blk .{
1901 .seg = self.data_segment_cmd_index.?,
1902 .sect = self.tlv_data_section_index.?,
1903 };
1859 break :blk self.tlv_data_section_index.?;
19041860 },
19051861 macho.S_THREAD_LOCAL_ZEROFILL => {
19061862 if (self.tlv_bss_section_index == null) {
......@@ -1914,11 +1870,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
19141870 },
19151871 );
19161872 }
1917
1918 break :blk .{
1919 .seg = self.data_segment_cmd_index.?,
1920 .sect = self.tlv_bss_section_index.?,
1921 };
1873 break :blk self.tlv_bss_section_index.?;
19221874 },
19231875 macho.S_COALESCED => {
19241876 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
......@@ -1933,11 +1885,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
19331885 .{},
19341886 );
19351887 }
1936
1937 break :blk .{
1938 .seg = self.text_segment_cmd_index.?,
1939 .sect = self.eh_frame_section_index.?,
1940 };
1888 break :blk self.eh_frame_section_index.?;
19411889 }
19421890
19431891 // TODO audit this: is this the right mapping?
......@@ -1951,10 +1899,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
19511899 );
19521900 }
19531901
1954 break :blk .{
1955 .seg = self.data_const_segment_cmd_index.?,
1956 .sect = self.data_const_section_index.?,
1957 };
1902 break :blk self.data_const_section_index.?;
19581903 },
19591904 macho.S_REGULAR => {
19601905 if (sect.isCode()) {
......@@ -1971,11 +1916,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
19711916 },
19721917 );
19731918 }
1974
1975 break :blk .{
1976 .seg = self.text_segment_cmd_index.?,
1977 .sect = self.text_section_index.?,
1978 };
1919 break :blk self.text_section_index.?;
19791920 }
19801921 if (sect.isDebug()) {
19811922 // TODO debug attributes
......@@ -1998,11 +1939,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
19981939 .{},
19991940 );
20001941 }
2001
2002 break :blk .{
2003 .seg = self.text_segment_cmd_index.?,
2004 .sect = self.ustring_section_index.?,
2005 };
1942 break :blk self.ustring_section_index.?;
20061943 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
20071944 if (self.gcc_except_tab_section_index == null) {
20081945 self.gcc_except_tab_section_index = try self.initSection(
......@@ -2013,11 +1950,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20131950 .{},
20141951 );
20151952 }
2016
2017 break :blk .{
2018 .seg = self.text_segment_cmd_index.?,
2019 .sect = self.gcc_except_tab_section_index.?,
2020 };
1953 break :blk self.gcc_except_tab_section_index.?;
20211954 } else if (mem.eql(u8, sectname, "__objc_methlist")) {
20221955 if (self.objc_methlist_section_index == null) {
20231956 self.objc_methlist_section_index = try self.initSection(
......@@ -2028,11 +1961,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20281961 .{},
20291962 );
20301963 }
2031
2032 break :blk .{
2033 .seg = self.text_segment_cmd_index.?,
2034 .sect = self.objc_methlist_section_index.?,
2035 };
1964 break :blk self.objc_methlist_section_index.?;
20361965 } else if (mem.eql(u8, sectname, "__rodata") or
20371966 mem.eql(u8, sectname, "__typelink") or
20381967 mem.eql(u8, sectname, "__itablink") or
......@@ -2048,11 +1977,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20481977 .{},
20491978 );
20501979 }
2051
2052 break :blk .{
2053 .seg = self.data_const_segment_cmd_index.?,
2054 .sect = self.data_const_section_index.?,
2055 };
1980 break :blk self.data_const_section_index.?;
20561981 } else {
20571982 if (self.text_const_section_index == null) {
20581983 self.text_const_section_index = try self.initSection(
......@@ -2063,11 +1988,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20631988 .{},
20641989 );
20651990 }
2066
2067 break :blk .{
2068 .seg = self.text_segment_cmd_index.?,
2069 .sect = self.text_const_section_index.?,
2070 };
1991 break :blk self.text_const_section_index.?;
20711992 }
20721993 }
20731994
......@@ -2081,11 +2002,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20812002 .{},
20822003 );
20832004 }
2084
2085 break :blk .{
2086 .seg = self.data_const_segment_cmd_index.?,
2087 .sect = self.data_const_section_index.?,
2088 };
2005 break :blk self.data_const_section_index.?;
20892006 }
20902007
20912008 if (mem.eql(u8, segname, "__DATA")) {
......@@ -2099,11 +2016,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
20992016 .{},
21002017 );
21012018 }
2102
2103 break :blk .{
2104 .seg = self.data_const_segment_cmd_index.?,
2105 .sect = self.data_const_section_index.?,
2106 };
2019 break :blk self.data_const_section_index.?;
21072020 } else if (mem.eql(u8, sectname, "__cfstring")) {
21082021 if (self.objc_cfstring_section_index == null) {
21092022 self.objc_cfstring_section_index = try self.initSection(
......@@ -2114,11 +2027,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21142027 .{},
21152028 );
21162029 }
2117
2118 break :blk .{
2119 .seg = self.data_const_segment_cmd_index.?,
2120 .sect = self.objc_cfstring_section_index.?,
2121 };
2030 break :blk self.objc_cfstring_section_index.?;
21222031 } else if (mem.eql(u8, sectname, "__objc_classlist")) {
21232032 if (self.objc_classlist_section_index == null) {
21242033 self.objc_classlist_section_index = try self.initSection(
......@@ -2129,11 +2038,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21292038 .{},
21302039 );
21312040 }
2132
2133 break :blk .{
2134 .seg = self.data_const_segment_cmd_index.?,
2135 .sect = self.objc_classlist_section_index.?,
2136 };
2041 break :blk self.objc_classlist_section_index.?;
21372042 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
21382043 if (self.objc_imageinfo_section_index == null) {
21392044 self.objc_imageinfo_section_index = try self.initSection(
......@@ -2144,11 +2049,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21442049 .{},
21452050 );
21462051 }
2147
2148 break :blk .{
2149 .seg = self.data_const_segment_cmd_index.?,
2150 .sect = self.objc_imageinfo_section_index.?,
2151 };
2052 break :blk self.objc_imageinfo_section_index.?;
21522053 } else if (mem.eql(u8, sectname, "__objc_const")) {
21532054 if (self.objc_const_section_index == null) {
21542055 self.objc_const_section_index = try self.initSection(
......@@ -2159,11 +2060,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21592060 .{},
21602061 );
21612062 }
2162
2163 break :blk .{
2164 .seg = self.data_segment_cmd_index.?,
2165 .sect = self.objc_const_section_index.?,
2166 };
2063 break :blk self.objc_const_section_index.?;
21672064 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
21682065 if (self.objc_classrefs_section_index == null) {
21692066 self.objc_classrefs_section_index = try self.initSection(
......@@ -2174,11 +2071,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21742071 .{},
21752072 );
21762073 }
2177
2178 break :blk .{
2179 .seg = self.data_segment_cmd_index.?,
2180 .sect = self.objc_classrefs_section_index.?,
2181 };
2074 break :blk self.objc_classrefs_section_index.?;
21822075 } else if (mem.eql(u8, sectname, "__objc_data")) {
21832076 if (self.objc_data_section_index == null) {
21842077 self.objc_data_section_index = try self.initSection(
......@@ -2189,11 +2082,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21892082 .{},
21902083 );
21912084 }
2192
2193 break :blk .{
2194 .seg = self.data_segment_cmd_index.?,
2195 .sect = self.objc_data_section_index.?,
2196 };
2085 break :blk self.objc_data_section_index.?;
21972086 } else if (mem.eql(u8, sectname, ".rustc")) {
21982087 if (self.rustc_section_index == null) {
21992088 self.rustc_section_index = try self.initSection(
......@@ -2207,11 +2096,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
22072096 // decompress the metadata.
22082097 self.rustc_section_size = sect.size;
22092098 }
2210
2211 break :blk .{
2212 .seg = self.data_segment_cmd_index.?,
2213 .sect = self.rustc_section_index.?,
2214 };
2099 break :blk self.rustc_section_index.?;
22152100 } else {
22162101 if (self.data_section_index == null) {
22172102 self.data_section_index = try self.initSection(
......@@ -2222,11 +2107,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
22222107 .{},
22232108 );
22242109 }
2225
2226 break :blk .{
2227 .seg = self.data_segment_cmd_index.?,
2228 .sect = self.data_section_index.?,
2229 };
2110 break :blk self.data_section_index.?;
22302111 }
22312112 }
22322113
......@@ -2259,30 +2140,33 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32
22592140 return atom;
22602141}
22612142
2262pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2263 const sect = self.getSection(match);
2143pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
2144 const section = self.sections.get(sect_id);
22642145 const sym = atom.getSymbol(self);
2265 const file_offset = sect.offset + sym.n_value - sect.addr;
2146 const file_offset = section.header.offset + sym.n_value - section.header.addr;
22662147 try atom.resolveRelocs(self);
22672148 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
22682149 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
22692150}
22702151
22712152fn allocateSymbols(self: *MachO) !void {
2272 var it = self.atoms.iterator();
2273 while (it.next()) |entry| {
2274 const match = entry.key_ptr.*;
2275 var atom = entry.value_ptr.*;
2153 const slice = self.sections.slice();
2154 for (slice.items(.last_atom)) |last_atom, sect_id| {
2155 const header = slice.items(.header)[sect_id];
2156 var atom = last_atom orelse continue;
22762157
22772158 while (atom.prev) |prev| {
22782159 atom = prev;
22792160 }
22802161
2281 const n_sect = self.getSectionOrdinal(match);
2282 const sect = self.getSection(match);
2283 var base_vaddr = sect.addr;
2162 const n_sect = @intCast(u8, sect_id + 1);
2163 var base_vaddr = header.addr;
22842164
2285 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ n_sect, sect.segName(), sect.sectName() });
2165 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
2166 n_sect,
2167 header.segName(),
2168 header.sectName(),
2169 });
22862170
22872171 while (true) {
22882172 const alignment = try math.powi(u32, 2, atom.alignment);
......@@ -2296,7 +2180,10 @@ fn allocateSymbols(self: *MachO) !void {
22962180
22972181 // Update each symbol contained within the atom
22982182 for (atom.contained.items) |sym_at_off| {
2299 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2183 const contained_sym = self.getSymbolPtr(.{
2184 .sym_index = sym_at_off.sym_index,
2185 .file = atom.file,
2186 });
23002187 contained_sym.n_value = base_vaddr + sym_at_off.offset;
23012188 contained_sym.n_sect = n_sect;
23022189 }
......@@ -2310,15 +2197,18 @@ fn allocateSymbols(self: *MachO) !void {
23102197 }
23112198}
23122199
2313fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void {
2314 var atom = self.atoms.get(match) orelse return;
2200fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
2201 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
23152202
23162203 while (true) {
23172204 const atom_sym = atom.getSymbolPtr(self);
23182205 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
23192206
23202207 for (atom.contained.items) |sym_at_off| {
2321 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2208 const contained_sym = self.getSymbolPtr(.{
2209 .sym_index = sym_at_off.sym_index,
2210 .file = atom.file,
2211 });
23222212 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
23232213 }
23242214
......@@ -2336,16 +2226,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23362226 const global = self.globals.get(name) orelse continue;
23372227 if (global.file != null) continue;
23382228 const sym = self.getSymbolPtr(global);
2339 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2340 sym.n_sect = self.getSectionOrdinal(.{
2341 .seg = self.text_segment_cmd_index.?,
2342 .sect = 0,
2343 });
2344 sym.n_value = seg.inner.vmaddr;
2229 const seg = self.segments.items[self.text_segment_cmd_index.?];
2230 sym.n_sect = 1;
2231 sym.n_value = seg.vmaddr;
23452232
23462233 log.debug("allocating {s} at the start of {s}", .{
23472234 name,
2348 seg.inner.segName(),
2235 seg.segName(),
23492236 });
23502237 }
23512238}
......@@ -2353,18 +2240,20 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23532240fn writeAtomsOneShot(self: *MachO) !void {
23542241 assert(self.mode == .one_shot);
23552242
2356 var it = self.atoms.iterator();
2357 while (it.next()) |entry| {
2358 const sect = self.getSection(entry.key_ptr.*);
2359 var atom: *Atom = entry.value_ptr.*;
2243 const gpa = self.base.allocator;
2244 const slice = self.sections.slice();
2245
2246 for (slice.items(.last_atom)) |last_atom, sect_id| {
2247 const header = slice.items(.header)[sect_id];
2248 var atom = last_atom.?;
23602249
2361 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
2250 if (header.flags == macho.S_ZEROFILL or header.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
23622251
2363 var buffer = std.ArrayList(u8).init(self.base.allocator);
2252 var buffer = std.ArrayList(u8).init(gpa);
23642253 defer buffer.deinit();
2365 try buffer.ensureTotalCapacity(math.cast(usize, sect.size) orelse return error.Overflow);
2254 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
23662255
2367 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
2256 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
23682257
23692258 while (atom.prev) |prev| {
23702259 atom = prev;
......@@ -2399,18 +2288,18 @@ fn writeAtomsOneShot(self: *MachO) !void {
23992288 if (atom.next) |next| {
24002289 atom = next;
24012290 } else {
2402 assert(buffer.items.len == sect.size);
2403 log.debug(" (writing at file offset 0x{x})", .{sect.offset});
2404 try self.base.file.?.pwriteAll(buffer.items, sect.offset);
2291 assert(buffer.items.len == header.size);
2292 log.debug(" (writing at file offset 0x{x})", .{header.offset});
2293 try self.base.file.?.pwriteAll(buffer.items, header.offset);
24052294 break;
24062295 }
24072296 }
24082297 }
24092298}
24102299
2411fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anytype) !void {
2412 const is_code = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
2413 const min_alignment: u3 = if (!is_code)
2300fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
2301 const header = self.sections.items(.header)[sect_id];
2302 const min_alignment: u3 = if (!header.isCode())
24142303 1
24152304 else switch (self.base.options.target.cpu.arch) {
24162305 .aarch64 => @sizeOf(u32),
......@@ -2421,7 +2310,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24212310 const len = @divExact(size, min_alignment);
24222311 var i: usize = 0;
24232312 while (i < len) : (i += 1) {
2424 if (!is_code) {
2313 if (!header.isCode()) {
24252314 try writer.writeByte(0);
24262315 } else switch (self.base.options.target.cpu.arch) {
24272316 .aarch64 => {
......@@ -2439,20 +2328,20 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24392328fn writeAtomsIncremental(self: *MachO) !void {
24402329 assert(self.mode == .incremental);
24412330
2442 var it = self.atoms.iterator();
2443 while (it.next()) |entry| {
2444 const match = entry.key_ptr.*;
2445 const sect = self.getSection(match);
2446 var atom: *Atom = entry.value_ptr.*;
2331 const slice = self.sections.slice();
2332 for (slice.items(.last_atom)) |last, i| {
2333 var atom: *Atom = last orelse continue;
2334 const sect_i = @intCast(u8, i);
2335 const header = slice.items(.header)[sect_i];
24472336
24482337 // TODO handle zerofill in stage2
24492338 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
24502339
2451 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
2340 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
24522341
24532342 while (true) {
24542343 if (atom.dirty) {
2455 try self.writeAtom(atom, match);
2344 try self.writeAtom(atom, sect_i);
24562345 atom.dirty = false;
24572346 }
24582347
......@@ -2503,10 +2392,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25032392 try self.managed_atoms.append(gpa, atom);
25042393 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25052394
2506 try self.allocateAtomCommon(atom, .{
2507 .seg = self.data_const_segment_cmd_index.?,
2508 .sect = self.got_section_index.?,
2509 });
2395 try self.allocateAtomCommon(atom, self.got_section_index.?);
25102396
25112397 return atom;
25122398}
......@@ -2535,7 +2421,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25352421 try self.managed_atoms.append(gpa, atom);
25362422 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25372423
2538 const match = (try self.getMatchingSection(.{
2424 const match = (try self.getOutputSection(.{
25392425 .segname = makeStaticString("__DATA"),
25402426 .sectname = makeStaticString("__thread_ptrs"),
25412427 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
......@@ -2561,10 +2447,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
25612447 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
25622448 self.dyld_private_atom = atom;
25632449
2564 try self.allocateAtomCommon(atom, .{
2565 .seg = self.data_segment_cmd_index.?,
2566 .sect = self.data_section_index.?,
2567 });
2450 try self.allocateAtomCommon(atom, self.data_section_index.?);
25682451
25692452 try self.managed_atoms.append(gpa, atom);
25702453 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2692,10 +2575,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
26922575 }
26932576 self.stub_helper_preamble_atom = atom;
26942577
2695 try self.allocateAtomCommon(atom, .{
2696 .seg = self.text_segment_cmd_index.?,
2697 .sect = self.stub_helper_section_index.?,
2698 });
2578 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
26992579
27002580 try self.managed_atoms.append(gpa, atom);
27012581 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2771,10 +2651,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
27712651 try self.managed_atoms.append(gpa, atom);
27722652 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
27732653
2774 try self.allocateAtomCommon(atom, .{
2775 .seg = self.text_segment_cmd_index.?,
2776 .sect = self.stub_helper_section_index.?,
2777 });
2654 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
27782655
27792656 return atom;
27802657}
......@@ -2814,10 +2691,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
28142691 try self.managed_atoms.append(gpa, atom);
28152692 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28162693
2817 try self.allocateAtomCommon(atom, .{
2818 .seg = self.data_segment_cmd_index.?,
2819 .sect = self.la_symbol_ptr_section_index.?,
2820 });
2694 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
28212695
28222696 return atom;
28232697}
......@@ -2896,10 +2770,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
28962770 try self.managed_atoms.append(gpa, atom);
28972771 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28982772
2899 try self.allocateAtomCommon(atom, .{
2900 .seg = self.text_segment_cmd_index.?,
2901 .sect = self.stubs_section_index.?,
2902 });
2773 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
29032774
29042775 return atom;
29052776}
......@@ -2917,12 +2788,6 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29172788
29182789 // Convert any tentative definition into a regular symbol and allocate
29192790 // text blocks for each tentative definition.
2920 const match = MatchingSection{
2921 .seg = self.data_segment_cmd_index.?,
2922 .sect = self.bss_section_index.?,
2923 };
2924 _ = try self.section_ordinals.getOrPut(gpa, match);
2925
29262791 const size = sym.n_value;
29272792 const alignment = (sym.n_desc >> 8) & 0x0f;
29282793
......@@ -2937,7 +2802,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29372802 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
29382803 atom.file = global.file;
29392804
2940 try self.allocateAtomCommon(atom, match);
2805 try self.allocateAtomCommon(atom, self.bss_section_index.?);
29412806
29422807 if (global.file) |file| {
29432808 const object = &self.objects.items[file];
......@@ -3060,7 +2925,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
30602925 gop.value_ptr.* = current;
30612926}
30622927
3063fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2928fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2929 const object = &self.objects.items[object_id];
30642930 log.debug("resolving symbols in '{s}'", .{object.name});
30652931
30662932 for (object.symtab.items) |sym, index| {
......@@ -3115,6 +2981,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
31152981fn resolveSymbolsInArchives(self: *MachO) !void {
31162982 if (self.archives.items.len == 0) return;
31172983
2984 const gpa = self.base.allocator;
2985 const cpu_arch = self.base.options.target.cpu.arch;
31182986 var next_sym: usize = 0;
31192987 loop: while (next_sym < self.unresolved.count()) {
31202988 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
......@@ -3129,13 +2997,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
31292997 assert(offsets.items.len > 0);
31302998
31312999 const object_id = @intCast(u16, self.objects.items.len);
3132 const object = try self.objects.addOne(self.base.allocator);
3133 object.* = try archive.parseObject(
3134 self.base.allocator,
3135 self.base.options.target.cpu.arch,
3136 offsets.items[0],
3137 );
3138 try self.resolveSymbolsInObject(object, object_id);
3000 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
3001 try self.objects.append(gpa, object);
3002 try self.resolveSymbolsInObject(object_id);
31393003
31403004 continue :loop;
31413005 }
......@@ -3159,7 +3023,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31593023
31603024 const dylib_id = @intCast(u16, id);
31613025 if (!self.referenced_dylibs.contains(dylib_id)) {
3162 try self.addLoadDylibLC(dylib_id);
31633026 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
31643027 }
31653028
......@@ -3257,7 +3120,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32573120
32583121 const dylib_id = @intCast(u16, id);
32593122 if (!self.referenced_dylibs.contains(dylib_id)) {
3260 try self.addLoadDylibLC(dylib_id);
32613123 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
32623124 }
32633125
......@@ -3280,47 +3142,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32803142 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
32813143}
32823144
3283fn addLoadDylibLC(self: *MachO, id: u16) !void {
3284 const dylib = self.dylibs.items[id];
3285 const dylib_id = dylib.id orelse unreachable;
3286 var dylib_cmd = try macho.createLoadDylibCommand(
3287 self.base.allocator,
3288 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3289 dylib_id.name,
3290 dylib_id.timestamp,
3291 dylib_id.current_version,
3292 dylib_id.compatibility_version,
3293 );
3294 errdefer dylib_cmd.deinit(self.base.allocator);
3295 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
3296 self.load_commands_dirty = true;
3145fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
3146 const name_len = mem.sliceTo(default_dyld_path, 0).len;
3147 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3148 u64,
3149 @sizeOf(macho.dylinker_command) + name_len,
3150 @sizeOf(u64),
3151 ));
3152 try lc_writer.writeStruct(macho.dylinker_command{
3153 .cmd = .LOAD_DYLINKER,
3154 .cmdsize = cmdsize,
3155 .name = @sizeOf(macho.dylinker_command),
3156 });
3157 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
3158 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
3159 if (padding > 0) {
3160 try lc_writer.writeByteNTimes(0, padding);
3161 }
3162 ncmds.* += 1;
3163}
3164
3165fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3166 if (self.base.options.output_mode != .Exe) return;
3167 const seg = self.segments.items[self.text_segment_cmd_index.?];
3168 const global = try self.getEntryPoint();
3169 const sym = self.getSymbol(global);
3170 try lc_writer.writeStruct(macho.entry_point_command{
3171 .cmd = .MAIN,
3172 .cmdsize = @sizeOf(macho.entry_point_command),
3173 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
3174 .stacksize = self.base.options.stack_size_override orelse 0,
3175 });
3176 ncmds.* += 1;
32973177}
32983178
3299fn addCodeSignatureLC(self: *MachO) !void {
3300 if (self.code_signature_cmd_index != null or self.code_signature == null) return;
3301 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
3302 try self.load_commands.append(self.base.allocator, .{
3303 .linkedit_data = .{
3304 .cmd = .CODE_SIGNATURE,
3305 .cmdsize = @sizeOf(macho.linkedit_data_command),
3306 .dataoff = 0,
3307 .datasize = 0,
3179const WriteDylibLCCtx = struct {
3180 cmd: macho.LC,
3181 name: []const u8,
3182 timestamp: u32 = 2,
3183 current_version: u32 = 0x10000,
3184 compatibility_version: u32 = 0x10000,
3185};
3186
3187fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
3188 const name_len = ctx.name.len + 1;
3189 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3190 u64,
3191 @sizeOf(macho.dylib_command) + name_len,
3192 @sizeOf(u64),
3193 ));
3194 try lc_writer.writeStruct(macho.dylib_command{
3195 .cmd = ctx.cmd,
3196 .cmdsize = cmdsize,
3197 .dylib = .{
3198 .name = @sizeOf(macho.dylib_command),
3199 .timestamp = ctx.timestamp,
3200 .current_version = ctx.current_version,
3201 .compatibility_version = ctx.compatibility_version,
33083202 },
33093203 });
3310 self.load_commands_dirty = true;
3204 try lc_writer.writeAll(ctx.name);
3205 try lc_writer.writeByte(0);
3206 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
3207 if (padding > 0) {
3208 try lc_writer.writeByteNTimes(0, padding);
3209 }
3210 ncmds.* += 1;
33113211}
33123212
3313fn setEntryPoint(self: *MachO) !void {
3314 if (self.base.options.output_mode != .Exe) return;
3213fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3214 if (self.base.options.output_mode != .Lib) return;
3215 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
3216 const curr = self.base.options.version orelse std.builtin.Version{
3217 .major = 1,
3218 .minor = 0,
3219 .patch = 0,
3220 };
3221 const compat = self.base.options.compatibility_version orelse std.builtin.Version{
3222 .major = 1,
3223 .minor = 0,
3224 .patch = 0,
3225 };
3226 try writeDylibLC(.{
3227 .cmd = .ID_DYLIB,
3228 .name = install_name,
3229 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
3230 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
3231 }, ncmds, lc_writer);
3232}
33153233
3316 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
3317 const global = try self.getEntryPoint();
3318 const sym = self.getSymbol(global);
3319 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
3320 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
3321 ec.stacksize = self.base.options.stack_size_override orelse 0;
3322 self.entry_addr = sym.n_value;
3323 self.load_commands_dirty = true;
3234const RpathIterator = struct {
3235 buffer: []const []const u8,
3236 table: std.StringHashMap(void),
3237 count: usize = 0,
3238
3239 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
3240 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
3241 }
3242
3243 fn deinit(it: *RpathIterator) void {
3244 it.table.deinit();
3245 }
3246
3247 fn next(it: *RpathIterator) !?[]const u8 {
3248 while (true) {
3249 if (it.count >= it.buffer.len) return null;
3250 const rpath = it.buffer[it.count];
3251 it.count += 1;
3252 const gop = try it.table.getOrPut(rpath);
3253 if (gop.found_existing) continue;
3254 return rpath;
3255 }
3256 }
3257};
3258
3259fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3260 const gpa = self.base.allocator;
3261
3262 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
3263 defer it.deinit();
3264
3265 while (try it.next()) |rpath| {
3266 const rpath_len = rpath.len + 1;
3267 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3268 u64,
3269 @sizeOf(macho.rpath_command) + rpath_len,
3270 @sizeOf(u64),
3271 ));
3272 try lc_writer.writeStruct(macho.rpath_command{
3273 .cmdsize = cmdsize,
3274 .path = @sizeOf(macho.rpath_command),
3275 });
3276 try lc_writer.writeAll(rpath);
3277 try lc_writer.writeByte(0);
3278 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
3279 if (padding > 0) {
3280 try lc_writer.writeByteNTimes(0, padding);
3281 }
3282 ncmds.* += 1;
3283 }
3284}
3285
3286fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3287 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
3288 const platform_version = blk: {
3289 const ver = self.base.options.target.os.version_range.semver.min;
3290 const platform_version = ver.major << 16 | ver.minor << 8;
3291 break :blk platform_version;
3292 };
3293 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
3294 const ver = sdk.version;
3295 const sdk_version = ver.major << 16 | ver.minor << 8;
3296 break :blk sdk_version;
3297 } else platform_version;
3298 const is_simulator_abi = self.base.options.target.abi == .simulator;
3299 try lc_writer.writeStruct(macho.build_version_command{
3300 .cmdsize = cmdsize,
3301 .platform = switch (self.base.options.target.os.tag) {
3302 .macos => .MACOS,
3303 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
3304 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
3305 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
3306 else => unreachable,
3307 },
3308 .minos = platform_version,
3309 .sdk = sdk_version,
3310 .ntools = 1,
3311 });
3312 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
3313 .tool = .LD,
3314 .version = 0x0,
3315 }));
3316 ncmds.* += 1;
3317}
3318
3319fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3320 for (self.referenced_dylibs.keys()) |id| {
3321 const dylib = self.dylibs.items[id];
3322 const dylib_id = dylib.id orelse unreachable;
3323 try writeDylibLC(.{
3324 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3325 .name = dylib_id.name,
3326 .timestamp = dylib_id.timestamp,
3327 .current_version = dylib_id.current_version,
3328 .compatibility_version = dylib_id.compatibility_version,
3329 }, ncmds, lc_writer);
3330 }
33243331}
33253332
33263333pub fn deinit(self: *MachO) void {
......@@ -3334,7 +3341,6 @@ pub fn deinit(self: *MachO) void {
33343341 d_sym.deinit(gpa);
33353342 }
33363343
3337 self.section_ordinals.deinit(gpa);
33383344 self.tlv_ptr_entries.deinit(gpa);
33393345 self.tlv_ptr_entries_free_list.deinit(gpa);
33403346 self.tlv_ptr_entries_table.deinit(gpa);
......@@ -3371,24 +3377,19 @@ pub fn deinit(self: *MachO) void {
33713377 self.dylibs_map.deinit(gpa);
33723378 self.referenced_dylibs.deinit(gpa);
33733379
3374 for (self.load_commands.items) |*lc| {
3375 lc.deinit(gpa);
3380 self.segments.deinit(gpa);
3381
3382 for (self.sections.items(.free_list)) |*list| {
3383 list.deinit(gpa);
33763384 }
3377 self.load_commands.deinit(gpa);
3385 self.sections.deinit(gpa);
33783386
33793387 for (self.managed_atoms.items) |atom| {
33803388 atom.deinit(gpa);
33813389 gpa.destroy(atom);
33823390 }
33833391 self.managed_atoms.deinit(gpa);
3384 self.atoms.deinit(gpa);
3385 {
3386 var it = self.atom_free_lists.valueIterator();
3387 while (it.next()) |free_list| {
3388 free_list.deinit(gpa);
3389 }
3390 self.atom_free_lists.deinit(gpa);
3391 }
3392
33923393 if (self.base.options.module) |mod| {
33933394 for (self.decls.keys()) |decl_index| {
33943395 const decl = mod.declPtr(decl_index);
......@@ -3408,34 +3409,24 @@ pub fn deinit(self: *MachO) void {
34083409 }
34093410
34103411 self.atom_by_index_table.deinit(gpa);
3411
3412 if (self.code_signature) |*csig| {
3413 csig.deinit(gpa);
3414 }
34153412}
34163413
34173414pub fn closeFiles(self: MachO) void {
3418 for (self.objects.items) |object| {
3419 object.file.close();
3420 }
34213415 for (self.archives.items) |archive| {
34223416 archive.file.close();
34233417 }
3424 for (self.dylibs.items) |dylib| {
3425 dylib.file.close();
3426 }
34273418 if (self.d_sym) |ds| {
34283419 ds.file.close();
34293420 }
34303421}
34313422
3432fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
3423fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
34333424 log.debug("freeAtom {*}", .{atom});
34343425 if (!owns_atom) {
34353426 atom.deinit(self.base.allocator);
34363427 }
34373428
3438 const free_list = self.atom_free_lists.getPtr(match).?;
3429 const free_list = &self.sections.items(.free_list)[sect_id];
34393430 var already_have_free_list_node = false;
34403431 {
34413432 var i: usize = 0;
......@@ -3452,13 +3443,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34523443 }
34533444 }
34543445
3455 if (self.atoms.getPtr(match)) |last_atom| {
3456 if (last_atom.* == atom) {
3446 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3447 if (maybe_last_atom.*) |last_atom| {
3448 if (last_atom == atom) {
34573449 if (atom.prev) |prev| {
34583450 // TODO shrink the section size here
3459 last_atom.* = prev;
3451 maybe_last_atom.* = prev;
34603452 } else {
3461 _ = self.atoms.fetchRemove(match);
3453 maybe_last_atom.* = null;
34623454 }
34633455 }
34643456 }
......@@ -3486,21 +3478,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34863478 }
34873479}
34883480
3489fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSection) void {
3481fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, sect_id: u8) void {
34903482 _ = self;
34913483 _ = atom;
34923484 _ = new_block_size;
3493 _ = match;
3485 _ = sect_id;
34943486 // TODO check the new capacity, and if it crosses the size threshold into a big enough
34953487 // capacity, insert a free list node for it.
34963488}
34973489
3498fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
3490fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u8) !u64 {
34993491 const sym = atom.getSymbol(self);
35003492 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
35013493 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
35023494 if (!need_realloc) return sym.n_value;
3503 return self.allocateAtom(atom, new_atom_size, alignment, match);
3495 return self.allocateAtom(atom, new_atom_size, alignment, sect_id);
35043496}
35053497
35063498fn allocateSymbol(self: *MachO) !u32 {
......@@ -3671,10 +3663,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
36713663}
36723664
36733665pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
3674 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3666 const gpa = self.base.allocator;
3667
3668 var code_buffer = std.ArrayList(u8).init(gpa);
36753669 defer code_buffer.deinit();
36763670
3677 const gpa = self.base.allocator;
36783671 const module = self.base.options.module.?;
36793672 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
36803673 if (!gop.found_existing) {
......@@ -3725,25 +3718,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
37253718 atom.code.clearRetainingCapacity();
37263719 try atom.code.appendSlice(gpa, code);
37273720
3728 const match = try self.getMatchingSectionAtom(
3721 const sect_id = try self.getOutputSectionAtom(
37293722 atom,
37303723 decl_name,
37313724 typed_value.ty,
37323725 typed_value.val,
37333726 required_alignment,
37343727 );
3735 const addr = try self.allocateAtom(atom, code.len, required_alignment, match);
3728 const addr = try self.allocateAtom(atom, code.len, required_alignment, sect_id);
37363729
37373730 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
37383731 log.debug(" (required alignment 0x{x})", .{required_alignment});
37393732
3740 errdefer self.freeAtom(atom, match, true);
3733 errdefer self.freeAtom(atom, sect_id, true);
37413734
37423735 const symbol = atom.getSymbolPtr(self);
37433736 symbol.* = .{
37443737 .n_strx = name_str_index,
37453738 .n_type = macho.N_SECT,
3746 .n_sect = self.getSectionOrdinal(match),
3739 .n_sect = sect_id + 1,
37473740 .n_desc = 0,
37483741 .n_value = addr,
37493742 };
......@@ -3894,44 +3887,35 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
38943887 }
38953888}
38963889
3897fn getMatchingSectionAtom(
3890fn getOutputSectionAtom(
38983891 self: *MachO,
38993892 atom: *Atom,
39003893 name: []const u8,
39013894 ty: Type,
39023895 val: Value,
39033896 alignment: u32,
3904) !MatchingSection {
3897) !u8 {
39053898 const code = atom.code.items;
39063899 const mod = self.base.options.module.?;
39073900 const align_log_2 = math.log2(alignment);
39083901 const zig_ty = ty.zigTypeTag();
39093902 const mode = self.base.options.optimize_mode;
3910 const match: MatchingSection = blk: {
3903 const sect_id: u8 = blk: {
39113904 // TODO finish and audit this function
39123905 if (val.isUndefDeep()) {
39133906 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3914 break :blk MatchingSection{
3915 .seg = self.data_segment_cmd_index.?,
3916 .sect = self.bss_section_index.?,
3917 };
3907 break :blk self.bss_section_index.?;
39183908 } else {
3919 break :blk MatchingSection{
3920 .seg = self.data_segment_cmd_index.?,
3921 .sect = self.data_section_index.?,
3922 };
3909 break :blk self.data_section_index.?;
39233910 }
39243911 }
39253912
39263913 if (val.castTag(.variable)) |_| {
3927 break :blk MatchingSection{
3928 .seg = self.data_segment_cmd_index.?,
3929 .sect = self.data_section_index.?,
3930 };
3914 break :blk self.data_section_index.?;
39313915 }
39323916
39333917 if (needsPointerRebase(ty, val, mod)) {
3934 break :blk (try self.getMatchingSection(.{
3918 break :blk (try self.getOutputSection(.{
39353919 .segname = makeStaticString("__DATA_CONST"),
39363920 .sectname = makeStaticString("__const"),
39373921 .size = code.len,
......@@ -3941,10 +3925,7 @@ fn getMatchingSectionAtom(
39413925
39423926 switch (zig_ty) {
39433927 .Fn => {
3944 break :blk MatchingSection{
3945 .seg = self.text_segment_cmd_index.?,
3946 .sect = self.text_section_index.?,
3947 };
3928 break :blk self.text_section_index.?;
39483929 },
39493930 .Array => {
39503931 if (val.tag() == .bytes) {
......@@ -3953,7 +3934,7 @@ fn getMatchingSectionAtom(
39533934 .const_slice_u8_sentinel_0,
39543935 .manyptr_const_u8_sentinel_0,
39553936 => {
3956 break :blk (try self.getMatchingSection(.{
3937 break :blk (try self.getOutputSection(.{
39573938 .segname = makeStaticString("__TEXT"),
39583939 .sectname = makeStaticString("__cstring"),
39593940 .flags = macho.S_CSTRING_LITERALS,
......@@ -3967,22 +3948,21 @@ fn getMatchingSectionAtom(
39673948 },
39683949 else => {},
39693950 }
3970 break :blk (try self.getMatchingSection(.{
3951 break :blk (try self.getOutputSection(.{
39713952 .segname = makeStaticString("__TEXT"),
39723953 .sectname = makeStaticString("__const"),
39733954 .size = code.len,
39743955 .@"align" = align_log_2,
39753956 })).?;
39763957 };
3977 const sect = self.getSection(match);
3978 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
3958 const header = self.sections.items(.header)[sect_id];
3959 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
39793960 name,
3980 sect.segName(),
3981 sect.sectName(),
3982 match.seg,
3983 match.sect,
3961 header.segName(),
3962 header.sectName(),
3963 sect_id,
39843964 });
3985 return match;
3965 return sect_id;
39863966}
39873967
39883968fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
......@@ -3996,7 +3976,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
39963976
39973977 const decl_ptr = self.decls.getPtr(decl_index).?;
39983978 if (decl_ptr.* == null) {
3999 decl_ptr.* = try self.getMatchingSectionAtom(
3979 decl_ptr.* = try self.getOutputSectionAtom(
40003980 &decl.link.macho,
40013981 sym_name,
40023982 decl.ty,
......@@ -4045,7 +4025,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
40454025 symbol.* = .{
40464026 .n_strx = name_str_index,
40474027 .n_type = macho.N_SECT,
4048 .n_sect = self.getSectionOrdinal(match),
4028 .n_sect = match + 1,
40494029 .n_desc = 0,
40504030 .n_value = addr,
40514031 };
......@@ -4134,10 +4114,7 @@ pub fn updateDeclExports(
41344114 sym.* = .{
41354115 .n_strx = try self.strtab.insert(gpa, exp_name),
41364116 .n_type = macho.N_SECT | macho.N_EXT,
4137 .n_sect = self.getSectionOrdinal(.{
4138 .seg = self.text_segment_cmd_index.?,
4139 .sect = self.text_section_index.?, // TODO what if we export a variable?
4140 }),
4117 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?
41414118 .n_desc = 0,
41424119 .n_value = decl_sym.n_value,
41434120 };
......@@ -4208,10 +4185,7 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
42084185fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
42094186 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
42104187 for (unnamed_consts.items) |atom| {
4211 self.freeAtom(atom, .{
4212 .seg = self.text_segment_cmd_index.?,
4213 .sect = self.text_const_section_index.?,
4214 }, true);
4188 self.freeAtom(atom, self.text_const_section_index.?, true);
42154189 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
42164190 self.locals.items[atom.sym_index].n_type = 0;
42174191 _ = self.atom_by_index_table.remove(atom.sym_index);
......@@ -4294,6 +4268,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
42944268}
42954269
42964270fn populateMissingMetadata(self: *MachO) !void {
4271 const gpa = self.base.allocator;
42974272 const cpu_arch = self.base.options.target.cpu.arch;
42984273 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
42994274 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
......@@ -4305,21 +4280,16 @@ fn populateMissingMetadata(self: *MachO) !void {
43054280 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
43064281 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
43074282 }
4308 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4309 try self.load_commands.append(self.base.allocator, .{
4310 .segment = .{
4311 .inner = .{
4312 .segname = makeStaticString("__PAGEZERO"),
4313 .vmsize = aligned_pagezero_vmsize,
4314 .cmdsize = @sizeOf(macho.segment_command_64),
4315 },
4316 },
4283 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
4284 try self.segments.append(gpa, .{
4285 .segname = makeStaticString("__PAGEZERO"),
4286 .vmsize = aligned_pagezero_vmsize,
4287 .cmdsize = @sizeOf(macho.segment_command_64),
43174288 });
4318 self.load_commands_dirty = true;
43194289 }
43204290
43214291 if (self.text_segment_cmd_index == null) {
4322 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4292 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);
43234293 const needed_size = if (self.mode == .incremental) blk: {
43244294 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
43254295 const program_code_size_hint = self.base.options.program_code_size_hint;
......@@ -4329,20 +4299,15 @@ fn populateMissingMetadata(self: *MachO) !void {
43294299 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
43304300 break :blk needed_size;
43314301 } else 0;
4332 try self.load_commands.append(self.base.allocator, .{
4333 .segment = .{
4334 .inner = .{
4335 .segname = makeStaticString("__TEXT"),
4336 .vmaddr = aligned_pagezero_vmsize,
4337 .vmsize = needed_size,
4338 .filesize = needed_size,
4339 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
4340 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4341 .cmdsize = @sizeOf(macho.segment_command_64),
4342 },
4343 },
4302 try self.segments.append(gpa, .{
4303 .segname = makeStaticString("__TEXT"),
4304 .vmaddr = aligned_pagezero_vmsize,
4305 .vmsize = needed_size,
4306 .filesize = needed_size,
4307 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
4308 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4309 .cmdsize = @sizeOf(macho.segment_command_64),
43444310 });
4345 self.load_commands_dirty = true;
43464311 }
43474312
43484313 if (self.text_section_index == null) {
......@@ -4419,7 +4384,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44194384 }
44204385
44214386 if (self.data_const_segment_cmd_index == null) {
4422 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4387 self.data_const_segment_cmd_index = @intCast(u8, self.segments.items.len);
44234388 var vmaddr: u64 = 0;
44244389 var fileoff: u64 = 0;
44254390 var needed_size: u64 = 0;
......@@ -4434,21 +4399,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44344399 fileoff + needed_size,
44354400 });
44364401 }
4437 try self.load_commands.append(self.base.allocator, .{
4438 .segment = .{
4439 .inner = .{
4440 .segname = makeStaticString("__DATA_CONST"),
4441 .vmaddr = vmaddr,
4442 .vmsize = needed_size,
4443 .fileoff = fileoff,
4444 .filesize = needed_size,
4445 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4446 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4447 .cmdsize = @sizeOf(macho.segment_command_64),
4448 },
4449 },
4402 try self.segments.append(gpa, .{
4403 .segname = makeStaticString("__DATA_CONST"),
4404 .vmaddr = vmaddr,
4405 .vmsize = needed_size,
4406 .fileoff = fileoff,
4407 .filesize = needed_size,
4408 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4409 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4410 .cmdsize = @sizeOf(macho.segment_command_64),
44504411 });
4451 self.load_commands_dirty = true;
44524412 }
44534413
44544414 if (self.got_section_index == null) {
......@@ -4469,7 +4429,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44694429 }
44704430
44714431 if (self.data_segment_cmd_index == null) {
4472 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4432 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);
44734433 var vmaddr: u64 = 0;
44744434 var fileoff: u64 = 0;
44754435 var needed_size: u64 = 0;
......@@ -4484,21 +4444,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44844444 fileoff + needed_size,
44854445 });
44864446 }
4487 try self.load_commands.append(self.base.allocator, .{
4488 .segment = .{
4489 .inner = .{
4490 .segname = makeStaticString("__DATA"),
4491 .vmaddr = vmaddr,
4492 .vmsize = needed_size,
4493 .fileoff = fileoff,
4494 .filesize = needed_size,
4495 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4496 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4497 .cmdsize = @sizeOf(macho.segment_command_64),
4498 },
4499 },
4447 try self.segments.append(gpa, .{
4448 .segname = makeStaticString("__DATA"),
4449 .vmaddr = vmaddr,
4450 .vmsize = needed_size,
4451 .fileoff = fileoff,
4452 .filesize = needed_size,
4453 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4454 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4455 .cmdsize = @sizeOf(macho.segment_command_64),
45004456 });
4501 self.load_commands_dirty = true;
45024457 }
45034458
45044459 if (self.la_symbol_ptr_section_index == null) {
......@@ -4602,7 +4557,7 @@ fn populateMissingMetadata(self: *MachO) !void {
46024557 }
46034558
46044559 if (self.linkedit_segment_cmd_index == null) {
4605 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4560 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
46064561 var vmaddr: u64 = 0;
46074562 var fileoff: u64 = 0;
46084563 if (self.mode == .incremental) {
......@@ -4611,249 +4566,113 @@ fn populateMissingMetadata(self: *MachO) !void {
46114566 fileoff = base.fileoff;
46124567 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
46134568 }
4614 try self.load_commands.append(self.base.allocator, .{
4615 .segment = .{
4616 .inner = .{
4617 .segname = makeStaticString("__LINKEDIT"),
4618 .vmaddr = vmaddr,
4619 .fileoff = fileoff,
4620 .maxprot = macho.PROT.READ,
4621 .initprot = macho.PROT.READ,
4622 .cmdsize = @sizeOf(macho.segment_command_64),
4623 },
4624 },
4569 try self.segments.append(gpa, .{
4570 .segname = makeStaticString("__LINKEDIT"),
4571 .vmaddr = vmaddr,
4572 .fileoff = fileoff,
4573 .maxprot = macho.PROT.READ,
4574 .initprot = macho.PROT.READ,
4575 .cmdsize = @sizeOf(macho.segment_command_64),
46254576 });
4626 self.load_commands_dirty = true;
4627 }
4628
4629 if (self.dyld_info_cmd_index == null) {
4630 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
4631 try self.load_commands.append(self.base.allocator, .{
4632 .dyld_info_only = .{
4633 .cmd = .DYLD_INFO_ONLY,
4634 .cmdsize = @sizeOf(macho.dyld_info_command),
4635 .rebase_off = 0,
4636 .rebase_size = 0,
4637 .bind_off = 0,
4638 .bind_size = 0,
4639 .weak_bind_off = 0,
4640 .weak_bind_size = 0,
4641 .lazy_bind_off = 0,
4642 .lazy_bind_size = 0,
4643 .export_off = 0,
4644 .export_size = 0,
4645 },
4646 });
4647 self.load_commands_dirty = true;
4648 }
4649
4650 if (self.symtab_cmd_index == null) {
4651 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4652 try self.load_commands.append(self.base.allocator, .{
4653 .symtab = .{
4654 .cmdsize = @sizeOf(macho.symtab_command),
4655 .symoff = 0,
4656 .nsyms = 0,
4657 .stroff = 0,
4658 .strsize = 0,
4659 },
4660 });
4661 self.load_commands_dirty = true;
4662 }
4663
4664 if (self.dysymtab_cmd_index == null) {
4665 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4666 try self.load_commands.append(self.base.allocator, .{
4667 .dysymtab = .{
4668 .cmdsize = @sizeOf(macho.dysymtab_command),
4669 .ilocalsym = 0,
4670 .nlocalsym = 0,
4671 .iextdefsym = 0,
4672 .nextdefsym = 0,
4673 .iundefsym = 0,
4674 .nundefsym = 0,
4675 .tocoff = 0,
4676 .ntoc = 0,
4677 .modtaboff = 0,
4678 .nmodtab = 0,
4679 .extrefsymoff = 0,
4680 .nextrefsyms = 0,
4681 .indirectsymoff = 0,
4682 .nindirectsyms = 0,
4683 .extreloff = 0,
4684 .nextrel = 0,
4685 .locreloff = 0,
4686 .nlocrel = 0,
4687 },
4688 });
4689 self.load_commands_dirty = true;
46904577 }
4578}
46914579
4692 if (self.dylinker_cmd_index == null) {
4693 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
4694 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4695 u64,
4696 @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,
4697 @sizeOf(u64),
4698 ));
4699 var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
4700 .cmd = .LOAD_DYLINKER,
4701 .cmdsize = cmdsize,
4702 .name = @sizeOf(macho.dylinker_command),
4703 });
4704 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
4705 mem.set(u8, dylinker_cmd.data, 0);
4706 mem.copy(u8, dylinker_cmd.data, mem.sliceTo(default_dyld_path, 0));
4707 try self.load_commands.append(self.base.allocator, .{ .dylinker = dylinker_cmd });
4708 self.load_commands_dirty = true;
4709 }
4710
4711 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
4712 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
4713 try self.load_commands.append(self.base.allocator, .{
4714 .main = .{
4715 .cmdsize = @sizeOf(macho.entry_point_command),
4716 .entryoff = 0x0,
4717 .stacksize = 0,
4718 },
4719 });
4720 self.load_commands_dirty = true;
4721 }
4580inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
4581 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
4582 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
4583}
47224584
4723 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {
4724 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
4725 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
4726 const current_version = self.base.options.version orelse
4727 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4728 const compat_version = self.base.options.compatibility_version orelse
4729 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4730 var dylib_cmd = try macho.createLoadDylibCommand(
4731 self.base.allocator,
4732 .ID_DYLIB,
4733 install_name,
4734 2,
4735 current_version.major << 16 | current_version.minor << 8 | current_version.patch,
4736 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
4585fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4586 const gpa = self.base.allocator;
4587 var sizeofcmds: u64 = 0;
4588 for (self.segments.items) |seg| {
4589 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
4590 }
4591
4592 // LC_DYLD_INFO_ONLY
4593 sizeofcmds += @sizeOf(macho.dyld_info_command);
4594 // LC_FUNCTION_STARTS
4595 if (self.text_section_index != null) {
4596 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4597 }
4598 // LC_DATA_IN_CODE
4599 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4600 // LC_SYMTAB
4601 sizeofcmds += @sizeOf(macho.symtab_command);
4602 // LC_DYSYMTAB
4603 sizeofcmds += @sizeOf(macho.dysymtab_command);
4604 // LC_LOAD_DYLINKER
4605 sizeofcmds += calcInstallNameLen(
4606 @sizeOf(macho.dylinker_command),
4607 mem.sliceTo(default_dyld_path, 0),
4608 false,
4609 );
4610 // LC_MAIN
4611 if (self.base.options.output_mode == .Exe) {
4612 sizeofcmds += @sizeOf(macho.entry_point_command);
4613 }
4614 // LC_ID_DYLIB
4615 if (self.base.options.output_mode == .Lib) {
4616 sizeofcmds += blk: {
4617 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
4618 break :blk calcInstallNameLen(
4619 @sizeOf(macho.dylib_command),
4620 install_name,
4621 assume_max_path_len,
4622 );
4623 };
4624 }
4625 // LC_RPATH
4626 {
4627 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
4628 defer it.deinit();
4629 while (try it.next()) |rpath| {
4630 sizeofcmds += calcInstallNameLen(
4631 @sizeOf(macho.rpath_command),
4632 rpath,
4633 assume_max_path_len,
4634 );
4635 }
4636 }
4637 // LC_SOURCE_VERSION
4638 sizeofcmds += @sizeOf(macho.source_version_command);
4639 // LC_BUILD_VERSION
4640 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
4641 // LC_UUID
4642 sizeofcmds += @sizeOf(macho.uuid_command);
4643 // LC_LOAD_DYLIB
4644 for (self.referenced_dylibs.keys()) |id| {
4645 const dylib = self.dylibs.items[id];
4646 const dylib_id = dylib.id orelse unreachable;
4647 sizeofcmds += calcInstallNameLen(
4648 @sizeOf(macho.dylib_command),
4649 dylib_id.name,
4650 assume_max_path_len,
47374651 );
4738 errdefer dylib_cmd.deinit(self.base.allocator);
4739 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
4740 self.load_commands_dirty = true;
47414652 }
4742
4743 if (self.source_version_cmd_index == null) {
4744 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4745 try self.load_commands.append(self.base.allocator, .{
4746 .source_version = .{
4747 .cmdsize = @sizeOf(macho.source_version_command),
4748 .version = 0x0,
4749 },
4750 });
4751 self.load_commands_dirty = true;
4653 // LC_CODE_SIGNATURE
4654 {
4655 const target = self.base.options.target;
4656 const requires_codesig = blk: {
4657 if (self.base.options.entitlements) |_| break :blk true;
4658 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
4659 break :blk true;
4660 break :blk false;
4661 };
4662 if (requires_codesig) {
4663 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4664 }
47524665 }
47534666
4754 if (self.build_version_cmd_index == null) {
4755 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4756 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4757 u64,
4758 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
4759 @sizeOf(u64),
4760 ));
4761 const platform_version = blk: {
4762 const ver = self.base.options.target.os.version_range.semver.min;
4763 const platform_version = ver.major << 16 | ver.minor << 8;
4764 break :blk platform_version;
4765 };
4766 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
4767 const ver = sdk.version;
4768 const sdk_version = ver.major << 16 | ver.minor << 8;
4769 break :blk sdk_version;
4770 } else platform_version;
4771 const is_simulator_abi = self.base.options.target.abi == .simulator;
4772 var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{
4773 .cmdsize = cmdsize,
4774 .platform = switch (self.base.options.target.os.tag) {
4775 .macos => .MACOS,
4776 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
4777 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
4778 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
4779 else => unreachable,
4780 },
4781 .minos = platform_version,
4782 .sdk = sdk_version,
4783 .ntools = 1,
4784 });
4785 const ld_ver = macho.build_tool_version{
4786 .tool = .LD,
4787 .version = 0x0,
4788 };
4789 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4790 mem.set(u8, cmd.data, 0);
4791 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4792 try self.load_commands.append(self.base.allocator, .{ .build_version = cmd });
4793 self.load_commands_dirty = true;
4794 }
4795
4796 if (self.uuid_cmd_index == null) {
4797 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
4798 var uuid_cmd: macho.uuid_command = .{
4799 .cmdsize = @sizeOf(macho.uuid_command),
4800 .uuid = undefined,
4801 };
4802 std.crypto.random.bytes(&uuid_cmd.uuid);
4803 try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });
4804 self.load_commands_dirty = true;
4805 }
4806
4807 if (self.function_starts_cmd_index == null) {
4808 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
4809 try self.load_commands.append(self.base.allocator, .{
4810 .linkedit_data = .{
4811 .cmd = .FUNCTION_STARTS,
4812 .cmdsize = @sizeOf(macho.linkedit_data_command),
4813 .dataoff = 0,
4814 .datasize = 0,
4815 },
4816 });
4817 self.load_commands_dirty = true;
4818 }
4819
4820 if (self.data_in_code_cmd_index == null) {
4821 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
4822 try self.load_commands.append(self.base.allocator, .{
4823 .linkedit_data = .{
4824 .cmd = .DATA_IN_CODE,
4825 .cmdsize = @sizeOf(macho.linkedit_data_command),
4826 .dataoff = 0,
4827 .datasize = 0,
4828 },
4829 });
4830 self.load_commands_dirty = true;
4831 }
4667 return @intCast(u32, sizeofcmds);
48324668}
48334669
4834fn calcMinHeaderpad(self: *MachO) u64 {
4835 var sizeofcmds: u32 = 0;
4836 for (self.load_commands.items) |lc| {
4837 if (lc.cmd() == .NONE) continue;
4838 sizeofcmds += lc.cmdsize();
4839 }
4840
4841 var padding: u32 = sizeofcmds + (self.base.options.headerpad_size orelse 0);
4670fn calcMinHeaderPad(self: *MachO) !u64 {
4671 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
48424672 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
48434673
48444674 if (self.base.options.headerpad_max_install_names) {
4845 var min_headerpad_size: u32 = 0;
4846 for (self.load_commands.items) |lc| switch (lc.cmd()) {
4847 .ID_DYLIB,
4848 .LOAD_WEAK_DYLIB,
4849 .LOAD_DYLIB,
4850 .REEXPORT_DYLIB,
4851 => {
4852 min_headerpad_size += @sizeOf(macho.dylib_command) + std.os.PATH_MAX + 1;
4853 },
4854
4855 else => {},
4856 };
4675 var min_headerpad_size: u32 = try self.calcLCsSize(true);
48574676 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
48584677 min_headerpad_size + @sizeOf(macho.mach_header_64),
48594678 });
......@@ -4868,32 +4687,31 @@ fn calcMinHeaderpad(self: *MachO) u64 {
48684687fn allocateSegments(self: *MachO) !void {
48694688 try self.allocateSegment(self.text_segment_cmd_index, &.{
48704689 self.pagezero_segment_cmd_index,
4871 }, self.calcMinHeaderpad());
4690 }, try self.calcMinHeaderPad());
48724691
48734692 if (self.text_segment_cmd_index) |index| blk: {
4874 const seg = &self.load_commands.items[index].segment;
4875 if (seg.sections.items.len == 0) break :blk;
4693 const seg = &self.segments.items[index];
4694 if (seg.nsects == 0) break :blk;
48764695
48774696 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
48784697 var min_alignment: u32 = 0;
4879 for (seg.sections.items) |sect| {
4880 const alignment = try math.powi(u32, 2, sect.@"align");
4698 for (self.sections.items(.header)[0..seg.nsects]) |header| {
4699 const alignment = try math.powi(u32, 2, header.@"align");
48814700 min_alignment = math.max(min_alignment, alignment);
48824701 }
48834702
48844703 assert(min_alignment > 0);
4885 const last_sect_idx = seg.sections.items.len - 1;
4886 const last_sect = seg.sections.items[last_sect_idx];
4704 const last_header = self.sections.items(.header)[seg.nsects - 1];
48874705 const shift: u32 = shift: {
4888 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
4706 const diff = seg.filesize - last_header.offset - last_header.size;
48894707 const factor = @divTrunc(diff, min_alignment);
48904708 break :shift @intCast(u32, factor * min_alignment);
48914709 };
48924710
48934711 if (shift > 0) {
4894 for (seg.sections.items) |*sect| {
4895 sect.offset += shift;
4896 sect.addr += shift;
4712 for (self.sections.items(.header)[0..seg.nsects]) |*header| {
4713 header.offset += shift;
4714 header.addr += shift;
48974715 }
48984716 }
48994717 }
......@@ -4917,42 +4735,42 @@ fn allocateSegments(self: *MachO) !void {
49174735 }, 0);
49184736}
49194737
4920fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_size: u64) !void {
4738fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
49214739 const index = maybe_index orelse return;
4922 const seg = &self.load_commands.items[index].segment;
4740 const seg = &self.segments.items[index];
49234741
49244742 const base = self.getSegmentAllocBase(indices);
4925 seg.inner.vmaddr = base.vmaddr;
4926 seg.inner.fileoff = base.fileoff;
4927 seg.inner.filesize = init_size;
4928 seg.inner.vmsize = init_size;
4743 seg.vmaddr = base.vmaddr;
4744 seg.fileoff = base.fileoff;
4745 seg.filesize = init_size;
4746 seg.vmsize = init_size;
49294747
49304748 // Allocate the sections according to their alignment at the beginning of the segment.
49314749 var start = init_size;
4932 for (seg.sections.items) |*sect| {
4933 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
4934 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
4935 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4936 const alignment = try math.powi(u32, 2, sect.@"align");
4750 const slice = self.sections.slice();
4751 for (slice.items(.header)) |*header, sect_id| {
4752 const segment_index = slice.items(.segment_index)[sect_id];
4753 if (segment_index != index) continue;
4754 const is_zerofill = header.flags == macho.S_ZEROFILL or header.flags == macho.S_THREAD_LOCAL_ZEROFILL;
4755 const alignment = try math.powi(u32, 2, header.@"align");
49374756 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
49384757
4939 // TODO handle zerofill sections in stage2
4940 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4758 header.offset = if (is_zerofill)
49414759 0
49424760 else
4943 @intCast(u32, seg.inner.fileoff + start_aligned);
4944 sect.addr = seg.inner.vmaddr + start_aligned;
4761 @intCast(u32, seg.fileoff + start_aligned);
4762 header.addr = seg.vmaddr + start_aligned;
49454763
4946 start = start_aligned + sect.size;
4764 start = start_aligned + header.size;
49474765
4948 if (!(is_zerofill and (use_stage1 or use_llvm))) {
4949 seg.inner.filesize = start;
4766 if (!is_zerofill) {
4767 seg.filesize = start;
49504768 }
4951 seg.inner.vmsize = start;
4769 seg.vmsize = start;
49524770 }
49534771
4954 seg.inner.filesize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
4955 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.vmsize, self.page_size);
4772 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4773 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
49564774}
49574775
49584776const InitSectionOpts = struct {
......@@ -4963,16 +4781,16 @@ const InitSectionOpts = struct {
49634781
49644782fn initSection(
49654783 self: *MachO,
4966 segment_id: u16,
4784 segment_id: u8,
49674785 sectname: []const u8,
49684786 size: u64,
49694787 alignment: u32,
49704788 opts: InitSectionOpts,
4971) !u16 {
4972 const seg = &self.load_commands.items[segment_id].segment;
4973 var sect = macho.section_64{
4789) !u8 {
4790 const seg = &self.segments.items[segment_id];
4791 var header = macho.section_64{
49744792 .sectname = makeStaticString(sectname),
4975 .segname = seg.inner.segname,
4793 .segname = seg.segname,
49764794 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
49774795 .@"align" = alignment,
49784796 .flags = opts.flags,
......@@ -4982,165 +4800,157 @@ fn initSection(
49824800
49834801 if (self.mode == .incremental) {
49844802 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4985 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
4986 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)
4803 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?)
4804 try self.calcMinHeaderPad()
49874805 else
49884806 null;
49894807 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
49904808 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4991 sect.segName(),
4992 sect.sectName(),
4809 header.segName(),
4810 header.sectName(),
49934811 off,
49944812 off + size,
49954813 });
49964814
4997 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
4998
4999 const is_zerofill = opts.flags == macho.S_ZEROFILL or opts.flags == macho.S_THREAD_LOCAL_ZEROFILL;
5000 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
5001 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4815 header.addr = seg.vmaddr + off - seg.fileoff;
50024816
50034817 // TODO handle zerofill in stage2
5004 if (!(is_zerofill and (use_stage1 or use_llvm))) {
5005 sect.offset = @intCast(u32, off);
5006 }
5007 }
4818 // const is_zerofill = opts.flags == macho.S_ZEROFILL or opts.flags == macho.S_THREAD_LOCAL_ZEROFILL;
4819 header.offset = @intCast(u32, off);
50084820
5009 const index = @intCast(u16, seg.sections.items.len);
5010 try seg.sections.append(self.base.allocator, sect);
5011 seg.inner.cmdsize += @sizeOf(macho.section_64);
5012 seg.inner.nsects += 1;
5013
5014 const match = MatchingSection{
5015 .seg = segment_id,
5016 .sect = index,
5017 };
5018 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
5019 try self.atom_free_lists.putNoClobber(self.base.allocator, match, .{});
4821 try self.updateSectionOrdinals();
4822 }
50204823
5021 self.load_commands_dirty = true;
5022 self.sections_order_dirty = true;
4824 const index = @intCast(u8, self.sections.slice().len);
4825 try self.sections.append(self.base.allocator, .{
4826 .segment_index = segment_id,
4827 .header = header,
4828 });
4829 seg.cmdsize += @sizeOf(macho.section_64);
4830 seg.nsects += 1;
50234831
50244832 return index;
50254833}
50264834
5027fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 {
5028 const seg = self.load_commands.items[segment_id].segment;
5029 if (seg.sections.items.len == 0) {
5030 return if (start) |v| v else seg.inner.fileoff;
4835fn findFreeSpace(self: MachO, segment_id: u8, alignment: u64, start: ?u64) u64 {
4836 const seg = self.segments.items[segment_id];
4837 const indexes = self.getSectionIndexes(segment_id);
4838 if (indexes.end - indexes.start == 0) {
4839 return if (start) |v| v else seg.fileoff;
50314840 }
5032 const last_sect = seg.sections.items[seg.sections.items.len - 1];
4841 const last_sect = self.sections.items(.header)[indexes.end - 1];
50334842 const final_off = last_sect.offset + padToIdeal(last_sect.size);
50344843 return mem.alignForwardGeneric(u64, final_off, alignment);
50354844}
50364845
5037fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
5038 const seg = &self.load_commands.items[seg_id].segment;
5039 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
5040 assert(new_seg_size > seg.inner.filesize);
5041 const offset_amt = new_seg_size - seg.inner.filesize;
4846fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
4847 const segment = &self.segments.items[segment_index];
4848 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
4849 assert(new_segment_size > segment.filesize);
4850 const offset_amt = new_segment_size - segment.filesize;
50424851 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
5043 seg.inner.segname,
5044 seg.inner.filesize,
5045 new_seg_size,
4852 segment.segname,
4853 segment.filesize,
4854 new_segment_size,
50464855 });
5047 seg.inner.filesize = new_seg_size;
5048 seg.inner.vmsize = new_seg_size;
4856 segment.filesize = new_segment_size;
4857 segment.vmsize = new_segment_size;
50494858
50504859 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5051 seg.inner.fileoff,
5052 seg.inner.fileoff + seg.inner.filesize,
5053 seg.inner.vmaddr,
5054 seg.inner.vmaddr + seg.inner.vmsize,
4860 segment.fileoff,
4861 segment.fileoff + segment.filesize,
4862 segment.vmaddr,
4863 segment.vmaddr + segment.vmsize,
50554864 });
50564865
5057 var next: usize = seg_id + 1;
4866 var next: u8 = segment_index + 1;
50584867 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
5059 const next_seg = &self.load_commands.items[next].segment;
4868 const next_segment = &self.segments.items[next];
50604869
50614870 try MachO.copyRangeAllOverlappingAlloc(
50624871 self.base.allocator,
50634872 self.base.file.?,
5064 next_seg.inner.fileoff,
5065 next_seg.inner.fileoff + offset_amt,
5066 math.cast(usize, next_seg.inner.filesize) orelse return error.Overflow,
4873 next_segment.fileoff,
4874 next_segment.fileoff + offset_amt,
4875 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
50674876 );
50684877
5069 next_seg.inner.fileoff += offset_amt;
5070 next_seg.inner.vmaddr += offset_amt;
4878 next_segment.fileoff += offset_amt;
4879 next_segment.vmaddr += offset_amt;
50714880
50724881 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5073 next_seg.inner.segname,
5074 next_seg.inner.fileoff,
5075 next_seg.inner.fileoff + next_seg.inner.filesize,
5076 next_seg.inner.vmaddr,
5077 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4882 next_segment.segname,
4883 next_segment.fileoff,
4884 next_segment.fileoff + next_segment.filesize,
4885 next_segment.vmaddr,
4886 next_segment.vmaddr + next_segment.vmsize,
50784887 });
50794888
5080 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
5081 moved_sect.offset += @intCast(u32, offset_amt);
5082 moved_sect.addr += offset_amt;
4889 const indexes = self.getSectionIndexes(next);
4890 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
4891 header.offset += @intCast(u32, offset_amt);
4892 header.addr += offset_amt;
50834893
50844894 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5085 moved_sect.segName(),
5086 moved_sect.sectName(),
5087 moved_sect.offset,
5088 moved_sect.offset + moved_sect.size,
5089 moved_sect.addr,
5090 moved_sect.addr + moved_sect.size,
4895 header.segName(),
4896 header.sectName(),
4897 header.offset,
4898 header.offset + header.size,
4899 header.addr,
4900 header.addr + header.size,
50914901 });
50924902
5093 try self.shiftLocalsByOffset(.{
5094 .seg = @intCast(u16, next),
5095 .sect = @intCast(u16, moved_sect_id),
5096 }, @intCast(i64, offset_amt));
4903 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
50974904 }
50984905 }
50994906}
51004907
5101fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4908fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
51024909 const tracy = trace(@src());
51034910 defer tracy.end();
51044911
5105 const seg = &self.load_commands.items[match.seg].segment;
5106 const sect = &seg.sections.items[match.sect];
4912 const section = self.sections.get(sect_id);
4913 const segment_index = section.segment_index;
4914 const header = section.header;
4915 const segment = self.segments.items[segment_index];
51074916
5108 const alignment = try math.powi(u32, 2, sect.@"align");
5109 const max_size = self.allocatedSize(match.seg, sect.offset);
4917 const alignment = try math.powi(u32, 2, header.@"align");
4918 const max_size = self.allocatedSize(segment_index, header.offset);
51104919 const ideal_size = padToIdeal(new_size);
51114920 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
51124921
51134922 if (needed_size > max_size) blk: {
51144923 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });
51154924
5116 if (match.sect == seg.sections.items.len - 1) {
4925 const indexes = self.getSectionIndexes(segment_index);
4926 if (sect_id == indexes.end - 1) {
51174927 // Last section, just grow segments
5118 try self.growSegment(match.seg, seg.inner.filesize + needed_size - max_size);
4928 try self.growSegment(segment_index, segment.filesize + needed_size - max_size);
51194929 break :blk;
51204930 }
51214931
51224932 // Need to move all sections below in file and address spaces.
51234933 const offset_amt = offset: {
5124 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);
4934 const max_alignment = try self.getSectionMaxAlignment(sect_id + 1, indexes.end);
51254935 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
51264936 };
51274937
51284938 // Before we commit to this, check if the segment needs to grow too.
51294939 // We assume that each section header is growing linearly with the increasing
51304940 // file offset / virtual memory address space.
5131 const last_sect = seg.sections.items[seg.sections.items.len - 1];
5132 const last_sect_off = last_sect.offset + last_sect.size;
5133 const seg_off = seg.inner.fileoff + seg.inner.filesize;
4941 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
4942 const last_sect_off = last_sect_header.offset + last_sect_header.size;
4943 const seg_off = segment.fileoff + segment.filesize;
51344944
51354945 if (last_sect_off + offset_amt > seg_off) {
51364946 // Need to grow segment first.
51374947 const spill_size = (last_sect_off + offset_amt) - seg_off;
5138 try self.growSegment(match.seg, seg.inner.filesize + spill_size);
4948 try self.growSegment(segment_index, segment.filesize + spill_size);
51394949 }
51404950
51414951 // We have enough space to expand within the segment, so move all sections by
51424952 // the required amount and update their header offsets.
5143 const next_sect = seg.sections.items[match.sect + 1];
4953 const next_sect = self.sections.items(.header)[sect_id + 1];
51444954 const total_size = last_sect_off - next_sect.offset;
51454955
51464956 try MachO.copyRangeAllOverlappingAlloc(
......@@ -5151,9 +4961,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51514961 math.cast(usize, total_size) orelse return error.Overflow,
51524962 );
51534963
5154 var next = match.sect + 1;
5155 while (next < seg.sections.items.len) : (next += 1) {
5156 const moved_sect = &seg.sections.items[next];
4964 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
51574965 moved_sect.offset += @intCast(u32, offset_amt);
51584966 moved_sect.addr += offset_amt;
51594967
......@@ -5166,49 +4974,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51664974 moved_sect.addr + moved_sect.size,
51674975 });
51684976
5169 try self.shiftLocalsByOffset(.{
5170 .seg = match.seg,
5171 .sect = next,
5172 }, @intCast(i64, offset_amt));
4977 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
51734978 }
51744979 }
51754980}
51764981
5177fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
5178 const seg = self.load_commands.items[segment_id].segment;
5179 assert(start >= seg.inner.fileoff);
5180 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
4982fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
4983 const segment = self.segments.items[segment_id];
4984 const indexes = self.getSectionIndexes(segment_id);
4985 assert(start >= segment.fileoff);
4986 var min_pos: u64 = segment.fileoff + segment.filesize;
51814987 if (start > min_pos) return 0;
5182 for (seg.sections.items) |section| {
5183 if (section.offset <= start) continue;
5184 if (section.offset < min_pos) min_pos = section.offset;
4988 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4989 if (header.offset <= start) continue;
4990 if (header.offset < min_pos) min_pos = header.offset;
51854991 }
51864992 return min_pos - start;
51874993}
51884994
5189fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
5190 const seg = self.load_commands.items[segment_id].segment;
4995fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
51914996 var max_alignment: u32 = 1;
5192 var next = start_sect_id;
5193 while (next < seg.sections.items.len) : (next += 1) {
5194 const sect = seg.sections.items[next];
5195 const alignment = try math.powi(u32, 2, sect.@"align");
4997 const slice = self.sections.slice();
4998 for (slice.items(.header)[start..end]) |header| {
4999 const alignment = try math.powi(u32, 2, header.@"align");
51965000 max_alignment = math.max(max_alignment, alignment);
51975001 }
51985002 return max_alignment;
51995003}
52005004
5201fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5005fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
52025006 const sym = atom.getSymbolPtr(self);
52035007 if (self.mode == .incremental) {
52045008 const size = atom.size;
52055009 const alignment = try math.powi(u32, 2, atom.alignment);
5206 const vaddr = try self.allocateAtom(atom, size, alignment, match);
5010 const vaddr = try self.allocateAtom(atom, size, alignment, sect_id);
52075011 const sym_name = atom.getName(self);
52085012 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
52095013 sym.n_value = vaddr;
5210 } else try self.addAtomToSection(atom, match);
5211 sym.n_sect = self.getSectionOrdinal(match);
5014 } else try self.addAtomToSection(atom, sect_id);
5015 sym.n_sect = sect_id + 1;
52125016}
52135017
52145018fn allocateAtom(
......@@ -5216,15 +5020,15 @@ fn allocateAtom(
52165020 atom: *Atom,
52175021 new_atom_size: u64,
52185022 alignment: u64,
5219 match: MatchingSection,
5023 sect_id: u8,
52205024) !u64 {
52215025 const tracy = trace(@src());
52225026 defer tracy.end();
52235027
5224 const sect = self.getSectionPtr(match);
5225 var free_list = self.atom_free_lists.get(match).?;
5226 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
5227 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;
5028 const header = &self.sections.items(.header)[sect_id];
5029 const free_list = &self.sections.items(.free_list)[sect_id];
5030 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
5031 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
52285032
52295033 // We use these to indicate our intention to update metadata, placing the new atom,
52305034 // and possibly removing a free list node.
......@@ -5244,7 +5048,7 @@ fn allocateAtom(
52445048 // Is it enough that we could fit this new atom?
52455049 const sym = big_atom.getSymbol(self);
52465050 const capacity = big_atom.capacity(self);
5247 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
5051 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
52485052 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
52495053 const capacity_end_vaddr = sym.n_value + capacity;
52505054 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
......@@ -5272,30 +5076,28 @@ fn allocateAtom(
52725076 free_list_removal = i;
52735077 }
52745078 break :blk new_start_vaddr;
5275 } else if (self.atoms.get(match)) |last| {
5079 } else if (maybe_last_atom.*) |last| {
52765080 const last_symbol = last.getSymbol(self);
5277 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
5081 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
52785082 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
52795083 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
52805084 atom_placement = last;
52815085 break :blk new_start_vaddr;
52825086 } else {
5283 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);
5087 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
52845088 }
52855089 };
52865090
52875091 const expand_section = atom_placement == null or atom_placement.?.next == null;
52885092 if (expand_section) {
5289 const needed_size = @intCast(u32, (vaddr + new_atom_size) - sect.addr);
5290 try self.growSection(match, needed_size);
5291 _ = try self.atoms.put(self.base.allocator, match, atom);
5292 sect.size = needed_size;
5293 self.load_commands_dirty = true;
5093 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
5094 try self.growSection(sect_id, needed_size);
5095 maybe_last_atom.* = atom;
5096 header.size = needed_size;
52945097 }
52955098 const align_pow = @intCast(u32, math.log2(alignment));
5296 if (sect.@"align" < align_pow) {
5297 sect.@"align" = align_pow;
5298 self.load_commands_dirty = true;
5099 if (header.@"align" < align_pow) {
5100 header.@"align" = align_pow;
52995101 }
53005102 atom.size = new_atom_size;
53015103 atom.alignment = align_pow;
......@@ -5322,20 +5124,19 @@ fn allocateAtom(
53225124 return vaddr;
53235125}
53245126
5325pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5326 if (self.atoms.getPtr(match)) |last| {
5327 last.*.next = atom;
5328 atom.prev = last.*;
5329 last.* = atom;
5330 } else {
5331 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5127pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
5128 var section = self.sections.get(sect_id);
5129 if (section.header.size > 0) {
5130 section.last_atom.?.next = atom;
5131 atom.prev = section.last_atom.?;
53325132 }
5333 const sect = self.getSectionPtr(match);
5133 section.last_atom = atom;
53345134 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5335 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5336 const padding = aligned_end_addr - sect.size;
5337 sect.size += padding + atom.size;
5338 sect.@"align" = @maximum(sect.@"align", atom.alignment);
5135 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
5136 const padding = aligned_end_addr - section.header.size;
5137 section.header.size += padding + atom.size;
5138 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
5139 self.sections.set(sect_id, section);
53395140}
53405141
53415142pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
......@@ -5368,74 +5169,27 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
53685169 return sym_index;
53695170}
53705171
5371fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
5172fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
53725173 for (indices) |maybe_prev_id| {
53735174 const prev_id = maybe_prev_id orelse continue;
5374 const prev = self.load_commands.items[prev_id].segment;
5175 const prev = self.segments.items[prev_id];
53755176 return .{
5376 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,
5377 .fileoff = prev.inner.fileoff + prev.inner.filesize,
5177 .vmaddr = prev.vmaddr + prev.vmsize,
5178 .fileoff = prev.fileoff + prev.filesize,
53785179 };
53795180 }
53805181 return .{ .vmaddr = 0, .fileoff = 0 };
53815182}
53825183
5383fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {
5384 const seg_id = maybe_seg_id.* orelse return;
5385
5386 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);
5387 defer mapping.deinit();
5388
5389 const seg = &self.load_commands.items[seg_id].segment;
5390 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5391 defer self.base.allocator.free(sections);
5392 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5393
5394 for (indices) |maybe_index| {
5395 const old_idx = maybe_index.* orelse continue;
5396 const sect = &sections[old_idx];
5397 if (sect.size == 0) {
5398 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5399 maybe_index.* = null;
5400 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5401 seg.inner.nsects -= 1;
5402 } else {
5403 maybe_index.* = @intCast(u16, seg.sections.items.len);
5404 seg.sections.appendAssumeCapacity(sect.*);
5405 }
5406 try mapping.putNoClobber(old_idx, maybe_index.*);
5407 }
5408
5409 var atoms = std.ArrayList(struct { match: MatchingSection, atom: *Atom }).init(self.base.allocator);
5410 defer atoms.deinit();
5411 try atoms.ensureTotalCapacity(mapping.count());
5412
5413 for (mapping.keys()) |old_sect| {
5414 const new_sect = mapping.get(old_sect).? orelse {
5415 _ = self.atoms.remove(.{ .seg = seg_id, .sect = old_sect });
5416 continue;
5417 };
5418 const kv = self.atoms.fetchRemove(.{ .seg = seg_id, .sect = old_sect }).?;
5419 atoms.appendAssumeCapacity(.{
5420 .match = .{ .seg = seg_id, .sect = new_sect },
5421 .atom = kv.value,
5422 });
5423 }
5184fn pruneAndSortSections(self: *MachO) !void {
5185 const gpa = self.base.allocator;
54245186
5425 while (atoms.popOrNull()) |next| {
5426 try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom);
5427 }
5187 var sections = self.sections.toOwnedSlice();
5188 defer sections.deinit(gpa);
5189 try self.sections.ensureTotalCapacity(gpa, sections.len);
54285190
5429 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5430 // Segment has now become empty, so mark it as such
5431 log.debug("marking segment {s} as dead", .{seg.inner.segName()});
5432 seg.inner.cmd = @intToEnum(macho.LC, 0);
5433 maybe_seg_id.* = null;
5434 }
5435}
5436
5437fn pruneAndSortSections(self: *MachO) !void {
5438 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{
5191 for (&[_]*?u8{
5192 // __TEXT
54395193 &self.text_section_index,
54405194 &self.stubs_section_index,
54415195 &self.stub_helper_section_index,
......@@ -5448,9 +5202,7 @@ fn pruneAndSortSections(self: *MachO) !void {
54485202 &self.objc_methtype_section_index,
54495203 &self.objc_classname_section_index,
54505204 &self.eh_frame_section_index,
5451 });
5452
5453 try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{
5205 // __DATA_CONST
54545206 &self.got_section_index,
54555207 &self.mod_init_func_section_index,
54565208 &self.mod_term_func_section_index,
......@@ -5458,9 +5210,7 @@ fn pruneAndSortSections(self: *MachO) !void {
54585210 &self.objc_cfstring_section_index,
54595211 &self.objc_classlist_section_index,
54605212 &self.objc_imageinfo_section_index,
5461 });
5462
5463 try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{
5213 // __DATA
54645214 &self.rustc_section_index,
54655215 &self.la_symbol_ptr_section_index,
54665216 &self.objc_const_section_index,
......@@ -5473,103 +5223,129 @@ fn pruneAndSortSections(self: *MachO) !void {
54735223 &self.tlv_data_section_index,
54745224 &self.tlv_bss_section_index,
54755225 &self.bss_section_index,
5476 });
5477
5478 // Create new section ordinals.
5479 self.section_ordinals.clearRetainingCapacity();
5480 if (self.text_segment_cmd_index) |seg_id| {
5481 const seg = self.load_commands.items[seg_id].segment;
5482 for (seg.sections.items) |_, sect_id| {
5483 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5484 .seg = seg_id,
5485 .sect = @intCast(u16, sect_id),
5486 });
5487 assert(!res.found_existing);
5488 }
5489 }
5490 if (self.data_const_segment_cmd_index) |seg_id| {
5491 const seg = self.load_commands.items[seg_id].segment;
5492 for (seg.sections.items) |_, sect_id| {
5493 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5494 .seg = seg_id,
5495 .sect = @intCast(u16, sect_id),
5226 }) |maybe_index| {
5227 const old_idx = maybe_index.* orelse continue;
5228 const segment_index = sections.items(.segment_index)[old_idx];
5229 const header = sections.items(.header)[old_idx];
5230 const last_atom = sections.items(.last_atom)[old_idx];
5231 if (header.size == 0) {
5232 log.debug("pruning section {s},{s}", .{ header.segName(), header.sectName() });
5233 maybe_index.* = null;
5234 const seg = &self.segments.items[segment_index];
5235 seg.cmdsize -= @sizeOf(macho.section_64);
5236 seg.nsects -= 1;
5237 } else {
5238 maybe_index.* = @intCast(u8, self.sections.slice().len);
5239 self.sections.appendAssumeCapacity(.{
5240 .segment_index = segment_index,
5241 .header = header,
5242 .last_atom = last_atom,
54965243 });
5497 assert(!res.found_existing);
54985244 }
54995245 }
5500 if (self.data_segment_cmd_index) |seg_id| {
5501 const seg = self.load_commands.items[seg_id].segment;
5502 for (seg.sections.items) |_, sect_id| {
5503 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5504 .seg = seg_id,
5505 .sect = @intCast(u16, sect_id),
5506 });
5507 assert(!res.found_existing);
5246
5247 for (self.segments.items) |*seg| {
5248 const segname = seg.segName();
5249 if (seg.nsects == 0 and
5250 !mem.eql(u8, "__TEXT", segname) and
5251 !mem.eql(u8, "__PAGEZERO", segname) and
5252 !mem.eql(u8, "__LINKEDIT", segname))
5253 {
5254 // Segment has now become empty, so mark it as such
5255 log.debug("marking segment {s} as dead", .{seg.segName()});
5256 seg.cmd = @intToEnum(macho.LC, 0);
55085257 }
55095258 }
5510 self.sections_order_dirty = false;
55115259}
55125260
55135261fn updateSectionOrdinals(self: *MachO) !void {
5514 if (!self.sections_order_dirty) return;
5515
5262 _ = self;
55165263 const tracy = trace(@src());
55175264 defer tracy.end();
55185265
5519 log.debug("updating section ordinals", .{});
5520
5521 const gpa = self.base.allocator;
5266 @panic("updating section ordinals");
5267
5268 // const gpa = self.base.allocator;
5269
5270 // var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5271 // defer ordinal_remap.deinit();
5272 // var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
5273
5274 // var new_ordinal: u8 = 0;
5275 // for (&[_]?u16{
5276 // self.text_segment_cmd_index,
5277 // self.data_const_segment_cmd_index,
5278 // self.data_segment_cmd_index,
5279 // }) |maybe_index| {
5280 // const index = maybe_index orelse continue;
5281 // const seg = self.load_commands.items[index].segment;
5282 // for (seg.sections.items) |sect, sect_id| {
5283 // const match = MatchingSection{
5284 // .seg = @intCast(u16, index),
5285 // .sect = @intCast(u16, sect_id),
5286 // };
5287 // const old_ordinal = self.getSectionOrdinal(match);
5288 // new_ordinal += 1;
5289 // log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5290 // sect.segName(),
5291 // sect.sectName(),
5292 // old_ordinal,
5293 // new_ordinal,
5294 // });
5295 // try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5296 // try ordinals.putNoClobber(gpa, match, {});
5297 // }
5298 // }
5299
5300 // // FIXME Jakub
5301 // // TODO no need for duping work here; simply walk the atom graph
5302 // for (self.locals.items) |*sym| {
5303 // if (sym.undf()) continue;
5304 // if (sym.n_sect == 0) continue;
5305 // sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5306 // }
5307 // for (self.objects.items) |*object| {
5308 // for (object.symtab.items) |*sym| {
5309 // if (sym.undf()) continue;
5310 // if (sym.n_sect == 0) continue;
5311 // sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5312 // }
5313 // }
5314
5315 // self.section_ordinals.deinit(gpa);
5316 // self.section_ordinals = ordinals;
5317}
55225318
5523 var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5524 defer ordinal_remap.deinit();
5525 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
5319pub fn writeSegmentHeaders(self: *MachO, start: usize, end: usize, ncmds: *u32, writer: anytype) !void {
5320 var count: usize = 0;
5321 for (self.segments.items[start..end]) |seg| {
5322 if (seg.cmd == .NONE) continue;
5323 try writer.writeStruct(seg);
55265324
5527 var new_ordinal: u8 = 0;
5528 for (&[_]?u16{
5529 self.text_segment_cmd_index,
5530 self.data_const_segment_cmd_index,
5531 self.data_segment_cmd_index,
5532 }) |maybe_index| {
5533 const index = maybe_index orelse continue;
5534 const seg = self.load_commands.items[index].segment;
5535 for (seg.sections.items) |sect, sect_id| {
5536 const match = MatchingSection{
5537 .seg = @intCast(u16, index),
5538 .sect = @intCast(u16, sect_id),
5539 };
5540 const old_ordinal = self.getSectionOrdinal(match);
5541 new_ordinal += 1;
5542 log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5543 sect.segName(),
5544 sect.sectName(),
5545 old_ordinal,
5546 new_ordinal,
5547 });
5548 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5549 try ordinals.putNoClobber(gpa, match, {});
5325 // TODO
5326 for (self.sections.items(.header)[count..][0..seg.nsects]) |header| {
5327 try writer.writeStruct(header);
55505328 }
5551 }
55525329
5553 // FIXME Jakub
5554 // TODO no need for duping work here; simply walk the atom graph
5555 for (self.locals.items) |*sym| {
5556 if (sym.undf()) continue;
5557 if (sym.n_sect == 0) continue;
5558 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5559 }
5560 for (self.objects.items) |*object| {
5561 for (object.symtab.items) |*sym| {
5562 if (sym.undf()) continue;
5563 if (sym.n_sect == 0) continue;
5564 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5565 }
5330 count += seg.nsects;
5331 ncmds.* += 1;
55665332 }
5333}
5334
5335fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5336 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5337 seg.filesize = 0;
5338 seg.vmsize = 0;
55675339
5568 self.section_ordinals.deinit(gpa);
5569 self.section_ordinals = ordinals;
5340 try self.writeDyldInfoData(ncmds, lc_writer);
5341 try self.writeFunctionStarts(ncmds, lc_writer);
5342 try self.writeDataInCode(ncmds, lc_writer);
5343 try self.writeSymtabs(ncmds, lc_writer);
5344
5345 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
55705346}
55715347
5572fn writeDyldInfoData(self: *MachO) !void {
5348fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
55735349 const tracy = trace(@src());
55745350 defer tracy.end();
55755351
......@@ -5582,89 +5358,86 @@ fn writeDyldInfoData(self: *MachO) !void {
55825358 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
55835359 defer lazy_bind_pointers.deinit();
55845360
5585 {
5586 var it = self.atoms.iterator();
5587 while (it.next()) |entry| {
5588 const match = entry.key_ptr.*;
5589 var atom: *Atom = entry.value_ptr.*;
5361 const slice = self.sections.slice();
5362 for (slice.items(.last_atom)) |last_atom, sect_id| {
5363 var atom = last_atom orelse continue;
5364 const segment_index = slice.items(.segment_index)[sect_id];
5365 const header = slice.items(.header)[sect_id];
55905366
5591 if (self.text_segment_cmd_index) |seg| {
5592 if (match.seg == seg) continue; // __TEXT is non-writable
5593 }
5367 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
55945368
5595 const seg = self.getSegment(match);
5596 const sect = self.getSection(match);
5597 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
5369 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
55985370
5599 while (true) {
5600 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5601 const sym = atom.getSymbol(self);
5602 const base_offset = sym.n_value - seg.inner.vmaddr;
5371 const seg = self.segments.items[segment_index];
56035372
5604 for (atom.rebases.items) |offset| {
5605 log.debug(" | rebase at {x}", .{base_offset + offset});
5606 try rebase_pointers.append(.{
5607 .offset = base_offset + offset,
5608 .segment_id = match.seg,
5609 });
5610 }
5373 while (true) {
5374 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5375 const sym = atom.getSymbol(self);
5376 const base_offset = sym.n_value - seg.vmaddr;
56115377
5612 for (atom.bindings.items) |binding| {
5613 const bind_sym = self.getSymbol(binding.target);
5614 const bind_sym_name = self.getSymbolName(binding.target);
5615 const dylib_ordinal = @divTrunc(
5616 @bitCast(i16, bind_sym.n_desc),
5617 macho.N_SYMBOL_RESOLVER,
5618 );
5619 var flags: u4 = 0;
5620 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5621 binding.offset + base_offset,
5622 bind_sym_name,
5623 dylib_ordinal,
5624 });
5625 if (bind_sym.weakRef()) {
5626 log.debug(" | marking as weak ref ", .{});
5627 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5628 }
5629 try bind_pointers.append(.{
5630 .offset = binding.offset + base_offset,
5631 .segment_id = match.seg,
5632 .dylib_ordinal = dylib_ordinal,
5633 .name = bind_sym_name,
5634 .bind_flags = flags,
5635 });
5636 }
5378 for (atom.rebases.items) |offset| {
5379 log.debug(" | rebase at {x}", .{base_offset + offset});
5380 try rebase_pointers.append(.{
5381 .offset = base_offset + offset,
5382 .segment_id = segment_index,
5383 });
5384 }
56375385
5638 for (atom.lazy_bindings.items) |binding| {
5639 const bind_sym = self.getSymbol(binding.target);
5640 const bind_sym_name = self.getSymbolName(binding.target);
5641 const dylib_ordinal = @divTrunc(
5642 @bitCast(i16, bind_sym.n_desc),
5643 macho.N_SYMBOL_RESOLVER,
5644 );
5645 var flags: u4 = 0;
5646 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5647 binding.offset + base_offset,
5648 bind_sym_name,
5649 dylib_ordinal,
5650 });
5651 if (bind_sym.weakRef()) {
5652 log.debug(" | marking as weak ref ", .{});
5653 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5654 }
5655 try lazy_bind_pointers.append(.{
5656 .offset = binding.offset + base_offset,
5657 .segment_id = match.seg,
5658 .dylib_ordinal = dylib_ordinal,
5659 .name = bind_sym_name,
5660 .bind_flags = flags,
5661 });
5386 for (atom.bindings.items) |binding| {
5387 const bind_sym = self.getSymbol(binding.target);
5388 const bind_sym_name = self.getSymbolName(binding.target);
5389 const dylib_ordinal = @divTrunc(
5390 @bitCast(i16, bind_sym.n_desc),
5391 macho.N_SYMBOL_RESOLVER,
5392 );
5393 var flags: u4 = 0;
5394 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5395 binding.offset + base_offset,
5396 bind_sym_name,
5397 dylib_ordinal,
5398 });
5399 if (bind_sym.weakRef()) {
5400 log.debug(" | marking as weak ref ", .{});
5401 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
56625402 }
5403 try bind_pointers.append(.{
5404 .offset = binding.offset + base_offset,
5405 .segment_id = segment_index,
5406 .dylib_ordinal = dylib_ordinal,
5407 .name = bind_sym_name,
5408 .bind_flags = flags,
5409 });
5410 }
56635411
5664 if (atom.prev) |prev| {
5665 atom = prev;
5666 } else break;
5412 for (atom.lazy_bindings.items) |binding| {
5413 const bind_sym = self.getSymbol(binding.target);
5414 const bind_sym_name = self.getSymbolName(binding.target);
5415 const dylib_ordinal = @divTrunc(
5416 @bitCast(i16, bind_sym.n_desc),
5417 macho.N_SYMBOL_RESOLVER,
5418 );
5419 var flags: u4 = 0;
5420 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5421 binding.offset + base_offset,
5422 bind_sym_name,
5423 dylib_ordinal,
5424 });
5425 if (bind_sym.weakRef()) {
5426 log.debug(" | marking as weak ref ", .{});
5427 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5428 }
5429 try lazy_bind_pointers.append(.{
5430 .offset = binding.offset + base_offset,
5431 .segment_id = segment_index,
5432 .dylib_ordinal = dylib_ordinal,
5433 .name = bind_sym_name,
5434 .bind_flags = flags,
5435 });
56675436 }
5437
5438 if (atom.prev) |prev| {
5439 atom = prev;
5440 } else break;
56685441 }
56695442 }
56705443
......@@ -5675,8 +5448,8 @@ fn writeDyldInfoData(self: *MachO) !void {
56755448 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
56765449 log.debug("generating export trie", .{});
56775450
5678 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5679 const base_address = text_segment.inner.vmaddr;
5451 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5452 const base_address = text_segment.vmaddr;
56805453
56815454 if (self.base.options.output_mode == .Exe) {
56825455 for (&[_]SymbolWithLoc{
......@@ -5714,48 +5487,27 @@ fn writeDyldInfoData(self: *MachO) !void {
57145487 try trie.finalize(gpa);
57155488 }
57165489
5717 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5718 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;
5719
5720 const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64));
5490 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5491 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
5492 assert(rebase_off == link_seg.fileoff);
57215493 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5722 dyld_info.rebase_off = @intCast(u32, rebase_off);
5723 dyld_info.rebase_size = @intCast(u32, rebase_size);
5724 log.debug("writing rebase info from 0x{x} to 0x{x}", .{
5725 dyld_info.rebase_off,
5726 dyld_info.rebase_off + dyld_info.rebase_size,
5727 });
5494 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
57285495
5729 const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64));
5496 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
57305497 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5731 dyld_info.bind_off = @intCast(u32, bind_off);
5732 dyld_info.bind_size = @intCast(u32, bind_size);
5733 log.debug("writing bind info from 0x{x} to 0x{x}", .{
5734 dyld_info.bind_off,
5735 dyld_info.bind_off + dyld_info.bind_size,
5736 });
5498 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
57375499
5738 const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64));
5500 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
57395501 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
5740 dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off);
5741 dyld_info.lazy_bind_size = @intCast(u32, lazy_bind_size);
5742 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
5743 dyld_info.lazy_bind_off,
5744 dyld_info.lazy_bind_off + dyld_info.lazy_bind_size,
5745 });
5502 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
57465503
5747 const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64));
5504 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
57485505 const export_size = trie.size;
5749 dyld_info.export_off = @intCast(u32, export_off);
5750 dyld_info.export_size = @intCast(u32, export_size);
5751 log.debug("writing export trie from 0x{x} to 0x{x}", .{
5752 dyld_info.export_off,
5753 dyld_info.export_off + dyld_info.export_size,
5754 });
5506 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
57555507
5756 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
5508 const needed_size = export_off + export_size - rebase_off;
5509 link_seg.filesize = needed_size;
57575510
5758 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;
57595511 var buffer = try gpa.alloc(u8, needed_size);
57605512 defer gpa.free(buffer);
57615513 mem.set(u8, buffer, 0);
......@@ -5763,54 +5515,61 @@ fn writeDyldInfoData(self: *MachO) !void {
57635515 var stream = std.io.fixedBufferStream(buffer);
57645516 const writer = stream.writer();
57655517
5766 const base_off = dyld_info.rebase_off;
57675518 try bind.writeRebaseInfo(rebase_pointers.items, writer);
5768 try stream.seekTo(dyld_info.bind_off - base_off);
5519 try stream.seekTo(bind_off - rebase_off);
57695520
57705521 try bind.writeBindInfo(bind_pointers.items, writer);
5771 try stream.seekTo(dyld_info.lazy_bind_off - base_off);
5522 try stream.seekTo(lazy_bind_off - rebase_off);
57725523
57735524 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
5774 try stream.seekTo(dyld_info.export_off - base_off);
5525 try stream.seekTo(export_off - rebase_off);
57755526
57765527 _ = try trie.write(writer);
57775528
57785529 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
5779 dyld_info.rebase_off,
5780 dyld_info.rebase_off + needed_size,
5530 rebase_off,
5531 rebase_off + needed_size,
57815532 });
57825533
5783 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
5784 try self.populateLazyBindOffsetsInStubHelper(
5785 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],
5786 );
5787
5788 self.load_commands_dirty = true;
5534 try self.base.file.?.pwriteAll(buffer, rebase_off);
5535 try self.populateLazyBindOffsetsInStubHelper(buffer[lazy_bind_off - rebase_off ..][0..lazy_bind_size]);
5536
5537 try lc_writer.writeStruct(macho.dyld_info_command{
5538 .cmd = .DYLD_INFO_ONLY,
5539 .cmdsize = @sizeOf(macho.dyld_info_command),
5540 .rebase_off = @intCast(u32, rebase_off),
5541 .rebase_size = @intCast(u32, rebase_size),
5542 .bind_off = @intCast(u32, bind_off),
5543 .bind_size = @intCast(u32, bind_size),
5544 .weak_bind_off = 0,
5545 .weak_bind_size = 0,
5546 .lazy_bind_off = @intCast(u32, lazy_bind_off),
5547 .lazy_bind_size = @intCast(u32, lazy_bind_size),
5548 .export_off = @intCast(u32, export_off),
5549 .export_size = @intCast(u32, export_size),
5550 });
5551 ncmds.* += 1;
57895552}
57905553
57915554fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
57925555 const gpa = self.base.allocator;
5793 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
5556
57945557 const stub_helper_section_index = self.stub_helper_section_index orelse return;
5795 const last_atom = self.atoms.get(.{
5796 .seg = text_segment_cmd_index,
5797 .sect = stub_helper_section_index,
5798 }) orelse return;
57995558 if (self.stub_helper_preamble_atom == null) return;
5800 if (last_atom == self.stub_helper_preamble_atom.?) return;
5559
5560 const section = self.sections.get(stub_helper_section_index);
5561 const last_atom = section.last_atom orelse return;
5562 if (last_atom == self.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
58015563
58025564 var table = std.AutoHashMap(i64, *Atom).init(gpa);
58035565 defer table.deinit();
58045566
58055567 {
58065568 var stub_atom = last_atom;
5807 var laptr_atom = self.atoms.get(.{
5808 .seg = self.data_segment_cmd_index.?,
5809 .sect = self.la_symbol_ptr_section_index.?,
5810 }).?;
5569 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
58115570 const base_addr = blk: {
5812 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
5813 break :blk seg.inner.vmaddr;
5571 const seg = self.segments.items[self.data_segment_cmd_index.?];
5572 break :blk seg.vmaddr;
58145573 };
58155574
58165575 while (true) {
......@@ -5871,10 +5630,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58715630 }
58725631 }
58735632
5874 const sect = self.getSection(.{
5875 .seg = text_segment_cmd_index,
5876 .sect = stub_helper_section_index,
5877 });
5633 const header = self.sections.items(.header)[stub_helper_section_index];
58785634 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
58795635 .x86_64 => 1,
58805636 .aarch64 => 2 * @sizeOf(u32),
......@@ -5886,7 +5642,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58865642 while (offsets.popOrNull()) |bind_offset| {
58875643 const atom = table.get(bind_offset.sym_offset).?;
58885644 const sym = atom.getSymbol(self);
5889 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
5645 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
58905646 mem.writeIntLittle(u32, &buf, bind_offset.offset);
58915647 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
58925648 bind_offset.offset,
......@@ -5899,14 +5655,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58995655
59005656const asc_u64 = std.sort.asc(u64);
59015657
5902fn writeFunctionStarts(self: *MachO) !void {
5903 const text_seg_index = self.text_segment_cmd_index orelse return;
5904 const text_sect_index = self.text_section_index orelse return;
5905 const text_seg = self.load_commands.items[text_seg_index].segment;
5906
5658fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59075659 const tracy = trace(@src());
59085660 defer tracy.end();
59095661
5662 const text_seg_index = self.text_segment_cmd_index orelse return;
5663 const text_sect_index = self.text_section_index orelse return;
5664 const text_seg = self.segments.items[text_seg_index];
5665
59105666 const gpa = self.base.allocator;
59115667
59125668 // We need to sort by address first
......@@ -5918,8 +5674,8 @@ fn writeFunctionStarts(self: *MachO) !void {
59185674 const sym = self.getSymbol(global);
59195675 if (sym.undf()) continue;
59205676 if (sym.n_desc == N_DESC_GCED) continue;
5921 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5922 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;
5677 const sect_id = sym.n_sect - 1;
5678 if (sect_id != text_sect_index) continue;
59235679
59245680 addresses.appendAssumeCapacity(sym.n_value);
59255681 }
......@@ -5932,7 +5688,7 @@ fn writeFunctionStarts(self: *MachO) !void {
59325688
59335689 var last_off: u32 = 0;
59345690 for (addresses.items) |addr| {
5935 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);
5691 const offset = @intCast(u32, addr - text_seg.vmaddr);
59365692 const diff = offset - last_off;
59375693
59385694 if (diff == 0) continue;
......@@ -5951,22 +5707,22 @@ fn writeFunctionStarts(self: *MachO) !void {
59515707 try std.leb.writeULEB128(buffer.writer(), offset);
59525708 }
59535709
5954 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5955 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
5710 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5711 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
5712 const needed_size = buffer.items.len;
5713 link_seg.filesize = offset + needed_size - link_seg.fileoff;
59565714
5957 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
5958 const datasize = buffer.items.len;
5959 fn_cmd.dataoff = @intCast(u32, dataoff);
5960 fn_cmd.datasize = @intCast(u32, datasize);
5961 seg.inner.filesize = fn_cmd.dataoff + fn_cmd.datasize - seg.inner.fileoff;
5715 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
59625716
5963 log.debug("writing function starts info from 0x{x} to 0x{x}", .{
5964 fn_cmd.dataoff,
5965 fn_cmd.dataoff + fn_cmd.datasize,
5966 });
5717 try self.base.file.?.pwriteAll(buffer.items, offset);
59675718
5968 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);
5969 self.load_commands_dirty = true;
5719 try lc_writer.writeStruct(macho.linkedit_data_command{
5720 .cmd = .FUNCTION_STARTS,
5721 .cmdsize = @sizeOf(macho.linkedit_data_command),
5722 .dataoff = @intCast(u32, offset),
5723 .datasize = @intCast(u32, needed_size),
5724 });
5725 ncmds.* += 1;
59705726}
59715727
59725728fn filterDataInCode(
......@@ -5988,17 +5744,15 @@ fn filterDataInCode(
59885744 return dices[start..end];
59895745}
59905746
5991fn writeDataInCode(self: *MachO) !void {
5747fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59925748 const tracy = trace(@src());
59935749 defer tracy.end();
59945750
59955751 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
59965752 defer out_dice.deinit();
59975753
5998 const text_sect = self.getSection(.{
5999 .seg = self.text_segment_cmd_index orelse return,
6000 .sect = self.text_section_index orelse return,
6001 });
5754 const text_sect_id = self.text_section_index orelse return;
5755 const text_sect_header = self.sections.items(.header)[text_sect_id];
60025756
60035757 for (self.objects.items) |object| {
60045758 const dice = object.parseDataInCode() orelse continue;
......@@ -6008,15 +5762,15 @@ fn writeDataInCode(self: *MachO) !void {
60085762 const sym = atom.getSymbol(self);
60095763 if (sym.n_desc == N_DESC_GCED) continue;
60105764
6011 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
6012 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {
5765 const sect_id = sym.n_sect - 1;
5766 if (sect_id != self.text_section_index.?) {
60135767 continue;
60145768 }
60155769
60165770 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
60175771 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
60185772 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
6019 const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse
5773 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
60205774 return error.Overflow;
60215775
60225776 for (filtered_dice) |single| {
......@@ -6030,33 +5784,63 @@ fn writeDataInCode(self: *MachO) !void {
60305784 }
60315785 }
60325786
6033 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6034 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
5787 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5788 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5789 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
5790 seg.filesize = offset + needed_size - seg.fileoff;
60355791
6036 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6037 const datasize = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
6038 dice_cmd.dataoff = @intCast(u32, dataoff);
6039 dice_cmd.datasize = @intCast(u32, datasize);
6040 seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff;
5792 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
60415793
6042 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
6043 dice_cmd.dataoff,
6044 dice_cmd.dataoff + dice_cmd.datasize,
5794 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
5795 try lc_writer.writeStruct(macho.linkedit_data_command{
5796 .cmd = .DATA_IN_CODE,
5797 .cmdsize = @sizeOf(macho.linkedit_data_command),
5798 .dataoff = @intCast(u32, offset),
5799 .datasize = @intCast(u32, needed_size),
60455800 });
6046
6047 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6048 self.load_commands_dirty = true;
5801 ncmds.* += 1;
60495802}
60505803
6051fn writeSymtab(self: *MachO) !void {
6052 const tracy = trace(@src());
6053 defer tracy.end();
5804fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5805 var symtab_cmd = macho.symtab_command{
5806 .cmdsize = @sizeOf(macho.symtab_command),
5807 .symoff = 0,
5808 .nsyms = 0,
5809 .stroff = 0,
5810 .strsize = 0,
5811 };
5812 var dysymtab_cmd = macho.dysymtab_command{
5813 .cmdsize = @sizeOf(macho.dysymtab_command),
5814 .ilocalsym = 0,
5815 .nlocalsym = 0,
5816 .iextdefsym = 0,
5817 .nextdefsym = 0,
5818 .iundefsym = 0,
5819 .nundefsym = 0,
5820 .tocoff = 0,
5821 .ntoc = 0,
5822 .modtaboff = 0,
5823 .nmodtab = 0,
5824 .extrefsymoff = 0,
5825 .nextrefsyms = 0,
5826 .indirectsymoff = 0,
5827 .nindirectsyms = 0,
5828 .extreloff = 0,
5829 .nextrel = 0,
5830 .locreloff = 0,
5831 .nlocrel = 0,
5832 };
5833 var ctx = try self.writeSymtab(&symtab_cmd);
5834 defer ctx.imports_table.deinit();
5835 try self.writeDysymtab(ctx, &dysymtab_cmd);
5836 try self.writeStrtab(&symtab_cmd);
5837 try lc_writer.writeStruct(symtab_cmd);
5838 try lc_writer.writeStruct(dysymtab_cmd);
5839 ncmds.* += 2;
5840}
60545841
5842fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
60555843 const gpa = self.base.allocator;
6056 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6057 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6058 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));
6059 symtab.symoff = @intCast(u32, symoff);
60605844
60615845 var locals = std.ArrayList(macho.nlist_64).init(gpa);
60625846 defer locals.deinit();
......@@ -6101,8 +5885,8 @@ fn writeSymtab(self: *MachO) !void {
61015885
61025886 var imports = std.ArrayList(macho.nlist_64).init(gpa);
61035887 defer imports.deinit();
5888
61045889 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6105 defer imports_table.deinit();
61065890
61075891 for (self.globals.values()) |global| {
61085892 const sym = self.getSymbol(global);
......@@ -6115,56 +5899,84 @@ fn writeSymtab(self: *MachO) !void {
61155899 try imports_table.putNoClobber(global, new_index);
61165900 }
61175901
6118 const nlocals = locals.items.len;
6119 const nexports = exports.items.len;
6120 const nimports = imports.items.len;
6121 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);
5902 const nlocals = @intCast(u32, locals.items.len);
5903 const nexports = @intCast(u32, exports.items.len);
5904 const nimports = @intCast(u32, imports.items.len);
5905 const nsyms = nlocals + nexports + nimports;
5906
5907 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5908 const offset = mem.alignForwardGeneric(
5909 u64,
5910 seg.fileoff + seg.filesize,
5911 @alignOf(macho.nlist_64),
5912 );
5913 const needed_size = nsyms * @sizeOf(macho.nlist_64);
5914 seg.filesize = offset + needed_size - seg.fileoff;
61225915
61235916 var buffer = std.ArrayList(u8).init(gpa);
61245917 defer buffer.deinit();
6125 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
5918 try buffer.ensureTotalCapacityPrecise(needed_size);
61265919 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
61275920 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
61285921 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
61295922
6130 log.debug("writing symtab from 0x{x} to 0x{x}", .{ symtab.symoff, symtab.symoff + buffer.items.len });
6131 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);
5923 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5924 try self.base.file.?.pwriteAll(buffer.items, offset);
5925
5926 lc.symoff = @intCast(u32, offset);
5927 lc.nsyms = nsyms;
61325928
6133 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
5929 return SymtabCtx{
5930 .nlocalsym = nlocals,
5931 .nextdefsym = nexports,
5932 .nundefsym = nimports,
5933 .imports_table = imports_table,
5934 };
5935}
61345936
6135 // Update dynamic symbol table.
6136 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
6137 dysymtab.nlocalsym = @intCast(u32, nlocals);
6138 dysymtab.iextdefsym = dysymtab.nlocalsym;
6139 dysymtab.nextdefsym = @intCast(u32, nexports);
6140 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6141 dysymtab.nundefsym = @intCast(u32, nimports);
5937fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
5938 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5939 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5940 const needed_size = self.strtab.buffer.items.len;
5941 seg.filesize = offset + needed_size - seg.fileoff;
61425942
5943 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5944
5945 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
5946
5947 lc.stroff = @intCast(u32, offset);
5948 lc.strsize = @intCast(u32, needed_size);
5949}
5950
5951const SymtabCtx = struct {
5952 nlocalsym: u32,
5953 nextdefsym: u32,
5954 nundefsym: u32,
5955 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
5956};
5957
5958fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
5959 const gpa = self.base.allocator;
61435960 const nstubs = @intCast(u32, self.stubs_table.count());
61445961 const ngot_entries = @intCast(u32, self.got_entries_table.count());
5962 const nindirectsyms = nstubs * 2 + ngot_entries;
5963 const iextdefsym = ctx.nlocalsym;
5964 const iundefsym = iextdefsym + ctx.nextdefsym;
61455965
6146 const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6147 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);
6148 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
5966 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5967 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5968 const needed_size = nindirectsyms * @sizeOf(u32);
5969 seg.filesize = offset + needed_size - seg.fileoff;
61495970
6150 seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff;
6151
6152 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
6153 dysymtab.indirectsymoff,
6154 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6155 });
5971 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
61565972
61575973 var buf = std.ArrayList(u8).init(gpa);
61585974 defer buf.deinit();
6159 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
5975 try buf.ensureTotalCapacity(needed_size);
61605976 const writer = buf.writer();
61615977
6162 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6163 const stubs_section_index = self.stubs_section_index orelse break :blk;
6164 const stubs = self.getSectionPtr(.{
6165 .seg = text_segment_cmd_index,
6166 .sect = stubs_section_index,
6167 });
5978 if (self.stubs_section_index) |sect_id| {
5979 const stubs = &self.sections.items(.header)[sect_id];
61685980 stubs.reserved1 = 0;
61695981 for (self.stubs.items) |entry| {
61705982 if (entry.sym_index == 0) continue;
......@@ -6172,16 +5984,12 @@ fn writeSymtab(self: *MachO) !void {
61725984 if (atom_sym.n_desc == N_DESC_GCED) continue;
61735985 const target_sym = self.getSymbol(entry.target);
61745986 assert(target_sym.undf());
6175 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5987 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61765988 }
61775989 }
61785990
6179 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6180 const got_section_index = self.got_section_index orelse break :blk;
6181 const got = self.getSectionPtr(.{
6182 .seg = data_const_segment_cmd_index,
6183 .sect = got_section_index,
6184 });
5991 if (self.got_section_index) |sect_id| {
5992 const got = &self.sections.items(.header)[sect_id];
61855993 got.reserved1 = nstubs;
61865994 for (self.got_entries.items) |entry| {
61875995 if (entry.sym_index == 0) continue;
......@@ -6189,19 +5997,15 @@ fn writeSymtab(self: *MachO) !void {
61895997 if (atom_sym.n_desc == N_DESC_GCED) continue;
61905998 const target_sym = self.getSymbol(entry.target);
61915999 if (target_sym.undf()) {
6192 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6000 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61936001 } else {
61946002 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
61956003 }
61966004 }
61976005 }
61986006
6199 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6200 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6201 const la_symbol_ptr = self.getSectionPtr(.{
6202 .seg = data_segment_cmd_index,
6203 .sect = la_symbol_ptr_section_index,
6204 });
6007 if (self.la_symbol_ptr_section_index) |sect_id| {
6008 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
62056009 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
62066010 for (self.stubs.items) |entry| {
62076011 if (entry.sym_index == 0) continue;
......@@ -6209,131 +6013,76 @@ fn writeSymtab(self: *MachO) !void {
62096013 if (atom_sym.n_desc == N_DESC_GCED) continue;
62106014 const target_sym = self.getSymbol(entry.target);
62116015 assert(target_sym.undf());
6212 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6016 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
62136017 }
62146018 }
62156019
6216 assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32));
6217
6218 try self.base.file.?.pwriteAll(buf.items, dysymtab.indirectsymoff);
6219 self.load_commands_dirty = true;
6220}
6221
6222fn writeStrtab(self: *MachO) !void {
6223 const tracy = trace(@src());
6224 defer tracy.end();
6225
6226 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6227 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6228 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6229
6230 const strsize = self.strtab.buffer.items.len;
6231 symtab.stroff = @intCast(u32, stroff);
6232 symtab.strsize = @intCast(u32, strsize);
6233 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;
6020 assert(buf.items.len == needed_size);
6021 try self.base.file.?.pwriteAll(buf.items, offset);
62346022
6235 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
6236
6237 try self.base.file.?.pwriteAll(self.strtab.buffer.items, symtab.stroff);
6238
6239 self.load_commands_dirty = true;
6240}
6241
6242fn writeLinkeditSegment(self: *MachO) !void {
6243 const tracy = trace(@src());
6244 defer tracy.end();
6245
6246 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6247 seg.inner.filesize = 0;
6248
6249 try self.writeDyldInfoData();
6250 try self.writeFunctionStarts();
6251 try self.writeDataInCode();
6252 try self.writeSymtab();
6253 try self.writeStrtab();
6254
6255 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6023 lc.nlocalsym = ctx.nlocalsym;
6024 lc.iextdefsym = iextdefsym;
6025 lc.nextdefsym = ctx.nextdefsym;
6026 lc.iundefsym = iundefsym;
6027 lc.nundefsym = ctx.nundefsym;
6028 lc.indirectsymoff = @intCast(u32, offset);
6029 lc.nindirectsyms = nindirectsyms;
62566030}
62576031
6258fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
6259 const tracy = trace(@src());
6260 defer tracy.end();
6261
6262 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6263 const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
6032fn writeCodeSignaturePadding(
6033 self: *MachO,
6034 code_sig: *CodeSignature,
6035 ncmds: *u32,
6036 lc_writer: anytype,
6037) !u32 {
6038 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
62646039 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
62656040 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
6266 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, 16);
6267 const datasize = code_sig.estimateSize(dataoff);
6268 cs_cmd.dataoff = @intCast(u32, dataoff);
6269 cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff));
6270
6271 // Advance size of __LINKEDIT segment
6272 seg.inner.filesize = cs_cmd.dataoff + cs_cmd.datasize - seg.inner.fileoff;
6273 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6274 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ dataoff, dataoff + datasize });
6041 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
6042 const needed_size = code_sig.estimateSize(offset);
6043 seg.filesize = offset + needed_size - seg.fileoff;
6044 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
6045 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
62756046 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
62766047 // except for code signature data.
6277 try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1);
6278 self.load_commands_dirty = true;
6279}
6048 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
62806049
6281fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
6282 const tracy = trace(@src());
6283 defer tracy.end();
6050 try lc_writer.writeStruct(macho.linkedit_data_command{
6051 .cmd = .CODE_SIGNATURE,
6052 .cmdsize = @sizeOf(macho.linkedit_data_command),
6053 .dataoff = @intCast(u32, offset),
6054 .datasize = @intCast(u32, needed_size),
6055 });
6056 ncmds.* += 1;
6057
6058 return @intCast(u32, offset);
6059}
62846060
6285 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
6286 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6061fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
6062 const seg = self.segments.items[self.text_segment_cmd_index.?];
62876063
62886064 var buffer = std.ArrayList(u8).init(self.base.allocator);
62896065 defer buffer.deinit();
62906066 try buffer.ensureTotalCapacityPrecise(code_sig.size());
62916067 try code_sig.writeAdhocSignature(self.base.allocator, .{
62926068 .file = self.base.file.?,
6293 .exec_seg_base = seg.inner.fileoff,
6294 .exec_seg_limit = seg.inner.filesize,
6295 .code_sig_cmd = code_sig_cmd,
6069 .exec_seg_base = seg.fileoff,
6070 .exec_seg_limit = seg.filesize,
6071 .file_size = offset,
62966072 .output_mode = self.base.options.output_mode,
62976073 }, buffer.writer());
62986074 assert(buffer.items.len == code_sig.size());
62996075
63006076 log.debug("writing code signature from 0x{x} to 0x{x}", .{
6301 code_sig_cmd.dataoff,
6302 code_sig_cmd.dataoff + buffer.items.len,
6077 offset,
6078 offset + buffer.items.len,
63036079 });
63046080
6305 try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff);
6306}
6307
6308/// Writes all load commands and section headers.
6309fn writeLoadCommands(self: *MachO) !void {
6310 if (!self.load_commands_dirty) return;
6311
6312 var sizeofcmds: u32 = 0;
6313 for (self.load_commands.items) |lc| {
6314 if (lc.cmd() == .NONE) continue;
6315 sizeofcmds += lc.cmdsize();
6316 }
6317
6318 var buffer = try self.base.allocator.alloc(u8, sizeofcmds);
6319 defer self.base.allocator.free(buffer);
6320 var fib = std.io.fixedBufferStream(buffer);
6321 const writer = fib.writer();
6322 for (self.load_commands.items) |lc| {
6323 if (lc.cmd() == .NONE) continue;
6324 try lc.write(writer);
6325 }
6326
6327 const off = @sizeOf(macho.mach_header_64);
6328
6329 log.debug("writing load commands from 0x{x} to 0x{x}", .{ off, off + sizeofcmds });
6330
6331 try self.base.file.?.pwriteAll(buffer, off);
6332 self.load_commands_dirty = false;
6081 try self.base.file.?.pwriteAll(buffer.items, offset);
63336082}
63346083
63356084/// Writes Mach-O file header.
6336fn writeHeader(self: *MachO) !void {
6085fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
63376086 var header: macho.mach_header_64 = .{};
63386087 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
63396088
......@@ -6365,14 +6114,8 @@ fn writeHeader(self: *MachO) !void {
63656114 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
63666115 }
63676116
6368 header.ncmds = 0;
6369 header.sizeofcmds = 0;
6370
6371 for (self.load_commands.items) |cmd| {
6372 if (cmd.cmd() == .NONE) continue;
6373 header.sizeofcmds += cmd.cmdsize();
6374 header.ncmds += 1;
6375 }
6117 header.ncmds = ncmds;
6118 header.sizeofcmds = sizeofcmds;
63766119
63776120 log.debug("writing Mach-O header {}", .{header});
63786121
......@@ -6392,33 +6135,13 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
63926135 return buf;
63936136}
63946137
6395pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {
6396 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;
6397}
6398
6399pub fn getMatchingSectionFromOrdinal(self: *MachO, ord: u8) MatchingSection {
6400 const index = ord - 1;
6401 assert(index < self.section_ordinals.count());
6402 return self.section_ordinals.keys()[index];
6403}
6404
6405pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
6406 assert(match.seg < self.load_commands.items.len);
6407 return &self.load_commands.items[match.seg].segment;
6408}
6409
6410pub fn getSegment(self: *MachO, match: MatchingSection) macho.SegmentCommand {
6411 return self.getSegmentPtr(match).*;
6412}
6413
6414pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {
6415 const seg = self.getSegmentPtr(match);
6416 assert(match.sect < seg.sections.items.len);
6417 return &seg.sections.items[match.sect];
6418}
6419
6420pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6421 return self.getSectionPtr(match).*;
6138fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
6139 var start: u8 = 0;
6140 const nsects = for (self.segments.items) |seg, i| {
6141 if (i == segment_index) break @intCast(u8, seg.nsects);
6142 start += @intCast(u8, seg.nsects);
6143 } else 0;
6144 return .{ .start = start, .end = start + nsects };
64226145}
64236146
64246147pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
......@@ -6512,72 +6235,6 @@ pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate:
65126235 return i;
65136236}
65146237
6515const DebugInfo = struct {
6516 inner: dwarf.DwarfInfo,
6517 debug_info: []const u8,
6518 debug_abbrev: []const u8,
6519 debug_str: []const u8,
6520 debug_line: []const u8,
6521 debug_line_str: []const u8,
6522 debug_ranges: []const u8,
6523
6524 pub fn parse(allocator: Allocator, object: Object) !?DebugInfo {
6525 var debug_info = blk: {
6526 const index = object.dwarf_debug_info_index orelse return null;
6527 break :blk try object.getSectionContents(index);
6528 };
6529 var debug_abbrev = blk: {
6530 const index = object.dwarf_debug_abbrev_index orelse return null;
6531 break :blk try object.getSectionContents(index);
6532 };
6533 var debug_str = blk: {
6534 const index = object.dwarf_debug_str_index orelse return null;
6535 break :blk try object.getSectionContents(index);
6536 };
6537 var debug_line = blk: {
6538 const index = object.dwarf_debug_line_index orelse return null;
6539 break :blk try object.getSectionContents(index);
6540 };
6541 var debug_line_str = blk: {
6542 if (object.dwarf_debug_line_str_index) |ind| {
6543 break :blk try object.getSectionContents(ind);
6544 }
6545 break :blk &[0]u8{};
6546 };
6547 var debug_ranges = blk: {
6548 if (object.dwarf_debug_ranges_index) |ind| {
6549 break :blk try object.getSectionContents(ind);
6550 }
6551 break :blk &[0]u8{};
6552 };
6553
6554 var inner: dwarf.DwarfInfo = .{
6555 .endian = .Little,
6556 .debug_info = debug_info,
6557 .debug_abbrev = debug_abbrev,
6558 .debug_str = debug_str,
6559 .debug_line = debug_line,
6560 .debug_line_str = debug_line_str,
6561 .debug_ranges = debug_ranges,
6562 };
6563 try dwarf.openDwarfDebugInfo(&inner, allocator);
6564
6565 return DebugInfo{
6566 .inner = inner,
6567 .debug_info = debug_info,
6568 .debug_abbrev = debug_abbrev,
6569 .debug_str = debug_str,
6570 .debug_line = debug_line,
6571 .debug_line_str = debug_line_str,
6572 .debug_ranges = debug_ranges,
6573 };
6574 }
6575
6576 pub fn deinit(self: *DebugInfo, allocator: Allocator) void {
6577 self.inner.deinit(allocator);
6578 }
6579};
6580
65816238pub fn generateSymbolStabs(
65826239 self: *MachO,
65836240 object: Object,
......@@ -6585,14 +6242,15 @@ pub fn generateSymbolStabs(
65856242) !void {
65866243 assert(!self.base.options.strip);
65876244
6588 const gpa = self.base.allocator;
6589
65906245 log.debug("parsing debug info in '{s}'", .{object.name});
65916246
6592 var debug_info = (try DebugInfo.parse(gpa, object)) orelse return;
6247 const gpa = self.base.allocator;
6248 var debug_info = try object.parseDwarfInfo();
6249 defer debug_info.deinit(gpa);
6250 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
65936251
65946252 // We assume there is only one CU.
6595 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
6253 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
65966254 error.MissingDebugInfo => {
65976255 // TODO audit cases with missing debug info and audit our dwarf.zig module.
65986256 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
......@@ -6600,8 +6258,8 @@ pub fn generateSymbolStabs(
66006258 },
66016259 else => |e| return e,
66026260 };
6603 const tu_name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);
6604 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);
6261 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name);
6262 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir);
66056263
66066264 // Open scope
66076265 try locals.ensureUnusedCapacity(3);
......@@ -6664,7 +6322,7 @@ pub fn generateSymbolStabs(
66646322fn generateSymbolStabsForSymbol(
66656323 self: *MachO,
66666324 sym_loc: SymbolWithLoc,
6667 debug_info: DebugInfo,
6325 debug_info: dwarf.DwarfInfo,
66686326 buf: *[4]macho.nlist_64,
66696327) ![]const macho.nlist_64 {
66706328 const gpa = self.base.allocator;
......@@ -6679,7 +6337,7 @@ fn generateSymbolStabsForSymbol(
66796337 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
66806338 const size: ?u64 = size: {
66816339 if (source_sym.tentative()) break :size null;
6682 for (debug_info.inner.func_list.items) |func| {
6340 for (debug_info.func_list.items) |func| {
66836341 if (func.pc_range) |range| {
66846342 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
66856343 break :size range.end - range.start;
......@@ -6731,260 +6389,260 @@ fn generateSymbolStabsForSymbol(
67316389 }
67326390}
67336391
6734fn snapshotState(self: *MachO) !void {
6735 const emit = self.base.options.emit orelse {
6736 log.debug("no emit directory found; skipping snapshot...", .{});
6737 return;
6738 };
6739
6740 const Snapshot = struct {
6741 const Node = struct {
6742 const Tag = enum {
6743 section_start,
6744 section_end,
6745 atom_start,
6746 atom_end,
6747 relocation,
6748
6749 pub fn jsonStringify(
6750 tag: Tag,
6751 options: std.json.StringifyOptions,
6752 out_stream: anytype,
6753 ) !void {
6754 _ = options;
6755 switch (tag) {
6756 .section_start => try out_stream.writeAll("\"section_start\""),
6757 .section_end => try out_stream.writeAll("\"section_end\""),
6758 .atom_start => try out_stream.writeAll("\"atom_start\""),
6759 .atom_end => try out_stream.writeAll("\"atom_end\""),
6760 .relocation => try out_stream.writeAll("\"relocation\""),
6761 }
6762 }
6763 };
6764 const Payload = struct {
6765 name: []const u8 = "",
6766 aliases: [][]const u8 = &[0][]const u8{},
6767 is_global: bool = false,
6768 target: u64 = 0,
6769 };
6770 address: u64,
6771 tag: Tag,
6772 payload: Payload,
6773 };
6774 timestamp: i128,
6775 nodes: []Node,
6776 };
6777
6778 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6779 defer arena_allocator.deinit();
6780 const arena = arena_allocator.allocator();
6781
6782 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6783 .truncate = false,
6784 .read = true,
6785 });
6786 defer out_file.close();
6787
6788 if (out_file.seekFromEnd(-1)) {
6789 try out_file.writer().writeByte(',');
6790 } else |err| switch (err) {
6791 error.Unseekable => try out_file.writer().writeByte('['),
6792 else => |e| return e,
6793 }
6794 const writer = out_file.writer();
6795
6796 var snapshot = Snapshot{
6797 .timestamp = std.time.nanoTimestamp(),
6798 .nodes = undefined,
6799 };
6800 var nodes = std.ArrayList(Snapshot.Node).init(arena);
6801
6802 for (self.section_ordinals.keys()) |key| {
6803 const sect = self.getSection(key);
6804 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6805 try nodes.append(.{
6806 .address = sect.addr,
6807 .tag = .section_start,
6808 .payload = .{ .name = sect_name },
6809 });
6810
6811 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6812
6813 var atom: *Atom = self.atoms.get(key) orelse {
6814 try nodes.append(.{
6815 .address = sect.addr + sect.size,
6816 .tag = .section_end,
6817 .payload = .{},
6818 });
6819 continue;
6820 };
6821
6822 while (atom.prev) |prev| {
6823 atom = prev;
6824 }
6825
6826 while (true) {
6827 const atom_sym = atom.getSymbol(self);
6828 var node = Snapshot.Node{
6829 .address = atom_sym.n_value,
6830 .tag = .atom_start,
6831 .payload = .{
6832 .name = atom.getName(self),
6833 .is_global = self.globals.contains(atom.getName(self)),
6834 },
6835 };
6836
6837 var aliases = std.ArrayList([]const u8).init(arena);
6838 for (atom.contained.items) |sym_off| {
6839 if (sym_off.offset == 0) {
6840 try aliases.append(self.getSymbolName(.{
6841 .sym_index = sym_off.sym_index,
6842 .file = atom.file,
6843 }));
6844 }
6845 }
6846 node.payload.aliases = aliases.toOwnedSlice();
6847 try nodes.append(node);
6848
6849 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6850 for (atom.relocs.items) |rel| {
6851 const source_addr = blk: {
6852 const source_sym = atom.getSymbol(self);
6853 break :blk source_sym.n_value + rel.offset;
6854 };
6855 const target_addr = blk: {
6856 const target_atom = rel.getTargetAtom(self) orelse {
6857 // If there is no atom for target, we still need to check for special, atom-less
6858 // symbols such as `___dso_handle`.
6859 const target_name = self.getSymbolName(rel.target);
6860 if (self.globals.contains(target_name)) {
6861 const atomless_sym = self.getSymbol(rel.target);
6862 break :blk atomless_sym.n_value;
6863 }
6864 break :blk 0;
6865 };
6866 const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6867 self.getSymbol(rel.target)
6868 else
6869 target_atom.getSymbol(self);
6870 const base_address: u64 = if (is_tlv) base_address: {
6871 const sect_id: u16 = sect_id: {
6872 if (self.tlv_data_section_index) |i| {
6873 break :sect_id i;
6874 } else if (self.tlv_bss_section_index) |i| {
6875 break :sect_id i;
6876 } else unreachable;
6877 };
6878 break :base_address self.getSection(.{
6879 .seg = self.data_segment_cmd_index.?,
6880 .sect = sect_id,
6881 }).addr;
6882 } else 0;
6883 break :blk target_sym.n_value - base_address;
6884 };
6885
6886 relocs.appendAssumeCapacity(.{
6887 .address = source_addr,
6888 .tag = .relocation,
6889 .payload = .{ .target = target_addr },
6890 });
6891 }
6892
6893 if (atom.contained.items.len == 0) {
6894 try nodes.appendSlice(relocs.items);
6895 } else {
6896 // Need to reverse iteration order of relocs since by default for relocatable sources
6897 // they come in reverse. For linking, this doesn't matter in any way, however, for
6898 // arranging the memoryline for displaying it does.
6899 std.mem.reverse(Snapshot.Node, relocs.items);
6900
6901 var next_i: usize = 0;
6902 var last_rel: usize = 0;
6903 while (next_i < atom.contained.items.len) : (next_i += 1) {
6904 const loc = SymbolWithLoc{
6905 .sym_index = atom.contained.items[next_i].sym_index,
6906 .file = atom.file,
6907 };
6908 const cont_sym = self.getSymbol(loc);
6909 const cont_sym_name = self.getSymbolName(loc);
6910 var contained_node = Snapshot.Node{
6911 .address = cont_sym.n_value,
6912 .tag = .atom_start,
6913 .payload = .{
6914 .name = cont_sym_name,
6915 .is_global = self.globals.contains(cont_sym_name),
6916 },
6917 };
6918
6919 // Accumulate aliases
6920 var inner_aliases = std.ArrayList([]const u8).init(arena);
6921 while (true) {
6922 if (next_i + 1 >= atom.contained.items.len) break;
6923 const next_sym_loc = SymbolWithLoc{
6924 .sym_index = atom.contained.items[next_i + 1].sym_index,
6925 .file = atom.file,
6926 };
6927 const next_sym = self.getSymbol(next_sym_loc);
6928 if (next_sym.n_value != cont_sym.n_value) break;
6929 const next_sym_name = self.getSymbolName(next_sym_loc);
6930 if (self.globals.contains(next_sym_name)) {
6931 try inner_aliases.append(contained_node.payload.name);
6932 contained_node.payload.name = next_sym_name;
6933 contained_node.payload.is_global = true;
6934 } else try inner_aliases.append(next_sym_name);
6935 next_i += 1;
6936 }
6937
6938 const cont_size = if (next_i + 1 < atom.contained.items.len)
6939 self.getSymbol(.{
6940 .sym_index = atom.contained.items[next_i + 1].sym_index,
6941 .file = atom.file,
6942 }).n_value - cont_sym.n_value
6943 else
6944 atom_sym.n_value + atom.size - cont_sym.n_value;
6945
6946 contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6947 try nodes.append(contained_node);
6948
6949 for (relocs.items[last_rel..]) |rel| {
6950 if (rel.address >= cont_sym.n_value + cont_size) {
6951 break;
6952 }
6953 try nodes.append(rel);
6954 last_rel += 1;
6955 }
6956
6957 try nodes.append(.{
6958 .address = cont_sym.n_value + cont_size,
6959 .tag = .atom_end,
6960 .payload = .{},
6961 });
6962 }
6963 }
6964
6965 try nodes.append(.{
6966 .address = atom_sym.n_value + atom.size,
6967 .tag = .atom_end,
6968 .payload = .{},
6969 });
6970
6971 if (atom.next) |next| {
6972 atom = next;
6973 } else break;
6974 }
6975
6976 try nodes.append(.{
6977 .address = sect.addr + sect.size,
6978 .tag = .section_end,
6979 .payload = .{},
6980 });
6981 }
6982
6983 snapshot.nodes = nodes.toOwnedSlice();
6984
6985 try std.json.stringify(snapshot, .{}, writer);
6986 try writer.writeByte(']');
6987}
6392// fn snapshotState(self: *MachO) !void {
6393// const emit = self.base.options.emit orelse {
6394// log.debug("no emit directory found; skipping snapshot...", .{});
6395// return;
6396// };
6397
6398// const Snapshot = struct {
6399// const Node = struct {
6400// const Tag = enum {
6401// section_start,
6402// section_end,
6403// atom_start,
6404// atom_end,
6405// relocation,
6406
6407// pub fn jsonStringify(
6408// tag: Tag,
6409// options: std.json.StringifyOptions,
6410// out_stream: anytype,
6411// ) !void {
6412// _ = options;
6413// switch (tag) {
6414// .section_start => try out_stream.writeAll("\"section_start\""),
6415// .section_end => try out_stream.writeAll("\"section_end\""),
6416// .atom_start => try out_stream.writeAll("\"atom_start\""),
6417// .atom_end => try out_stream.writeAll("\"atom_end\""),
6418// .relocation => try out_stream.writeAll("\"relocation\""),
6419// }
6420// }
6421// };
6422// const Payload = struct {
6423// name: []const u8 = "",
6424// aliases: [][]const u8 = &[0][]const u8{},
6425// is_global: bool = false,
6426// target: u64 = 0,
6427// };
6428// address: u64,
6429// tag: Tag,
6430// payload: Payload,
6431// };
6432// timestamp: i128,
6433// nodes: []Node,
6434// };
6435
6436// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6437// defer arena_allocator.deinit();
6438// const arena = arena_allocator.allocator();
6439
6440// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6441// .truncate = false,
6442// .read = true,
6443// });
6444// defer out_file.close();
6445
6446// if (out_file.seekFromEnd(-1)) {
6447// try out_file.writer().writeByte(',');
6448// } else |err| switch (err) {
6449// error.Unseekable => try out_file.writer().writeByte('['),
6450// else => |e| return e,
6451// }
6452// const writer = out_file.writer();
6453
6454// var snapshot = Snapshot{
6455// .timestamp = std.time.nanoTimestamp(),
6456// .nodes = undefined,
6457// };
6458// var nodes = std.ArrayList(Snapshot.Node).init(arena);
6459
6460// for (self.section_ordinals.keys()) |key| {
6461// const sect = self.getSection(key);
6462// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6463// try nodes.append(.{
6464// .address = sect.addr,
6465// .tag = .section_start,
6466// .payload = .{ .name = sect_name },
6467// });
6468
6469// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6470
6471// var atom: *Atom = self.atoms.get(key) orelse {
6472// try nodes.append(.{
6473// .address = sect.addr + sect.size,
6474// .tag = .section_end,
6475// .payload = .{},
6476// });
6477// continue;
6478// };
6479
6480// while (atom.prev) |prev| {
6481// atom = prev;
6482// }
6483
6484// while (true) {
6485// const atom_sym = atom.getSymbol(self);
6486// var node = Snapshot.Node{
6487// .address = atom_sym.n_value,
6488// .tag = .atom_start,
6489// .payload = .{
6490// .name = atom.getName(self),
6491// .is_global = self.globals.contains(atom.getName(self)),
6492// },
6493// };
6494
6495// var aliases = std.ArrayList([]const u8).init(arena);
6496// for (atom.contained.items) |sym_off| {
6497// if (sym_off.offset == 0) {
6498// try aliases.append(self.getSymbolName(.{
6499// .sym_index = sym_off.sym_index,
6500// .file = atom.file,
6501// }));
6502// }
6503// }
6504// node.payload.aliases = aliases.toOwnedSlice();
6505// try nodes.append(node);
6506
6507// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6508// for (atom.relocs.items) |rel| {
6509// const source_addr = blk: {
6510// const source_sym = atom.getSymbol(self);
6511// break :blk source_sym.n_value + rel.offset;
6512// };
6513// const target_addr = blk: {
6514// const target_atom = rel.getTargetAtom(self) orelse {
6515// // If there is no atom for target, we still need to check for special, atom-less
6516// // symbols such as `___dso_handle`.
6517// const target_name = self.getSymbolName(rel.target);
6518// if (self.globals.contains(target_name)) {
6519// const atomless_sym = self.getSymbol(rel.target);
6520// break :blk atomless_sym.n_value;
6521// }
6522// break :blk 0;
6523// };
6524// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6525// self.getSymbol(rel.target)
6526// else
6527// target_atom.getSymbol(self);
6528// const base_address: u64 = if (is_tlv) base_address: {
6529// const sect_id: u16 = sect_id: {
6530// if (self.tlv_data_section_index) |i| {
6531// break :sect_id i;
6532// } else if (self.tlv_bss_section_index) |i| {
6533// break :sect_id i;
6534// } else unreachable;
6535// };
6536// break :base_address self.getSection(.{
6537// .seg = self.data_segment_cmd_index.?,
6538// .sect = sect_id,
6539// }).addr;
6540// } else 0;
6541// break :blk target_sym.n_value - base_address;
6542// };
6543
6544// relocs.appendAssumeCapacity(.{
6545// .address = source_addr,
6546// .tag = .relocation,
6547// .payload = .{ .target = target_addr },
6548// });
6549// }
6550
6551// if (atom.contained.items.len == 0) {
6552// try nodes.appendSlice(relocs.items);
6553// } else {
6554// // Need to reverse iteration order of relocs since by default for relocatable sources
6555// // they come in reverse. For linking, this doesn't matter in any way, however, for
6556// // arranging the memoryline for displaying it does.
6557// std.mem.reverse(Snapshot.Node, relocs.items);
6558
6559// var next_i: usize = 0;
6560// var last_rel: usize = 0;
6561// while (next_i < atom.contained.items.len) : (next_i += 1) {
6562// const loc = SymbolWithLoc{
6563// .sym_index = atom.contained.items[next_i].sym_index,
6564// .file = atom.file,
6565// };
6566// const cont_sym = self.getSymbol(loc);
6567// const cont_sym_name = self.getSymbolName(loc);
6568// var contained_node = Snapshot.Node{
6569// .address = cont_sym.n_value,
6570// .tag = .atom_start,
6571// .payload = .{
6572// .name = cont_sym_name,
6573// .is_global = self.globals.contains(cont_sym_name),
6574// },
6575// };
6576
6577// // Accumulate aliases
6578// var inner_aliases = std.ArrayList([]const u8).init(arena);
6579// while (true) {
6580// if (next_i + 1 >= atom.contained.items.len) break;
6581// const next_sym_loc = SymbolWithLoc{
6582// .sym_index = atom.contained.items[next_i + 1].sym_index,
6583// .file = atom.file,
6584// };
6585// const next_sym = self.getSymbol(next_sym_loc);
6586// if (next_sym.n_value != cont_sym.n_value) break;
6587// const next_sym_name = self.getSymbolName(next_sym_loc);
6588// if (self.globals.contains(next_sym_name)) {
6589// try inner_aliases.append(contained_node.payload.name);
6590// contained_node.payload.name = next_sym_name;
6591// contained_node.payload.is_global = true;
6592// } else try inner_aliases.append(next_sym_name);
6593// next_i += 1;
6594// }
6595
6596// const cont_size = if (next_i + 1 < atom.contained.items.len)
6597// self.getSymbol(.{
6598// .sym_index = atom.contained.items[next_i + 1].sym_index,
6599// .file = atom.file,
6600// }).n_value - cont_sym.n_value
6601// else
6602// atom_sym.n_value + atom.size - cont_sym.n_value;
6603
6604// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6605// try nodes.append(contained_node);
6606
6607// for (relocs.items[last_rel..]) |rel| {
6608// if (rel.address >= cont_sym.n_value + cont_size) {
6609// break;
6610// }
6611// try nodes.append(rel);
6612// last_rel += 1;
6613// }
6614
6615// try nodes.append(.{
6616// .address = cont_sym.n_value + cont_size,
6617// .tag = .atom_end,
6618// .payload = .{},
6619// });
6620// }
6621// }
6622
6623// try nodes.append(.{
6624// .address = atom_sym.n_value + atom.size,
6625// .tag = .atom_end,
6626// .payload = .{},
6627// });
6628
6629// if (atom.next) |next| {
6630// atom = next;
6631// } else break;
6632// }
6633
6634// try nodes.append(.{
6635// .address = sect.addr + sect.size,
6636// .tag = .section_end,
6637// .payload = .{},
6638// });
6639// }
6640
6641// snapshot.nodes = nodes.toOwnedSlice();
6642
6643// try std.json.stringify(snapshot, .{}, writer);
6644// try writer.writeByte(']');
6645// }
69886646
69896647fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
69906648 mem.set(u8, buf[0..4], '_');
......@@ -7104,26 +6762,19 @@ fn logSymtab(self: *MachO) void {
71046762 }
71056763}
71066764
7107fn logSectionOrdinals(self: *MachO) void {
7108 for (self.section_ordinals.keys()) |match, i| {
7109 const sect = self.getSection(match);
7110 log.debug("sect({d}, '{s},{s}')", .{ i + 1, sect.segName(), sect.sectName() });
7111 }
7112}
7113
71146765fn logAtoms(self: *MachO) void {
71156766 log.debug("atoms:", .{});
7116 var it = self.atoms.iterator();
7117 while (it.next()) |entry| {
7118 const match = entry.key_ptr.*;
7119 var atom = entry.value_ptr.*;
6767
6768 const slice = self.sections.slice();
6769 for (slice.items(.last_atom)) |last, i| {
6770 var atom = last orelse continue;
6771 const header = slice.items(.header)[i];
71206772
71216773 while (atom.prev) |prev| {
71226774 atom = prev;
71236775 }
71246776
7125 const sect = self.getSection(match);
7126 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
6777 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
71276778
71286779 while (true) {
71296780 self.logAtom(atom);
src/link/MachO/Archive.zig+33-25
......@@ -6,19 +6,14 @@ const fs = std.fs;
66const log = std.log.scoped(.link);
77const macho = std.macho;
88const mem = std.mem;
9const fat = @import("fat.zig");
109
1110const Allocator = mem.Allocator;
1211const Object = @import("Object.zig");
1312
1413file: fs.File,
14fat_offset: u64,
1515name: []const u8,
16
17header: ?ar_hdr = null,
18
19// The actual contents we care about linking with will be embedded at
20// an offset within a file if we are linking against a fat lib
21library_offset: u64 = 0,
16header: ar_hdr = undefined,
2217
2318/// Parsed table of contents.
2419/// Each symbol name points to a list of all definition
......@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
10398 allocator.free(self.name);
10499}
105100
106pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
107 const reader = self.file.reader();
108 self.library_offset = try fat.getLibraryOffset(reader, cpu_arch);
109 try self.file.seekTo(self.library_offset);
110
101pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
111102 const magic = try reader.readBytesNoEof(SARMAG);
112103 if (!mem.eql(u8, &magic, ARMAG)) {
113104 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
......@@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
115106 }
116107
117108 self.header = try reader.readStruct(ar_hdr);
118 if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
119 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
109 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
110 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
111 ARFMAG,
112 self.header.ar_fmag,
113 });
120114 return error.NotArchive;
121115 }
122116
123 var embedded_name = try parseName(allocator, self.header.?, reader);
117 const name_or_length = try self.header.nameOrLength();
118 var embedded_name = try parseName(allocator, name_or_length, reader);
124119 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
125120 defer allocator.free(embedded_name);
126121
127122 try self.parseTableOfContents(allocator, reader);
128 try reader.context.seekTo(0);
129123}
130124
131fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {
132 const name_or_length = try header.nameOrLength();
125fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
133126 var name: []u8 = undefined;
134127 switch (name_or_length) {
135128 .Name => |n| {
......@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
187180 }
188181}
189182
190pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, offset: u32) !Object {
183pub fn parseObject(
184 self: Archive,
185 allocator: Allocator,
186 cpu_arch: std.Target.Cpu.Arch,
187 offset: u32,
188) !Object {
191189 const reader = self.file.reader();
192 try reader.context.seekTo(offset + self.library_offset);
190 try reader.context.seekTo(self.fat_offset + offset);
193191
194192 const object_header = try reader.readStruct(ar_hdr);
195193
......@@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
198196 return error.MalformedArchive;
199197 }
200198
201 const object_name = try parseName(allocator, object_header, reader);
199 const name_or_length = try object_header.nameOrLength();
200 const object_name = try parseName(allocator, name_or_length, reader);
202201 defer allocator.free(object_name);
203202
204203 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
......@@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
209208 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
210209 };
211210
211 const object_name_len = switch (name_or_length) {
212 .Name => 0,
213 .Length => |len| len,
214 };
215 const object_size = (try object_header.size()) - object_name_len;
216 const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
217 const amt = try reader.readAll(contents);
218 if (amt != object_size) {
219 return error.InputOutput;
220 }
221
212222 var object = Object{
213 .file = try fs.cwd().openFile(self.name, .{}),
214223 .name = name,
215 .file_offset = @intCast(u32, try reader.context.getPos()),
216 .mtime = try self.header.?.date(),
224 .mtime = try self.header.date(),
225 .contents = contents,
217226 };
218227
219228 try object.parse(allocator, cpu_arch);
220 try reader.context.seekTo(0);
221229
222230 return object;
223231}
src/link/MachO/Atom.zig+18-17
......@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
246246 else => {
247247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
248248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
249 log.err(" found {}", .{next});
249 log.err(" found {s}", .{@tagName(next)});
250250 return error.UnexpectedRelocationType;
251251 },
252252 }
......@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
285285 else => {
286286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
287287 log.err(" expected ARM64_RELOC_UNSIGNED", .{});
288 log.err(" found {}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
288 log.err(" found {s}", .{
289 @tagName(@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)),
290 });
289291 return error.UnexpectedRelocationType;
290292 },
291293 },
......@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
294296 else => {
295297 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
296298 log.err(" expected X86_64_RELOC_UNSIGNED", .{});
297 log.err(" found {}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
299 log.err(" found {s}", .{
300 @tagName(@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)),
301 });
298302 return error.UnexpectedRelocationType;
299303 },
300304 },
......@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
309313 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
310314 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
311315 const sect = object.getSourceSection(sect_id);
312 const match = (try context.macho_file.getMatchingSection(sect)) orelse
316 const match = (try context.macho_file.getOutputSection(sect)) orelse
313317 unreachable;
314318 const sym_index = @intCast(u32, object.symtab.items.len);
315319 try object.symtab.append(gpa, .{
316320 .n_strx = 0,
317321 .n_type = macho.N_SECT,
318 .n_sect = context.macho_file.getSectionOrdinal(match),
322 .n_sect = match + 1,
319323 .n_desc = 0,
320324 .n_value = sect.addr,
321325 });
......@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
459463 });
460464 } else {
461465 const source_sym = self.getSymbol(context.macho_file);
462 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
463 const sect = context.macho_file.getSection(match);
464 const sect_type = sect.type_();
466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
467 const header = section.header;
468 const segment_index = section.segment_index;
469 const sect_type = header.type_();
465470
466471 const should_rebase = rebase: {
467472 if (rel.r_length != 3) break :rebase false;
......@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
470475 // that the segment is writable should be enough here.
471476 const is_right_segment = blk: {
472477 if (context.macho_file.data_segment_cmd_index) |idx| {
473 if (match.seg == idx) {
478 if (segment_index == idx) {
474479 break :blk true;
475480 }
476481 }
477482 if (context.macho_file.data_const_segment_cmd_index) |idx| {
478 if (match.seg == idx) {
483 if (segment_index == idx) {
479484 break :blk true;
480485 }
481486 }
......@@ -565,9 +570,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
565570 };
566571 const is_tlv = is_tlv: {
567572 const source_sym = self.getSymbol(macho_file);
568 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
569 const sect = macho_file.getSection(match);
570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
573 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
574 break :is_tlv header.type_() == macho.S_THREAD_LOCAL_VARIABLES;
571575 };
572576 const target_addr = blk: {
573577 const target_atom = rel.getTargetAtom(macho_file) orelse {
......@@ -608,10 +612,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
608612 return error.FailedToResolveRelocationTarget;
609613 }
610614 };
611 break :base_address macho_file.getSection(.{
612 .seg = macho_file.data_segment_cmd_index.?,
613 .sect = sect_id,
614 }).addr;
615 break :base_address macho_file.sections.items(.header)[sect_id].addr;
615616 } else 0;
616617 break :blk target_sym.n_value - base_address;
617618 };
src/link/MachO/CodeSignature.zig+7-5
......@@ -252,7 +252,7 @@ pub const WriteOpts = struct {
252252 file: fs.File,
253253 exec_seg_base: u64,
254254 exec_seg_limit: u64,
255 code_sig_cmd: macho.linkedit_data_command,
255 file_size: u32,
256256 output_mode: std.builtin.OutputMode,
257257};
258258
......@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
274274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
275275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
276276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
277 const file_size = opts.code_sig_cmd.dataoff;
278 self.code_directory.inner.codeLimit = file_size;
277 self.code_directory.inner.codeLimit = opts.file_size;
279278
280 const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size;
279 const total_pages = mem.alignForward(opts.file_size, self.page_size) / self.page_size;
281280
282281 var buffer = try allocator.alloc(u8, self.page_size);
283282 defer allocator.free(buffer);
......@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
289288 var i: usize = 0;
290289 while (i < total_pages) : (i += 1) {
291290 const fstart = i * self.page_size;
292 const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size;
291 const fsize = if (fstart + self.page_size > opts.file_size)
292 opts.file_size - fstart
293 else
294 self.page_size;
293295 const len = try opts.file.preadAll(buffer, fstart);
294296 assert(fsize <= len);
295297
src/link/MachO/DebugSymbols.zig+174-336
......@@ -25,35 +25,18 @@ base: *MachO,
2525dwarf: Dwarf,
2626file: fs.File,
2727
28/// Table of all load commands
29load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
30/// __PAGEZERO segment
31pagezero_segment_cmd_index: ?u16 = null,
32/// __TEXT segment
33text_segment_cmd_index: ?u16 = null,
34/// __DATA_CONST segment
35data_const_segment_cmd_index: ?u16 = null,
36/// __DATA segment
37data_segment_cmd_index: ?u16 = null,
38/// __LINKEDIT segment
39linkedit_segment_cmd_index: ?u16 = null,
40/// __DWARF segment
41dwarf_segment_cmd_index: ?u16 = null,
42/// Symbol table
43symtab_cmd_index: ?u16 = null,
44/// UUID load command
45uuid_cmd_index: ?u16 = null,
46
47/// Index into __TEXT,__text section.
48text_section_index: ?u16 = null,
49
50debug_info_section_index: ?u16 = null,
51debug_abbrev_section_index: ?u16 = null,
52debug_str_section_index: ?u16 = null,
53debug_aranges_section_index: ?u16 = null,
54debug_line_section_index: ?u16 = null,
55
56load_commands_dirty: bool = false,
28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
29sections: std.ArrayListUnmanaged(macho.section_64) = .{},
30
31linkedit_segment_cmd_index: ?u8 = null,
32dwarf_segment_cmd_index: ?u8 = null,
33
34debug_info_section_index: ?u8 = null,
35debug_abbrev_section_index: ?u8 = null,
36debug_str_section_index: ?u8 = null,
37debug_aranges_section_index: ?u8 = null,
38debug_line_section_index: ?u8 = null,
39
5740debug_string_table_dirty: bool = false,
5841debug_abbrev_section_dirty: bool = false,
5942debug_aranges_section_dirty: bool = false,
......@@ -78,98 +61,40 @@ pub const Reloc = struct {
7861/// You must call this function *after* `MachO.populateMissingMetadata()`
7962/// has been called to get a viable debug symbols output.
8063pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
81 if (self.uuid_cmd_index == null) {
82 const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
83 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
84 try self.load_commands.append(allocator, base_cmd);
85 self.load_commands_dirty = true;
86 }
87
88 if (self.symtab_cmd_index == null) {
89 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
90 try self.load_commands.append(self.base.base.allocator, .{
91 .symtab = .{
92 .cmdsize = @sizeOf(macho.symtab_command),
93 .symoff = 0,
94 .nsyms = 0,
95 .stroff = 0,
96 .strsize = 0,
97 },
98 });
99 try self.strtab.buffer.append(allocator, 0);
100 self.load_commands_dirty = true;
101 }
102
103 if (self.pagezero_segment_cmd_index == null) {
104 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
105 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
106 const cmd = try self.copySegmentCommand(allocator, base_cmd);
107 try self.load_commands.append(allocator, .{ .segment = cmd });
108 self.load_commands_dirty = true;
109 }
110
111 if (self.text_segment_cmd_index == null) {
112 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
113 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
114 const cmd = try self.copySegmentCommand(allocator, base_cmd);
115 try self.load_commands.append(allocator, .{ .segment = cmd });
116 self.load_commands_dirty = true;
117 }
118
119 if (self.data_const_segment_cmd_index == null) outer: {
120 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
121 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
122 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].segment;
123 const cmd = try self.copySegmentCommand(allocator, base_cmd);
124 try self.load_commands.append(allocator, .{ .segment = cmd });
125 self.load_commands_dirty = true;
126 }
127
128 if (self.data_segment_cmd_index == null) outer: {
129 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
130 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
131 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].segment;
132 const cmd = try self.copySegmentCommand(allocator, base_cmd);
133 try self.load_commands.append(allocator, .{ .segment = cmd });
134 self.load_commands_dirty = true;
135 }
136
13764 if (self.linkedit_segment_cmd_index == null) {
138 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
139 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
140 var cmd = try self.copySegmentCommand(allocator, base_cmd);
65 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
14166 // TODO this needs reworking
142 cmd.inner.vmsize = self.base.page_size;
143 cmd.inner.fileoff = self.base.page_size;
144 cmd.inner.filesize = self.base.page_size;
145 try self.load_commands.append(allocator, .{ .segment = cmd });
146 self.load_commands_dirty = true;
67 try self.segments.append(allocator, .{
68 .segname = makeStaticString("__LINKEDIT"),
69 .vmaddr = self.base.page_size,
70 .vmsize = self.base.page_size,
71 .fileoff = self.base.page_size,
72 .filesize = self.base.page_size,
73 .maxprot = macho.PROT.READ,
74 .initprot = macho.PROT.READ,
75 .cmdsize = @sizeOf(macho.segment_command_64),
76 });
14777 }
14878
14979 if (self.dwarf_segment_cmd_index == null) {
150 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
80 self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
15181
152 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
82 const linkedit = self.segments.items[self.base.linkedit_segment_cmd_index.?];
15383 const ideal_size: u16 = 200 + 128 + 160 + 250;
15484 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
155 const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;
156 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
85 const fileoff = linkedit.fileoff + linkedit.filesize;
86 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
15787
15888 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
15989
160 try self.load_commands.append(allocator, .{
161 .segment = .{
162 .inner = .{
163 .segname = makeStaticString("__DWARF"),
164 .vmaddr = vmaddr,
165 .vmsize = needed_size,
166 .fileoff = fileoff,
167 .filesize = needed_size,
168 .cmdsize = @sizeOf(macho.segment_command_64),
169 },
170 },
90 try self.segments.append(allocator, .{
91 .segname = makeStaticString("__DWARF"),
92 .vmaddr = vmaddr,
93 .vmsize = needed_size,
94 .fileoff = fileoff,
95 .filesize = needed_size,
96 .cmdsize = @sizeOf(macho.segment_command_64),
17197 });
172 self.load_commands_dirty = true;
17398 }
17499
175100 if (self.debug_str_section_index == null) {
......@@ -203,18 +128,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
203128 }
204129}
205130
206fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
207 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
131fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
132 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
208133 var sect = macho.section_64{
209134 .sectname = makeStaticString(sectname),
210 .segname = seg.inner.segname,
135 .segname = segment.segname,
211136 .size = @intCast(u32, size),
212137 .@"align" = alignment,
213138 };
214139 const alignment_pow_2 = try math.powi(u32, 2, alignment);
215140 const off = self.findFreeSpace(size, alignment_pow_2);
216141
217 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
142 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
218143
219144 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
220145 sect.segName(),
......@@ -223,31 +148,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
223148 off + size,
224149 });
225150
226 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
151 sect.addr = segment.vmaddr + off - segment.fileoff;
227152 sect.offset = @intCast(u32, off);
228153
229 const index = @intCast(u16, seg.sections.items.len);
230 try seg.sections.append(self.base.base.allocator, sect);
231 seg.inner.cmdsize += @sizeOf(macho.section_64);
232 seg.inner.nsects += 1;
233
234 // TODO
235 // const match = MatchingSection{
236 // .seg = segment_id,
237 // .sect = index,
238 // };
239 // _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
240 // try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
241
242 self.load_commands_dirty = true;
154 const index = @intCast(u8, self.sections.items.len);
155 try self.sections.append(self.base.base.allocator, sect);
156 segment.cmdsize += @sizeOf(macho.section_64);
157 segment.nsects += 1;
243158
244159 return index;
245160}
246161
247162fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
248 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
249163 const end = start + padToIdeal(size);
250 for (seg.sections.items) |section| {
164 for (self.sections.items) |section| {
251165 const increased_size = padToIdeal(section.size);
252166 const test_end = section.offset + increased_size;
253167 if (end > section.offset and start < test_end) {
......@@ -258,8 +172,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
258172}
259173
260174pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
261 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
262 var offset: u64 = seg.inner.fileoff;
175 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
176 var offset: u64 = segment.fileoff;
263177 while (self.detectAllocCollision(offset, object_size)) |item_end| {
264178 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
265179 }
......@@ -296,8 +210,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
296210 break :blk got_entry.getName(self.base);
297211 },
298212 };
299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
300 const sect = &seg.sections.items[self.debug_info_section_index.?];
213 const sect = &self.sections.items[self.debug_info_section_index.?];
301214 const file_offset = sect.offset + reloc.offset;
302215 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
303216 reloc.target,
......@@ -311,15 +224,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
311224
312225 if (self.debug_abbrev_section_dirty) {
313226 try self.dwarf.writeDbgAbbrev(&self.base.base);
314 self.load_commands_dirty = true;
315227 self.debug_abbrev_section_dirty = false;
316228 }
317229
318230 if (self.debug_info_header_dirty) {
319231 // Currently only one compilation unit is supported, so the address range is simply
320232 // identical to the main program header virtual address and memory size.
321 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
322 const text_section = text_segment.sections.items[self.text_section_index.?];
233 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
323234 const low_pc = text_section.addr;
324235 const high_pc = text_section.addr + text_section.size;
325236 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
......@@ -329,10 +240,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
329240 if (self.debug_aranges_section_dirty) {
330241 // Currently only one compilation unit is supported, so the address range is simply
331242 // identical to the main program header virtual address and memory size.
332 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
333 const text_section = text_segment.sections.items[self.text_section_index.?];
243 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
334244 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
335 self.load_commands_dirty = true;
336245 self.debug_aranges_section_dirty = false;
337246 }
338247
......@@ -342,8 +251,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
342251 }
343252
344253 {
345 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
346 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
254 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
255 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
347256 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
348257 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
349258 const needed_size = self.dwarf.strtab.items.len;
......@@ -351,7 +260,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
351260 if (needed_size > allocated_size) {
352261 debug_strtab_sect.size = 0; // free the space
353262 const new_offset = self.findFreeSpace(needed_size, 1);
354 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
263 debug_strtab_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
355264 debug_strtab_sect.offset = @intCast(u32, new_offset);
356265 }
357266 debug_strtab_sect.size = @intCast(u32, needed_size);
......@@ -362,28 +271,53 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
362271 });
363272
364273 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
365 self.load_commands_dirty = true;
366274 self.debug_string_table_dirty = false;
367275 }
368276 }
369277
278 var lc_buffer = std.ArrayList(u8).init(allocator);
279 defer lc_buffer.deinit();
280 const lc_writer = lc_buffer.writer();
281 var ncmds: u32 = 0;
282
283 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
370284 self.updateDwarfSegment();
371 try self.writeLinkeditSegment();
372 try self.updateVirtualMemoryMapping();
373 try self.writeLoadCommands(allocator);
374 try self.writeHeader();
375285
376 assert(!self.load_commands_dirty);
286 {
287 try lc_writer.writeStruct(self.base.uuid);
288 ncmds += 1;
289 }
290
291 var headers_buf = std.ArrayList(u8).init(allocator);
292 defer headers_buf.deinit();
293 try self.base.writeSegmentHeaders(
294 0,
295 self.base.linkedit_segment_cmd_index.?,
296 &ncmds,
297 headers_buf.writer(),
298 );
299
300 for (self.segments.items) |seg| {
301 try headers_buf.writer().writeStruct(seg);
302 ncmds += 2;
303 }
304 for (self.sections.items) |header| {
305 try headers_buf.writer().writeStruct(header);
306 }
307
308 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
309 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
310
311 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
312
377313 assert(!self.debug_abbrev_section_dirty);
378314 assert(!self.debug_aranges_section_dirty);
379315 assert(!self.debug_string_table_dirty);
380316}
381317
382318pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
383 for (self.load_commands.items) |*lc| {
384 lc.deinit(allocator);
385 }
386 self.load_commands.deinit(allocator);
319 self.segments.deinit(allocator);
320 self.sections.deinit(allocator);
387321 self.dwarf.deinit();
388322 self.strtab.deinit(allocator);
389323 self.relocs.deinit(allocator);
......@@ -402,59 +336,19 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
402336 }
403337}
404338
405fn copySegmentCommand(
406 self: *DebugSymbols,
407 allocator: Allocator,
408 base_cmd: macho.SegmentCommand,
409) !macho.SegmentCommand {
410 var cmd = macho.SegmentCommand{
411 .inner = .{
412 .segname = undefined,
413 .cmdsize = base_cmd.inner.cmdsize,
414 .vmaddr = base_cmd.inner.vmaddr,
415 .vmsize = base_cmd.inner.vmsize,
416 .maxprot = base_cmd.inner.maxprot,
417 .initprot = base_cmd.inner.initprot,
418 .nsects = base_cmd.inner.nsects,
419 .flags = base_cmd.inner.flags,
420 },
421 };
422 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
423
424 try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects);
425 for (base_cmd.sections.items) |base_sect, i| {
426 var sect = macho.section_64{
427 .sectname = undefined,
428 .segname = undefined,
429 .addr = base_sect.addr,
430 .size = base_sect.size,
431 .offset = 0,
432 .@"align" = base_sect.@"align",
433 .reloff = 0,
434 .nreloc = 0,
435 .flags = base_sect.flags,
436 .reserved1 = base_sect.reserved1,
437 .reserved2 = base_sect.reserved2,
438 .reserved3 = base_sect.reserved3,
439 };
440 mem.copy(u8, &sect.sectname, &base_sect.sectname);
441 mem.copy(u8, &sect.segname, &base_sect.segname);
442
443 if (self.base.text_section_index.? == i) {
444 self.text_section_index = @intCast(u16, i);
445 }
339fn updateDwarfSegment(self: *DebugSymbols) void {
340 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
341 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
446342
447 cmd.sections.appendAssumeCapacity(sect);
343 const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
344 const old_start_aligned = dwarf_segment.vmaddr;
345 const diff = new_start_aligned - old_start_aligned;
346 if (diff > 0) {
347 dwarf_segment.vmaddr = new_start_aligned;
448348 }
449349
450 return cmd;
451}
452
453fn updateDwarfSegment(self: *DebugSymbols) void {
454 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
455
456350 var max_offset: u64 = 0;
457 for (dwarf_segment.sections.items) |sect| {
351 for (self.sections.items) |*sect| {
458352 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
459353 sect.segName(),
460354 sect.sectName(),
......@@ -466,44 +360,19 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
466360 if (sect.offset + sect.size > max_offset) {
467361 max_offset = sect.offset + sect.size;
468362 }
363 sect.addr += diff;
469364 }
470365
471 const file_size = max_offset - dwarf_segment.inner.fileoff;
366 const file_size = max_offset - dwarf_segment.fileoff;
472367 log.debug("__DWARF size 0x{x}", .{file_size});
473368
474 if (file_size != dwarf_segment.inner.filesize) {
475 dwarf_segment.inner.filesize = file_size;
476 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
477 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
478 }
479 self.load_commands_dirty = true;
480 }
481}
482
483/// Writes all load commands and section headers.
484fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
485 if (!self.load_commands_dirty) return;
486
487 var sizeofcmds: u32 = 0;
488 for (self.load_commands.items) |lc| {
489 sizeofcmds += lc.cmdsize();
369 if (file_size != dwarf_segment.filesize) {
370 dwarf_segment.filesize = file_size;
371 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
490372 }
491
492 var buffer = try allocator.alloc(u8, sizeofcmds);
493 defer allocator.free(buffer);
494 var fib = std.io.fixedBufferStream(buffer);
495 const writer = fib.writer();
496 for (self.load_commands.items) |lc| {
497 try lc.write(writer);
498 }
499
500 const off = @sizeOf(macho.mach_header_64);
501 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
502 try self.file.pwriteAll(buffer, off);
503 self.load_commands_dirty = false;
504373}
505374
506fn writeHeader(self: *DebugSymbols) !void {
375fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
507376 var header: macho.mach_header_64 = .{};
508377 header.filetype = macho.MH_DSYM;
509378
......@@ -519,12 +388,8 @@ fn writeHeader(self: *DebugSymbols) !void {
519388 else => return error.UnsupportedCpuArchitecture,
520389 }
521390
522 header.ncmds = @intCast(u32, self.load_commands.items.len);
523 header.sizeofcmds = 0;
524
525 for (self.load_commands.items) |cmd| {
526 header.sizeofcmds += cmd.cmdsize();
527 }
391 header.ncmds = ncmds;
392 header.sizeofcmds = sizeofcmds;
528393
529394 log.debug("writing Mach-O header {}", .{header});
530395
......@@ -532,79 +397,46 @@ fn writeHeader(self: *DebugSymbols) !void {
532397}
533398
534399pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
535 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
536 assert(start >= seg.inner.fileoff);
400 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
401 assert(start >= seg.fileoff);
537402 var min_pos: u64 = std.math.maxInt(u64);
538 for (seg.sections.items) |section| {
403 for (self.sections.items) |section| {
539404 if (section.offset <= start) continue;
540405 if (section.offset < min_pos) min_pos = section.offset;
541406 }
542407 return min_pos - start;
543408}
544409
545fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {
546 const macho_file = self.base;
547 const allocator = macho_file.base.allocator;
548
549 const IndexTuple = std.meta.Tuple(&[_]type{ *?u16, *?u16 });
550 const indices = &[_]IndexTuple{
551 .{ &macho_file.text_segment_cmd_index, &self.text_segment_cmd_index },
552 .{ &macho_file.data_const_segment_cmd_index, &self.data_const_segment_cmd_index },
553 .{ &macho_file.data_segment_cmd_index, &self.data_segment_cmd_index },
554 };
555
556 for (indices) |tuple| {
557 const orig_cmd = macho_file.load_commands.items[tuple[0].*.?].segment;
558 const cmd = try self.copySegmentCommand(allocator, orig_cmd);
559 const comp_cmd = &self.load_commands.items[tuple[1].*.?];
560 comp_cmd.deinit(allocator);
561 self.load_commands.items[tuple[1].*.?] = .{ .segment = cmd };
562 }
563
564 // TODO should we set the linkedit vmsize to that of the binary?
565 const orig_cmd = macho_file.load_commands.items[macho_file.linkedit_segment_cmd_index.?].segment;
566 const orig_vmaddr = orig_cmd.inner.vmaddr;
567 const linkedit_cmd = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
568 linkedit_cmd.inner.vmaddr = orig_vmaddr;
569
570 // Update VM address for the DWARF segment and sections including re-running relocations.
571 // TODO re-run relocations
572 const dwarf_cmd = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
573 const new_start_aligned = orig_vmaddr + linkedit_cmd.inner.vmsize;
574 const old_start_aligned = dwarf_cmd.inner.vmaddr;
575 const diff = new_start_aligned - old_start_aligned;
576 if (diff > 0) {
577 dwarf_cmd.inner.vmaddr = new_start_aligned;
578
579 for (dwarf_cmd.sections.items) |*sect| {
580 sect.addr += (new_start_aligned - old_start_aligned);
581 }
582 }
583
584 self.load_commands_dirty = true;
585}
586
587fn writeLinkeditSegment(self: *DebugSymbols) !void {
410fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
588411 const tracy = trace(@src());
589412 defer tracy.end();
590413
591 try self.writeSymbolTable();
592 try self.writeStringTable();
414 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
415 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
416 seg.vmaddr = source_vmaddr;
593417
594 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
595 const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);
596 seg.inner.filesize = aligned_size;
597 seg.inner.vmsize = aligned_size;
418 var symtab_cmd = macho.symtab_command{
419 .cmdsize = @sizeOf(macho.symtab_command),
420 .symoff = 0,
421 .nsyms = 0,
422 .stroff = 0,
423 .strsize = 0,
424 };
425 try self.writeSymtab(&symtab_cmd);
426 try self.writeStrtab(&symtab_cmd);
427 try lc_writer.writeStruct(symtab_cmd);
428 ncmds.* += 1;
429
430 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
431 seg.filesize = aligned_size;
432 seg.vmsize = aligned_size;
598433}
599434
600fn writeSymbolTable(self: *DebugSymbols) !void {
435fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601436 const tracy = trace(@src());
602437 defer tracy.end();
603438
604439 const gpa = self.base.base.allocator;
605 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
606 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
607 symtab.symoff = @intCast(u32, seg.inner.fileoff);
608440
609441 var locals = std.ArrayList(macho.nlist_64).init(gpa);
610442 defer locals.deinit();
......@@ -634,34 +466,36 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
634466
635467 const nlocals = locals.items.len;
636468 const nexports = exports.items.len;
637 const locals_off = symtab.symoff;
638 const locals_size = nlocals * @sizeOf(macho.nlist_64);
639 const exports_off = locals_off + locals_size;
640 const exports_size = nexports * @sizeOf(macho.nlist_64);
469 const nsyms = nlocals + nexports;
641470
642 symtab.nsyms = @intCast(u32, nlocals + nexports);
643 const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);
471 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
472 const offset = mem.alignForwardGeneric(
473 u64,
474 seg.fileoff + seg.filesize,
475 @alignOf(macho.nlist_64),
476 );
477 const needed_size = nsyms * @sizeOf(macho.nlist_64);
644478
645 if (needed_size > seg.inner.filesize) {
479 if (needed_size > seg.filesize) {
646480 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
647 const diff = @intCast(u32, aligned_size - seg.inner.filesize);
648 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
649 seg.inner.filesize = aligned_size;
481 const diff = @intCast(u32, aligned_size - seg.filesize);
482 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
483 seg.filesize = aligned_size;
650484
651485 try MachO.copyRangeAllOverlappingAlloc(
652486 self.base.base.allocator,
653487 self.file,
654 dwarf_seg.inner.fileoff,
655 dwarf_seg.inner.fileoff + diff,
656 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
488 dwarf_seg.fileoff,
489 dwarf_seg.fileoff + diff,
490 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
657491 );
658492
659 const old_seg_fileoff = dwarf_seg.inner.fileoff;
660 dwarf_seg.inner.fileoff += diff;
493 const old_seg_fileoff = dwarf_seg.fileoff;
494 dwarf_seg.fileoff += diff;
661495
662 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
496 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
663497
664 for (dwarf_seg.sections.items) |*sect| {
498 for (self.sections.items) |*sect| {
665499 const old_offset = sect.offset;
666500 sect.offset += diff;
667501
......@@ -674,47 +508,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
674508 }
675509 }
676510
511 lc.symoff = @intCast(u32, offset);
512 lc.nsyms = @intCast(u32, nsyms);
513
514 const locals_off = lc.symoff;
515 const locals_size = nlocals * @sizeOf(macho.nlist_64);
516 const exports_off = locals_off + locals_size;
517 const exports_size = nexports * @sizeOf(macho.nlist_64);
518
677519 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
678520 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
679521
680522 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
681523 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
682
683 self.load_commands_dirty = true;
684524}
685525
686fn writeStringTable(self: *DebugSymbols) !void {
526fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
687527 const tracy = trace(@src());
688528 defer tracy.end();
689529
690 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
691 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
692 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
693 symtab.stroff = symtab.symoff + symtab_size;
530 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
531 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
532 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
533 lc.stroff = @intCast(u32, offset);
694534
695535 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
696 symtab.strsize = @intCast(u32, needed_size);
536 lc.strsize = @intCast(u32, needed_size);
697537
698 if (symtab_size + needed_size > seg.inner.filesize) {
699 const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);
700 const diff = @intCast(u32, aligned_size - seg.inner.filesize);
701 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
702 seg.inner.filesize = aligned_size;
538 if (offset + needed_size > seg.filesize) {
539 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
540 const diff = @intCast(u32, aligned_size - seg.filesize);
541 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
542 seg.filesize = aligned_size;
703543
704544 try MachO.copyRangeAllOverlappingAlloc(
705545 self.base.base.allocator,
706546 self.file,
707 dwarf_seg.inner.fileoff,
708 dwarf_seg.inner.fileoff + diff,
709 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
547 dwarf_seg.fileoff,
548 dwarf_seg.fileoff + diff,
549 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
710550 );
711551
712 const old_seg_fileoff = dwarf_seg.inner.fileoff;
713 dwarf_seg.inner.fileoff += diff;
552 const old_seg_fileoff = dwarf_seg.fileoff;
553 dwarf_seg.fileoff += diff;
714554
715 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
555 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
716556
717 for (dwarf_seg.sections.items) |*sect| {
557 for (self.sections.items) |*sect| {
718558 const old_offset = sect.offset;
719559 sect.offset += diff;
720560
......@@ -727,9 +567,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
727567 }
728568 }
729569
730 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
731
732 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
570 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
733571
734 self.load_commands_dirty = true;
572 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
735573}
src/link/MachO/Dylib.zig+53-106
......@@ -13,23 +13,9 @@ const fat = @import("fat.zig");
1313const Allocator = mem.Allocator;
1414const CrossTarget = std.zig.CrossTarget;
1515const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommandIterator = macho.LoadCommandIterator;
1617const MachO = @import("../MachO.zig");
1718
18file: fs.File,
19name: []const u8,
20
21header: ?macho.mach_header_64 = null,
22
23// The actual dylib contents we care about linking with will be embedded at
24// an offset within a file if we are linking against a fat lib
25library_offset: u64 = 0,
26
27load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
28
29symtab_cmd_index: ?u16 = null,
30dysymtab_cmd_index: ?u16 = null,
31id_cmd_index: ?u16 = null,
32
3319id: ?Id = null,
3420weak: bool = false,
3521
......@@ -53,16 +39,12 @@ pub const Id = struct {
5339 };
5440 }
5541
56 pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {
57 const dylib = lc.inner.dylib;
58 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));
60
42 pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !Id {
6143 return Id{
62 .name = name,
63 .timestamp = dylib.timestamp,
64 .current_version = dylib.current_version,
65 .compatibility_version = dylib.compatibility_version,
44 .name = try allocator.dupe(u8, name),
45 .timestamp = lc.dylib.timestamp,
46 .current_version = lc.dylib.current_version,
47 .compatibility_version = lc.dylib.compatibility_version,
6648 };
6749 }
6850
......@@ -126,125 +108,89 @@ pub const Id = struct {
126108};
127109
128110pub fn deinit(self: *Dylib, allocator: Allocator) void {
129 for (self.load_commands.items) |*lc| {
130 lc.deinit(allocator);
131 }
132 self.load_commands.deinit(allocator);
133
134111 for (self.symbols.keys()) |key| {
135112 allocator.free(key);
136113 }
137114 self.symbols.deinit(allocator);
138
139 allocator.free(self.name);
140
141115 if (self.id) |*id| {
142116 id.deinit(allocator);
143117 }
144118}
145119
146pub fn parse(
120pub fn parseFromBinary(
147121 self: *Dylib,
148122 allocator: Allocator,
149123 cpu_arch: std.Target.Cpu.Arch,
150124 dylib_id: u16,
151125 dependent_libs: anytype,
126 name: []const u8,
127 data: []align(@alignOf(u64)) const u8,
152128) !void {
153 log.debug("parsing shared library '{s}'", .{self.name});
154
155 self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
129 var stream = std.io.fixedBufferStream(data);
130 const reader = stream.reader();
156131
157 try self.file.seekTo(self.library_offset);
132 log.debug("parsing shared library '{s}'", .{name});
158133
159 var reader = self.file.reader();
160 self.header = try reader.readStruct(macho.mach_header_64);
134 const header = try reader.readStruct(macho.mach_header_64);
161135
162 if (self.header.?.filetype != macho.MH_DYLIB) {
163 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
136 if (header.filetype != macho.MH_DYLIB) {
137 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
164138 return error.NotDylib;
165139 }
166140
167 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
141 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
168142
169143 if (this_arch != cpu_arch) {
170 log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
144 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
145 @tagName(cpu_arch),
146 @tagName(this_arch),
147 });
171148 return error.MismatchedCpuArchitecture;
172149 }
173150
174 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
175 try self.parseId(allocator);
176 try self.parseSymbols(allocator);
177}
178
179fn readLoadCommands(
180 self: *Dylib,
181 allocator: Allocator,
182 reader: anytype,
183 dylib_id: u16,
184 dependent_libs: anytype,
185) !void {
186 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
187
188 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
189
190 var i: u16 = 0;
191 while (i < self.header.?.ncmds) : (i += 1) {
192 var cmd = try macho.LoadCommand.read(allocator, reader);
151 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
152 var it = LoadCommandIterator{
153 .ncmds = header.ncmds,
154 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
155 };
156 while (it.next()) |cmd| {
193157 switch (cmd.cmd()) {
194158 .SYMTAB => {
195 self.symtab_cmd_index = i;
196 },
197 .DYSYMTAB => {
198 self.dysymtab_cmd_index = i;
159 const symtab_cmd = cmd.cast(macho.symtab_command).?;
160 const symtab = @ptrCast(
161 [*]const macho.nlist_64,
162 @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
163 )[0..symtab_cmd.nsyms];
164 const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];
165
166 for (symtab) |sym| {
167 const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
168 if (!add_to_symtab) continue;
169
170 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
171 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {});
172 }
199173 },
200174 .ID_DYLIB => {
201 self.id_cmd_index = i;
175 self.id = try Id.fromLoadCommand(
176 allocator,
177 cmd.cast(macho.dylib_command).?,
178 cmd.getDylibPathName(),
179 );
202180 },
203181 .REEXPORT_DYLIB => {
204182 if (should_lookup_reexports) {
205183 // Parse install_name to dependent dylib.
206 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
184 var id = try Id.fromLoadCommand(
185 allocator,
186 cmd.cast(macho.dylib_command).?,
187 cmd.getDylibPathName(),
188 );
207189 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
208190 }
209191 },
210 else => {
211 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
212 },
192 else => {},
213193 }
214 self.load_commands.appendAssumeCapacity(cmd);
215 }
216}
217
218fn parseId(self: *Dylib, allocator: Allocator) !void {
219 const index = self.id_cmd_index orelse {
220 log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
221 self.id = try Id.default(allocator, self.name);
222 return;
223 };
224 self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].dylib);
225}
226
227fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
228 const index = self.symtab_cmd_index orelse return;
229 const symtab_cmd = self.load_commands.items[index].symtab;
230
231 const symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
232 defer allocator.free(symtab);
233 _ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset);
234 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
235
236 const strtab = try allocator.alloc(u8, symtab_cmd.strsize);
237 defer allocator.free(strtab);
238 _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
239
240 for (slice) |sym| {
241 const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
242
243 if (!add_to_symtab) continue;
244
245 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
246 const name = try allocator.dupe(u8, sym_name);
247 try self.symbols.putNoClobber(allocator, name, {});
248194 }
249195}
250196
......@@ -356,10 +302,11 @@ pub fn parseFromStub(
356302 lib_stub: LibStub,
357303 dylib_id: u16,
358304 dependent_libs: anytype,
305 name: []const u8,
359306) !void {
360307 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
361308
362 log.debug("parsing shared library from stub '{s}'", .{self.name});
309 log.debug("parsing shared library from stub '{s}'", .{name});
363310
364311 const umbrella_lib = lib_stub.inner[0];
365312
src/link/MachO/Object.zig+134-172
......@@ -3,6 +3,7 @@ const Object = @This();
33const std = @import("std");
44const build_options = @import("build_options");
55const assert = std.debug.assert;
6const dwarf = std.dwarf;
67const fs = std.fs;
78const io = std.io;
89const log = std.log.scoped(.link);
......@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
1415
1516const Allocator = mem.Allocator;
1617const Atom = @import("Atom.zig");
18const LoadCommandIterator = macho.LoadCommandIterator;
1719const MachO = @import("../MachO.zig");
18const MatchingSection = MachO.MatchingSection;
1920const SymbolWithLoc = MachO.SymbolWithLoc;
2021
21file: fs.File,
2222name: []const u8,
2323mtime: u64,
24
25/// Data contents of the file. Includes sections, and data of load commands.
26/// Excludes the backing memory for the header and load commands.
27/// Initialized in `parse`.
28contents: []const u8 = undefined,
29
30file_offset: ?u32 = null,
24contents: []align(@alignOf(u64)) const u8,
3125
3226header: macho.mach_header_64 = undefined,
33
34load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
35
36segment_cmd_index: ?u16 = null,
37text_section_index: ?u16 = null,
38symtab_cmd_index: ?u16 = null,
39dysymtab_cmd_index: ?u16 = null,
40build_version_cmd_index: ?u16 = null,
41data_in_code_cmd_index: ?u16 = null,
42
43// __DWARF segment sections
44dwarf_debug_info_index: ?u16 = null,
45dwarf_debug_abbrev_index: ?u16 = null,
46dwarf_debug_str_index: ?u16 = null,
47dwarf_debug_line_index: ?u16 = null,
48dwarf_debug_line_str_index: ?u16 = null,
49dwarf_debug_ranges_index: ?u16 = null,
27in_symtab: []const macho.nlist_64 = undefined,
28in_strtab: []const u8 = undefined,
5029
5130symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
52strtab: []const u8 = &.{},
53data_in_code_entries: []const macho.data_in_code_entry = &.{},
31sections: std.ArrayListUnmanaged(macho.section_64) = .{},
5432
5533sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5634
......@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
6139atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
6240
6341pub fn deinit(self: *Object, gpa: Allocator) void {
64 for (self.load_commands.items) |*lc| {
65 lc.deinit(gpa);
66 }
67 self.load_commands.deinit(gpa);
68 gpa.free(self.contents);
6942 self.symtab.deinit(gpa);
43 self.sections.deinit(gpa);
7044 self.sections_as_symbols.deinit(gpa);
7145 self.atom_by_index_table.deinit(gpa);
7246
......@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
7751 self.managed_atoms.deinit(gpa);
7852
7953 gpa.free(self.name);
54 gpa.free(self.contents);
8055}
8156
8257pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
83 const file_stat = try self.file.stat();
84 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
85 self.contents = try self.file.readToEndAlloc(allocator, file_size);
86
8758 var stream = std.io.fixedBufferStream(self.contents);
8859 const reader = stream.reader();
8960
90 const file_offset = self.file_offset orelse 0;
91 if (file_offset > 0) {
92 try reader.context.seekTo(file_offset);
93 }
94
9561 self.header = try reader.readStruct(macho.mach_header_64);
62
9663 if (self.header.filetype != macho.MH_OBJECT) {
9764 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
9865 macho.MH_OBJECT,
......@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
11077 },
11178 };
11279 if (this_arch != cpu_arch) {
113 log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
80 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
81 @tagName(cpu_arch),
82 @tagName(this_arch),
83 });
11484 return error.MismatchedCpuArchitecture;
11585 }
11686
117 try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);
118
119 var i: u16 = 0;
120 while (i < self.header.ncmds) : (i += 1) {
121 var cmd = try macho.LoadCommand.read(allocator, reader);
87 var it = LoadCommandIterator{
88 .ncmds = self.header.ncmds,
89 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
90 };
91 while (it.next()) |cmd| {
12292 switch (cmd.cmd()) {
12393 .SEGMENT_64 => {
124 self.segment_cmd_index = i;
125 var seg = cmd.segment;
126 for (seg.sections.items) |*sect, j| {
127 const index = @intCast(u16, j);
128 const segname = sect.segName();
129 const sectname = sect.sectName();
130 if (mem.eql(u8, segname, "__DWARF")) {
131 if (mem.eql(u8, sectname, "__debug_info")) {
132 self.dwarf_debug_info_index = index;
133 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
134 self.dwarf_debug_abbrev_index = index;
135 } else if (mem.eql(u8, sectname, "__debug_str")) {
136 self.dwarf_debug_str_index = index;
137 } else if (mem.eql(u8, sectname, "__debug_line")) {
138 self.dwarf_debug_line_index = index;
139 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
140 self.dwarf_debug_line_str_index = index;
141 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
142 self.dwarf_debug_ranges_index = index;
143 }
144 } else if (mem.eql(u8, segname, "__TEXT")) {
145 if (mem.eql(u8, sectname, "__text")) {
146 self.text_section_index = index;
147 }
148 }
149
150 sect.offset += file_offset;
151 if (sect.reloff > 0) {
152 sect.reloff += file_offset;
153 }
94 const segment = cmd.cast(macho.segment_command_64).?;
95 try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
96 for (cmd.getSections()) |sect| {
97 self.sections.appendAssumeCapacity(sect);
15498 }
155
156 seg.inner.fileoff += file_offset;
15799 },
158100 .SYMTAB => {
159 self.symtab_cmd_index = i;
160 cmd.symtab.symoff += file_offset;
161 cmd.symtab.stroff += file_offset;
162 },
163 .DYSYMTAB => {
164 self.dysymtab_cmd_index = i;
165 },
166 .BUILD_VERSION => {
167 self.build_version_cmd_index = i;
168 },
169 .DATA_IN_CODE => {
170 self.data_in_code_cmd_index = i;
171 cmd.linkedit_data.dataoff += file_offset;
172 },
173 else => {
174 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
101 const symtab = cmd.cast(macho.symtab_command).?;
102 self.in_symtab = @ptrCast(
103 [*]const macho.nlist_64,
104 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
105 )[0..symtab.nsyms];
106 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
107 try self.symtab.appendSlice(allocator, self.in_symtab);
175108 },
109 else => {},
176110 }
177 self.load_commands.appendAssumeCapacity(cmd);
178111 }
179
180 try self.parseSymtab(allocator);
181112}
182113
183114const Context = struct {
184 symtab: []const macho.nlist_64,
185 strtab: []const u8,
115 object: *const Object,
186116};
187117
188118const SymbolAtIndex = struct {
189119 index: u32,
190120
191121 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
192 return ctx.symtab[self.index];
122 return ctx.object.getSourceSymbol(self.index).?;
193123 }
194124
195125 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
196126 const sym = self.getSymbol(ctx);
197 assert(sym.n_strx < ctx.strtab.len);
198 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
127 return ctx.object.getString(sym.n_strx);
199128 }
200129
201130 /// Returns whether lhs is less than rhs by allocated address in object file.
......@@ -293,7 +222,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
293222 defer tracy.end();
294223
295224 const gpa = macho_file.base.allocator;
296 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
297225
298226 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
299227
......@@ -302,13 +230,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
302230 // the GO compiler does not necessarily respect that therefore we sort immediately by type
303231 // and address within.
304232 const context = Context{
305 .symtab = self.getSourceSymtab(),
306 .strtab = self.strtab,
233 .object = self,
307234 };
308 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
235 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, self.in_symtab.len);
309236 defer sorted_all_syms.deinit();
310237
311 for (context.symtab) |_, index| {
238 for (self.in_symtab) |_, index| {
312239 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
313240 }
314241
......@@ -320,36 +247,36 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
320247
321248 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
322249 // have to infer the start of undef section in the symtab ourselves.
323 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
324 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
250 const iundefsym = blk: {
251 const dysymtab = self.parseDysymtab() orelse {
252 var iundefsym: usize = sorted_all_syms.items.len;
253 while (iundefsym > 0) : (iundefsym -= 1) {
254 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
255 if (sym.sect()) break;
256 }
257 break :blk iundefsym;
258 };
325259 break :blk dysymtab.iundefsym;
326 } else blk: {
327 var iundefsym: usize = sorted_all_syms.items.len;
328 while (iundefsym > 0) : (iundefsym -= 1) {
329 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
330 if (sym.sect()) break;
331 }
332 break :blk iundefsym;
333260 };
334261
335262 // We only care about defined symbols, so filter every other out.
336263 const sorted_syms = sorted_all_syms.items[0..iundefsym];
337264 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
338265
339 for (seg.sections.items) |sect, id| {
266 for (self.sections.items) |sect, id| {
340267 const sect_id = @intCast(u8, id);
341268 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
342269
343270 // Get matching segment/section in the final artifact.
344 const match = (try macho_file.getMatchingSection(sect)) orelse {
271 const match = (try macho_file.getOutputSection(sect)) orelse {
345272 log.debug(" unhandled section", .{});
346273 continue;
347274 };
348275
349276 log.debug(" output sect({d}, '{s},{s}')", .{
350 macho_file.getSectionOrdinal(match),
351 macho_file.getSection(match).segName(),
352 macho_file.getSection(match).sectName(),
277 match + 1,
278 macho_file.sections.items(.header)[match].segName(),
279 macho_file.sections.items(.header)[match].sectName(),
353280 });
354281
355282 const cpu_arch = macho_file.base.options.target.cpu.arch;
......@@ -359,14 +286,13 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
359286 };
360287
361288 // Read section's code
362 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
289 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect) else null;
363290
364291 // Read section's list of relocations
365 const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];
366 const relocs = mem.bytesAsSlice(
367 macho.relocation_info,
368 @alignCast(@alignOf(macho.relocation_info), raw_relocs),
369 );
292 const relocs = @ptrCast(
293 [*]const macho.relocation_info,
294 @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
295 )[0..sect.nreloc];
370296
371297 // Symbols within this section only.
372298 const filtered_syms = filterSymbolsByAddress(
......@@ -387,7 +313,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
387313 try self.symtab.append(gpa, .{
388314 .n_strx = 0,
389315 .n_type = macho.N_SECT,
390 .n_sect = macho_file.getSectionOrdinal(match),
316 .n_sect = match + 1,
391317 .n_desc = 0,
392318 .n_value = sect.addr,
393319 });
......@@ -476,7 +402,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
476402 try self.symtab.append(gpa, .{
477403 .n_strx = 0,
478404 .n_type = macho.N_SECT,
479 .n_sect = macho_file.getSectionOrdinal(match),
405 .n_sect = match + 1,
480406 .n_desc = 0,
481407 .n_value = addr,
482408 });
......@@ -501,7 +427,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
501427 try self.symtab.append(gpa, .{
502428 .n_strx = 0,
503429 .n_type = macho.N_SECT,
504 .n_sect = macho_file.getSectionOrdinal(match),
430 .n_sect = match + 1,
505431 .n_desc = 0,
506432 .n_value = sect.addr,
507433 });
......@@ -535,21 +461,21 @@ fn createAtomFromSubsection(
535461 code: ?[]const u8,
536462 relocs: []const macho.relocation_info,
537463 indexes: []const SymbolAtIndex,
538 match: MatchingSection,
464 match: u8,
539465 sect: macho.section_64,
540466) !*Atom {
541467 const gpa = macho_file.base.allocator;
542468 const sym = self.symtab.items[sym_index];
543469 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
544470 atom.file = object_id;
545 self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
471 self.symtab.items[sym_index].n_sect = match + 1;
546472
547473 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
548474 sym_index,
549475 self.getString(sym.n_strx),
550 macho_file.getSectionOrdinal(match),
551 macho_file.getSection(match).segName(),
552 macho_file.getSection(match).sectName(),
476 match + 1,
477 macho_file.sections.items(.header)[match].segName(),
478 macho_file.sections.items(.header)[match].sectName(),
553479 object_id,
554480 });
555481
......@@ -577,7 +503,7 @@ fn createAtomFromSubsection(
577503 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
578504 for (indexes) |inner_sym_index| {
579505 const inner_sym = &self.symtab.items[inner_sym_index.index];
580 inner_sym.n_sect = macho_file.getSectionOrdinal(match);
506 inner_sym.n_sect = match + 1;
581507 atom.contained.appendAssumeCapacity(.{
582508 .sym_index = inner_sym_index.index,
583509 .offset = inner_sym.n_value - sym.n_value,
......@@ -589,48 +515,84 @@ fn createAtomFromSubsection(
589515 return atom;
590516}
591517
592fn parseSymtab(self: *Object, allocator: Allocator) !void {
593 const index = self.symtab_cmd_index orelse return;
594 const symtab = self.load_commands.items[index].symtab;
595 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
596 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
518pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
519 if (index >= self.in_symtab.len) return null;
520 return self.in_symtab[index];
597521}
598522
599pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
600 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
601 const symtab = self.load_commands.items[index].symtab;
602 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
603 const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
604 return mem.bytesAsSlice(
605 macho.nlist_64,
606 @alignCast(@alignOf(macho.nlist_64), raw_symtab),
607 );
523pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
524 assert(index < self.sections.items.len);
525 return self.sections.items[index];
608526}
609527
610pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
611 const symtab = self.getSourceSymtab();
612 if (index >= symtab.len) return null;
613 return symtab[index];
528pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
529 var it = LoadCommandIterator{
530 .ncmds = self.header.ncmds,
531 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
532 };
533 while (it.next()) |cmd| {
534 switch (cmd.cmd()) {
535 .DATA_IN_CODE => {
536 const dice = cmd.cast(macho.linkedit_data_command).?;
537 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
538 return @ptrCast(
539 [*]const macho.data_in_code_entry,
540 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
541 )[0..ndice];
542 },
543 else => {},
544 }
545 } else return null;
614546}
615547
616pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
617 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
618 assert(index < seg.sections.items.len);
619 return seg.sections.items[index];
548fn parseDysymtab(self: Object) ?macho.dysymtab_command {
549 var it = LoadCommandIterator{
550 .ncmds = self.header.ncmds,
551 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
552 };
553 while (it.next()) |cmd| {
554 switch (cmd.cmd()) {
555 .DYSYMTAB => {
556 return cmd.cast(macho.dysymtab_command).?;
557 },
558 else => {},
559 }
560 } else return null;
620561}
621562
622pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
623 const index = self.data_in_code_cmd_index orelse return null;
624 const data_in_code = self.load_commands.items[index].linkedit_data;
625 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];
626 return mem.bytesAsSlice(
627 macho.data_in_code_entry,
628 @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),
629 );
563pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
564 var di = dwarf.DwarfInfo{
565 .endian = .Little,
566 .debug_info = &[0]u8{},
567 .debug_abbrev = &[0]u8{},
568 .debug_str = &[0]u8{},
569 .debug_line = &[0]u8{},
570 .debug_line_str = &[0]u8{},
571 .debug_ranges = &[0]u8{},
572 };
573 for (self.sections.items) |sect| {
574 const segname = sect.segName();
575 const sectname = sect.sectName();
576 if (mem.eql(u8, segname, "__DWARF")) {
577 if (mem.eql(u8, sectname, "__debug_info")) {
578 di.debug_info = try self.getSectionContents(sect);
579 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
580 di.debug_abbrev = try self.getSectionContents(sect);
581 } else if (mem.eql(u8, sectname, "__debug_str")) {
582 di.debug_str = try self.getSectionContents(sect);
583 } else if (mem.eql(u8, sectname, "__debug_line")) {
584 di.debug_line = try self.getSectionContents(sect);
585 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
586 di.debug_line_str = try self.getSectionContents(sect);
587 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
588 di.debug_ranges = try self.getSectionContents(sect);
589 }
590 }
591 }
592 return di;
630593}
631594
632pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
633 const sect = self.getSourceSection(index);
595pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
634596 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635597 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636598 sect.segName(),
......@@ -642,8 +604,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
642604}
643605
644606pub fn getString(self: Object, off: u32) []const u8 {
645 assert(off < self.strtab.len);
646 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
607 assert(off < self.in_strtab.len);
608 return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
647609}
648610
649611pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
src/link/MachO/dead_strip.zig+25-23
......@@ -8,7 +8,6 @@ const mem = std.mem;
88const Allocator = mem.Allocator;
99const Atom = @import("Atom.zig");
1010const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
1211
1312pub fn gcAtoms(macho_file: *MachO) !void {
1413 const gpa = macho_file.base.allocator;
......@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
2524 try prune(arena, alive, macho_file);
2625}
2726
28fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
29 const sect = macho_file.getSectionPtr(match);
27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
28 var section = macho_file.sections.get(match);
3029
3130 // If we want to enable GC for incremental codepath, we need to take into
3231 // account any padding that might have been left here.
33 sect.size -= atom.size;
32 section.header.size -= atom.size;
3433
3534 if (atom.prev) |prev| {
3635 prev.next = atom.next;
......@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
3837 if (atom.next) |next| {
3938 next.prev = atom.prev;
4039 } else {
41 const last = macho_file.atoms.getPtr(match).?;
4240 if (atom.prev) |prev| {
43 last.* = prev;
41 section.last_atom = prev;
4442 } else {
4543 // The section will be GCed in the next step.
46 last.* = undefined;
47 sect.size = 0;
44 section.last_atom = null;
45 section.header.size = 0;
4846 }
4947 }
48
49 macho_file.sections.set(match, section);
5050}
5151
5252fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
......@@ -173,19 +173,19 @@ fn mark(
173173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174174 // Any section that ends up here will be updated, that is,
175175 // its size and alignment recalculated.
176 var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
176 var gc_sections = std.AutoHashMap(u8, void).init(arena);
177177 var loop: bool = true;
178178 while (loop) {
179179 loop = false;
180180
181181 for (macho_file.objects.items) |object| {
182 for (object.getSourceSymtab()) |_, source_index| {
182 for (object.in_symtab) |_, source_index| {
183183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
184184 if (alive.contains(atom)) continue;
185185
186186 const global = atom.getSymbolWithLoc();
187187 const sym = atom.getSymbolPtr(macho_file);
188 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
188 const match = sym.n_sect - 1;
189189
190190 if (sym.n_desc == MachO.N_DESC_GCED) continue;
191191 if (!sym.ext() and !refersDead(atom, macho_file)) continue;
......@@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
232232
233233 // TODO tombstone
234234 const atom = entry.getAtom(macho_file);
235 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
235 const match = sym.n_sect - 1;
236236 removeAtomFromSection(atom, match, macho_file);
237237 _ = try gc_sections.put(match, {});
238238 _ = macho_file.got_entries_table.remove(entry.target);
......@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
244244
245245 // TODO tombstone
246246 const atom = entry.getAtom(macho_file);
247 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
247 const match = sym.n_sect - 1;
248248 removeAtomFromSection(atom, match, macho_file);
249249 _ = try gc_sections.put(match, {});
250250 _ = macho_file.stubs_table.remove(entry.target);
......@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
256256
257257 // TODO tombstone
258258 const atom = entry.getAtom(macho_file);
259 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
259 const match = sym.n_sect - 1;
260260 removeAtomFromSection(atom, match, macho_file);
261261 _ = try gc_sections.put(match, {});
262262 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);
......@@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
265265 var gc_sections_it = gc_sections.iterator();
266266 while (gc_sections_it.next()) |entry| {
267267 const match = entry.key_ptr.*;
268 const sect = macho_file.getSectionPtr(match);
269 if (sect.size == 0) continue; // Pruning happens automatically in next step.
268 var section = macho_file.sections.get(match);
269 if (section.header.size == 0) continue; // Pruning happens automatically in next step.
270270
271 sect.@"align" = 0;
272 sect.size = 0;
271 section.header.@"align" = 0;
272 section.header.size = 0;
273273
274 var atom = macho_file.atoms.get(match).?;
274 var atom = section.last_atom.?;
275275
276276 while (atom.prev) |prev| {
277277 atom = prev;
......@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
279279
280280 while (true) {
281281 const atom_alignment = try math.powi(u32, 2, atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
283 const padding = aligned_end_addr - sect.size;
284 sect.size += padding + atom.size;
285 sect.@"align" = @maximum(sect.@"align", atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
283 const padding = aligned_end_addr - section.header.size;
284 section.header.size += padding + atom.size;
285 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
286286
287287 if (atom.next) |next| {
288288 atom = next;
289289 } else break;
290290 }
291
292 macho_file.sections.set(match, section);
291293 }
292294}
src/link/MachO/fat.zig+3-1
......@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
4646 return fat_arch.offset;
4747 }
4848 } else {
49 log.err("Could not find matching cpu architecture in fat library: expected {}", .{cpu_arch});
49 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
50 @tagName(cpu_arch),
51 });
5052 return error.MismatchedCpuArchitecture;
5153 }
5254}