authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-03 22:02:50-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-03 22:02:50-07:00
log4a4f3c50ce3b0c32f19a3649c6e0768b1c57218f
tree3a0c469771f810cae91d6a31b78b01fd1a9fc023
parente483336ba454743e65fd72a5d4dd74732dc1ef0f
parent007eb3bd714caee550722cae0e4e98a205ead341
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12320 from ziglang/macho-zld-sync


12 files changed, 2366 insertions(+), 3650 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+55-409
......@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
780780 return parseName(&sect.segname);
781781 }
782782
783 pub fn type_(sect: section_64) u8 {
783 pub fn @"type"(sect: section_64) u8 {
784784 return @truncate(u8, sect.flags & 0xff);
785785 }
786786
......@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
793793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
794794 }
795795
796 pub fn isZerofill(sect: section_64) bool {
797 const tt = sect.@"type"();
798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799 }
800
796801 pub fn isDebug(sect: section_64) bool {
797802 return sect.attrs() & S_ATTR_DEBUG != 0;
798803 }
......@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {
18351840 kind: u16,
18361841};
18371842
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 }
1947
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 }
1982
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) = .{},
1843pub const LoadCommandIterator = struct {
1844 ncmds: usize,
1845 buffer: []align(@alignOf(u64)) const u8,
1846 index: usize = 0,
20091847
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);
1848 pub const LoadCommand = struct {
1849 hdr: load_command,
1850 data: []const u8,
20161851
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);
1852 pub fn cmd(lc: LoadCommand) LC {
1853 return lc.hdr.cmd;
20211854 }
20221855
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;
1856 pub fn cmdsize(lc: LoadCommand) u32 {
1857 return lc.hdr.cmdsize;
20441858 }
2045 return true;
2046 }
2047};
2048
2049pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
2050 return .{ .inner = cmd };
2051}
20521859
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 };
1860 pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
1861 if (lc.data.len < @sizeOf(Cmd)) return null;
1862 return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
20711863 }
20721864
2073 pub fn write(self: Self, writer: anytype) !void {
2074 try writer.writeAll(mem.asBytes(&self.inner));
2075 try writer.writeAll(self.data);
1865 /// Asserts LoadCommand is of type segment_command_64.
1866 pub fn getSections(lc: LoadCommand) []const section_64 {
1867 const segment_lc = lc.cast(segment_command_64).?;
1868 if (segment_lc.nsects == 0) return &[0]section_64{};
1869 const data = lc.data[@sizeOf(segment_command_64)..];
1870 const sections = @ptrCast(
1871 [*]const section_64,
1872 @alignCast(@alignOf(section_64), &data[0]),
1873 )[0..segment_lc.nsects];
1874 return sections;
20761875 }
20771876
2078 pub fn deinit(self: *Self, allocator: Allocator) void {
2079 allocator.free(self.data);
1877 /// Asserts LoadCommand is of type dylib_command.
1878 pub fn getDylibPathName(lc: LoadCommand) []const u8 {
1879 const dylib_lc = lc.cast(dylib_command).?;
1880 const data = lc.data[dylib_lc.dylib.name..];
1881 return mem.sliceTo(data, 0);
20801882 }
20811883
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);
1884 /// Asserts LoadCommand is of type rpath_command.
1885 pub fn getRpathPathName(lc: LoadCommand) []const u8 {
1886 const rpath_lc = lc.cast(rpath_command).?;
1887 const data = lc.data[rpath_lc.path..];
1888 return mem.sliceTo(data, 0);
20851889 }
20861890 };
2087}
2088
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}
2141
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}
21991891
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}
1892 pub fn next(it: *LoadCommandIterator) ?LoadCommand {
1893 if (it.index >= it.ncmds) return null;
22411894
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;
1895 const hdr = @ptrCast(
1896 *const load_command,
1897 @alignCast(@alignOf(load_command), &it.buffer[0]),
1898 ).*;
1899 const cmd = LoadCommand{
1900 .hdr = hdr,
1901 .data = it.buffer[0..hdr.cmdsize],
1902 };
22451903
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 });
1904 it.buffer = @alignCast(@alignOf(u64), it.buffer[hdr.cmdsize..]);
1905 it.index += 1;
22601906
2261 var out_buffer: [in_buffer.len]u8 = undefined;
2262 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
2263}
1907 return cmd;
1908 }
1909};
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+1753-2486
......@@ -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,32 @@ const SystemLib = struct {
6061 weak: bool = false,
6162};
6263
64const Section = struct {
65 header: macho.section_64,
66 segment_index: u8,
67
68 // TODO is null here necessary, or can we do away with tracking via section
69 // size in incremental context?
70 last_atom: ?*Atom = null,
71
72 /// A list of atoms that have surplus capacity. This list can have false
73 /// positives, as functions grow and shrink over time, only sometimes being added
74 /// or removed from the freelist.
75 ///
76 /// An atom has surplus capacity when its overcapacity value is greater than
77 /// padToIdeal(minimum_atom_size). That is, when it has so
78 /// much extra capacity, that we could fit a small new symbol in it, itself with
79 /// ideal_capacity or more.
80 ///
81 /// Ideal capacity is defined by size + (size / ideal_factor).
82 ///
83 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
84 /// overcapacity can be negative. A simple way to have negative overcapacity is to
85 /// allocate a fresh atom, which will have ideal capacity, and then grow it
86 /// by 1 byte. It will then have -1 overcapacity.
87 free_list: std.ArrayListUnmanaged(*Atom) = .{},
88};
89
6390base: File,
6491
6592/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -77,81 +104,32 @@ page_size: u16,
77104/// fashion (default for LLVM backend).
78105mode: enum { incremental, one_shot },
79106
80/// The absolute address of the entry point.
81entry_addr: ?u64 = null,
82
83/// Code signature (if any)
84code_signature: ?CodeSignature = null,
107uuid: macho.uuid_command = .{
108 .cmdsize = @sizeOf(macho.uuid_command),
109 .uuid = undefined,
110},
85111
86112objects: std.ArrayListUnmanaged(Object) = .{},
87113archives: std.ArrayListUnmanaged(Archive) = .{},
88
89114dylibs: std.ArrayListUnmanaged(Dylib) = .{},
90115dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
91116referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
92117
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,
112
113// __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,
128
129// __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,
134
135objc_cfstring_section_index: ?u16 = null,
136objc_classlist_section_index: ?u16 = null,
137objc_imageinfo_section_index: ?u16 = null,
138
139// __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,
154rustc_section_size: u64 = 0,
118segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
119sections: std.MultiArrayList(Section) = .{},
120
121pagezero_segment_cmd_index: ?u8 = null,
122text_segment_cmd_index: ?u8 = null,
123data_const_segment_cmd_index: ?u8 = null,
124data_segment_cmd_index: ?u8 = null,
125linkedit_segment_cmd_index: ?u8 = null,
126
127text_section_index: ?u8 = null,
128stubs_section_index: ?u8 = null,
129stub_helper_section_index: ?u8 = null,
130got_section_index: ?u8 = null,
131la_symbol_ptr_section_index: ?u8 = null,
132data_section_index: ?u8 = null,
155133
156134locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
157135globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
......@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188166
189167error_flags: File.ErrorFlags = File.ErrorFlags{},
190168
191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,
193
194169/// A helper var to indicate if we are at the start of the incremental updates, or
195170/// already somewhere further along the update-and-run chain.
196171/// TODO once we add opening a prelinked output binary from file, this will become
197172/// obsolete as we will carry on where we left off.
198173cold_start: bool = true,
199174
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
222175/// List of atoms that are either synthetic or map directly to the Zig source program.
223176managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224177
......@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},
250203/// We store them here so that we can properly dispose of any allocated
251204/// memory within the atom in the incremental linker.
252205/// TODO consolidate this.
253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
206decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
254207
255208const Entry = struct {
256209 target: SymbolWithLoc,
......@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
408361
409362pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
410363 const cpu_arch = options.target.cpu.arch;
411 const os_tag = options.target.os.tag;
412 const abi = options.target.abi;
413364 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);
417365 const use_llvm = build_options.have_llvm and options.use_llvm;
418366 const use_stage1 = build_options.is_stage1 and options.use_stage1;
419367
......@@ -428,10 +376,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
428376 .file = null,
429377 },
430378 .page_size = page_size,
431 .code_signature = if (requires_adhoc_codesig)
432 CodeSignature.init(page_size)
433 else
434 null,
435379 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436380 .one_shot
437381 else
......@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
562506 var dependent_libs = std.fifo.LinearFifo(struct {
563507 id: Dylib.Id,
564508 parent: u16,
565 }, .Dynamic).init(self.base.allocator);
566 defer dependent_libs.deinit();
509 }, .Dynamic).init(arena);
510
567511 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568512 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569513 }
......@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
573517 try self.createDyldPrivateAtom();
574518 try self.createStubHelperPreambleAtom();
575519 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577520
578521 if (self.unresolved.count() > 0) {
579522 return error.UndefinedSymbolReference;
......@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
583526
584527 if (build_options.enable_logging) {
585528 self.logSymtab();
586 self.logSectionOrdinals();
529 self.logSections();
587530 self.logAtoms();
588531 }
589532
590533 try self.writeAtomsIncremental();
591534
592 try self.setEntryPoint();
593 try self.updateSectionOrdinals();
594 try self.writeLinkeditSegment();
535 var lc_buffer = std.ArrayList(u8).init(arena);
536 const lc_writer = lc_buffer.writer();
537 var ncmds: u32 = 0;
595538
596 if (self.d_sym) |*d_sym| {
597 // Flush debug symbols bundle.
598 try d_sym.flushModule(self.base.allocator, self.base.options);
539 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
540 try writeDylinkerLC(&ncmds, lc_writer);
541
542 self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {
543 error.MissingMainEntrypoint => {
544 self.error_flags.no_entry_point_found = true;
545 },
546 else => |e| return e,
547 };
548
549 try self.writeDylibIdLC(&ncmds, lc_writer);
550 try self.writeRpathLCs(&ncmds, lc_writer);
551
552 {
553 try lc_writer.writeStruct(macho.source_version_command{
554 .cmdsize = @sizeOf(macho.source_version_command),
555 .version = 0x0,
556 });
557 ncmds += 1;
599558 }
600559
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 }
560 try self.writeBuildVersionLC(&ncmds, lc_writer);
561
562 {
563 std.crypto.random.bytes(&self.uuid.uuid);
564 try lc_writer.writeStruct(self.uuid);
565 ncmds += 1;
612566 }
613567
614 if (self.code_signature) |*csig| {
615 csig.clear(self.base.allocator);
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;
568 try self.writeLoadDylibLCs(&ncmds, lc_writer);
569
570 const target = self.base.options.target;
571 const requires_codesig = blk: {
572 if (self.base.options.entitlements) |_| break :blk true;
573 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
574 break :blk true;
575 break :blk false;
576 };
577 var codesig_offset: ?u32 = null;
578 var codesig: ?CodeSignature = if (requires_codesig) blk: {
617579 // Preallocate space for the code signature.
618580 // We need to do this at this stage so that we have the load commands with proper values
619581 // written out to the file.
620582 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621583 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);
623 }
584 var codesig = CodeSignature.init(self.page_size);
585 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
586 if (self.base.options.entitlements) |path| {
587 try codesig.addEntitlements(arena, path);
588 }
589 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
590 break :blk codesig;
591 } else null;
624592
625 try self.writeLoadCommands();
626 try self.writeHeader();
593 var headers_buf = std.ArrayList(u8).init(arena);
594 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
627595
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 }
596 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
597 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
635598
636 assert(!self.load_commands_dirty);
599 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
637600
638 if (self.code_signature) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last
601 if (codesig) |*csig| {
602 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
640603 }
641604
642 if (build_options.enable_link_snapshots) {
643 if (self.base.options.enable_link_snapshots)
644 try self.snapshotState();
605 if (self.d_sym) |*d_sym| {
606 // Flush debug symbols bundle.
607 try d_sym.flushModule(self.base.allocator, self.base.options);
645608 }
646609
610 // if (build_options.enable_link_snapshots) {
611 // if (self.base.options.enable_link_snapshots)
612 // try self.snapshotState();
613 // }
614
647615 if (cache_miss) {
648616 // Update the file with the digest. If it fails we can continue; it only
649617 // means that the next invocation will have an unnecessary cache miss.
......@@ -708,6 +676,9 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
708676 sub_prog_node.context.refresh();
709677 defer sub_prog_node.end();
710678
679 const cpu_arch = self.base.options.target.cpu.arch;
680 const os_tag = self.base.options.target.os.tag;
681 const abi = self.base.options.target.abi;
711682 const is_lib = self.base.options.output_mode == .Lib;
712683 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
713684 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
......@@ -990,40 +961,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
990961 }
991962 }
992963
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
1027964 if (self.base.options.verbose_link) {
1028965 var argv = std.ArrayList([]const u8).init(arena);
1029966
......@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1048985 try argv.append(syslibroot);
1049986 }
1050987
1051 for (rpath_table.keys()) |rpath| {
988 for (self.base.options.rpath_list) |rpath| {
1052989 try argv.append("-rpath");
1053990 try argv.append(rpath);
1054991 }
......@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11571094 var dependent_libs = std.fifo.LinearFifo(struct {
11581095 id: Dylib.Id,
11591096 parent: u16,
1160 }, .Dynamic).init(gpa);
1161 defer dependent_libs.deinit();
1097 }, .Dynamic).init(arena);
1098
11621099 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
11631100 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
11641101 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
11651102 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
11661103
1167 for (self.objects.items) |*object, object_id| {
1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1104 for (self.objects.items) |_, object_id| {
1105 try self.resolveSymbolsInObject(@intCast(u16, object_id));
11691106 }
11701107
11711108 try self.resolveSymbolsInArchives();
11721109 try self.resolveDyldStubBinder();
1173 try self.createDyldPrivateAtom();
1174 try self.createStubHelperPreambleAtom();
11751110 try self.resolveSymbolsInDylibs();
11761111 try self.createMhExecuteHeaderSymbol();
11771112 try self.createDsoHandleSymbol();
1178 try self.addCodeSignatureLC();
11791113 try self.resolveSymbolsAtLoading();
11801114
11811115 if (self.unresolved.count() > 0) {
......@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11881122 return error.FrameworkNotFound;
11891123 }
11901124
1125 for (self.objects.items) |*object| {
1126 try object.scanInputSections(self);
1127 }
1128
1129 try self.createDyldPrivateAtom();
11911130 try self.createTentativeDefAtoms();
1131 try self.createStubHelperPreambleAtom();
11921132
11931133 for (self.objects.items) |*object, object_id| {
11941134 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
......@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11981138 try dead_strip.gcAtoms(self);
11991139 }
12001140
1201 try self.pruneAndSortSections();
12021141 try self.allocateSegments();
12031142 try self.allocateSymbols();
12041143
12051144 try self.allocateSpecialSymbols();
12061145
1207 if (build_options.enable_logging) {
1146 if (build_options.enable_logging or true) {
12081147 self.logSymtab();
1209 self.logSectionOrdinals();
1148 self.logSections();
12101149 self.logAtoms();
12111150 }
12121151
12131152 try self.writeAtomsOneShot();
12141153
1215 if (self.rustc_section_index) |id| {
1216 const sect = self.getSectionPtr(.{
1217 .seg = self.data_segment_cmd_index.?,
1218 .sect = id,
1154 var lc_buffer = std.ArrayList(u8).init(arena);
1155 const lc_writer = lc_buffer.writer();
1156 var ncmds: u32 = 0;
1157
1158 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1159 try writeDylinkerLC(&ncmds, lc_writer);
1160 try self.writeMainLC(&ncmds, lc_writer);
1161 try self.writeDylibIdLC(&ncmds, lc_writer);
1162 try self.writeRpathLCs(&ncmds, lc_writer);
1163
1164 {
1165 try lc_writer.writeStruct(macho.source_version_command{
1166 .cmdsize = @sizeOf(macho.source_version_command),
1167 .version = 0x0,
12191168 });
1220 sect.size = self.rustc_section_size;
1169 ncmds += 1;
12211170 }
12221171
1223 try self.setEntryPoint();
1224 try self.writeLinkeditSegment();
1172 try self.writeBuildVersionLC(&ncmds, lc_writer);
12251173
1226 if (self.code_signature) |*csig| {
1227 csig.clear(gpa);
1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1174 {
1175 var uuid_lc = macho.uuid_command{
1176 .cmdsize = @sizeOf(macho.uuid_command),
1177 .uuid = undefined,
1178 };
1179 std.crypto.random.bytes(&uuid_lc.uuid);
1180 try lc_writer.writeStruct(uuid_lc);
1181 ncmds += 1;
1182 }
1183
1184 try self.writeLoadDylibLCs(&ncmds, lc_writer);
1185
1186 const requires_codesig = blk: {
1187 if (self.base.options.entitlements) |_| break :blk true;
1188 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
1189 break :blk false;
1190 };
1191 var codesig_offset: ?u32 = null;
1192 var codesig: ?CodeSignature = if (requires_codesig) blk: {
12291193 // Preallocate space for the code signature.
12301194 // We need to do this at this stage so that we have the load commands with proper values
12311195 // written out to the file.
12321196 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
12331197 // where the code signature goes into.
1234 try self.writeCodeSignaturePadding(csig);
1235 }
1198 var codesig = CodeSignature.init(self.page_size);
1199 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
1200 if (self.base.options.entitlements) |path| {
1201 try codesig.addEntitlements(arena, path);
1202 }
1203 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
1204 break :blk codesig;
1205 } else null;
1206
1207 var headers_buf = std.ArrayList(u8).init(arena);
1208 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
12361209
1237 try self.writeLoadCommands();
1238 try self.writeHeader();
1210 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1211 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
12391212
1240 assert(!self.load_commands_dirty);
1213 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
12411214
1242 if (self.code_signature) |*csig| {
1243 try self.writeCodeSignature(csig); // code signing always comes last
1215 if (codesig) |*csig| {
1216 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
12441217 }
12451218 }
12461219
......@@ -1395,66 +1368,77 @@ fn resolveFramework(
13951368}
13961369
13971370fn parseObject(self: *MachO, path: []const u8) !bool {
1371 const gpa = self.base.allocator;
13981372 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
13991373 error.FileNotFound => return false,
14001374 else => |e| return e,
14011375 };
1402 errdefer file.close();
1403
1404 const name = try self.base.allocator.dupe(u8, path);
1405 errdefer self.base.allocator.free(name);
1376 defer file.close();
14061377
1378 const name = try gpa.dupe(u8, path);
1379 errdefer gpa.free(name);
1380 const cpu_arch = self.base.options.target.cpu.arch;
14071381 const mtime: u64 = mtime: {
14081382 const stat = file.stat() catch break :mtime 0;
14091383 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
14101384 };
1385 const file_stat = try file.stat();
1386 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
1387 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
14111388
14121389 var object = Object{
14131390 .name = name,
1414 .file = file,
14151391 .mtime = mtime,
1392 .contents = contents,
14161393 };
14171394
1418 object.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1395 object.parse(gpa, cpu_arch) catch |err| switch (err) {
14191396 error.EndOfStream, error.NotObject => {
1420 object.deinit(self.base.allocator);
1397 object.deinit(gpa);
14211398 return false;
14221399 },
14231400 else => |e| return e,
14241401 };
14251402
1426 try self.objects.append(self.base.allocator, object);
1403 try self.objects.append(gpa, object);
14271404
14281405 return true;
14291406}
14301407
14311408fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1409 const gpa = self.base.allocator;
14321410 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
14331411 error.FileNotFound => return false,
14341412 else => |e| return e,
14351413 };
14361414 errdefer file.close();
14371415
1438 const name = try self.base.allocator.dupe(u8, path);
1439 errdefer self.base.allocator.free(name);
1416 const name = try gpa.dupe(u8, path);
1417 errdefer gpa.free(name);
1418 const cpu_arch = self.base.options.target.cpu.arch;
1419 const reader = file.reader();
1420 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
1421 try reader.context.seekTo(fat_offset);
14401422
14411423 var archive = Archive{
14421424 .name = name,
1425 .fat_offset = fat_offset,
14431426 .file = file,
14441427 };
14451428
1446 archive.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1429 archive.parse(gpa, reader) catch |err| switch (err) {
14471430 error.EndOfStream, error.NotArchive => {
1448 archive.deinit(self.base.allocator);
1431 archive.deinit(gpa);
14491432 return false;
14501433 },
14511434 else => |e| return e,
14521435 };
14531436
14541437 if (force_load) {
1455 defer archive.deinit(self.base.allocator);
1438 defer archive.deinit(gpa);
1439 defer file.close();
14561440 // Get all offsets from the ToC
1457 var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator);
1441 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
14581442 defer offsets.deinit();
14591443 for (archive.toc.values()) |offs| {
14601444 for (offs.items) |off| {
......@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
14621446 }
14631447 }
14641448 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 );
1449 const object = try archive.parseObject(gpa, cpu_arch, off);
1450 try self.objects.append(gpa, object);
14711451 }
14721452 } else {
1473 try self.archives.append(self.base.allocator, archive);
1453 try self.archives.append(gpa, archive);
14741454 }
14751455
14761456 return true;
......@@ -1481,6 +1461,7 @@ const ParseDylibError = error{
14811461 EmptyStubFile,
14821462 MismatchedCpuArchitecture,
14831463 UnsupportedCpuArchitecture,
1464 EndOfStream,
14841465} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
14851466
14861467const DylibCreateOpts = struct {
......@@ -1497,43 +1478,53 @@ pub fn parseDylib(
14971478 dependent_libs: anytype,
14981479 opts: DylibCreateOpts,
14991480) ParseDylibError!bool {
1481 const gpa = self.base.allocator;
15001482 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
15011483 error.FileNotFound => return false,
15021484 else => |e| return e,
15031485 };
1504 errdefer file.close();
1486 defer file.close();
1487
1488 const cpu_arch = self.base.options.target.cpu.arch;
1489 const file_stat = try file.stat();
1490 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
15051491
1506 const name = try self.base.allocator.dupe(u8, path);
1507 errdefer self.base.allocator.free(name);
1492 const reader = file.reader();
1493 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
1494 return error.Overflow;
1495 try file.seekTo(fat_offset);
1496 file_size -= fat_offset;
1497
1498 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
1499 defer gpa.free(contents);
15081500
15091501 const dylib_id = @intCast(u16, self.dylibs.items.len);
1510 var dylib = Dylib{
1511 .name = name,
1512 .file = file,
1513 .weak = opts.weak,
1514 };
1502 var dylib = Dylib{ .weak = opts.weak };
15151503
1516 dylib.parse(
1517 self.base.allocator,
1518 self.base.options.target.cpu.arch,
1504 dylib.parseFromBinary(
1505 gpa,
1506 cpu_arch,
15191507 dylib_id,
15201508 dependent_libs,
1509 path,
1510 contents,
15211511 ) catch |err| switch (err) {
15221512 error.EndOfStream, error.NotDylib => {
15231513 try file.seekTo(0);
15241514
1525 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
1526 dylib.deinit(self.base.allocator);
1515 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
1516 dylib.deinit(gpa);
15271517 return false;
15281518 };
15291519 defer lib_stub.deinit();
15301520
15311521 try dylib.parseFromStub(
1532 self.base.allocator,
1522 gpa,
15331523 self.base.options.target,
15341524 lib_stub,
15351525 dylib_id,
15361526 dependent_libs,
1527 path,
15371528 );
15381529 },
15391530 else => |e| return e,
......@@ -1547,13 +1538,13 @@ pub fn parseDylib(
15471538 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
15481539
15491540 // TODO maybe this should be an error and facilitate auto-cleanup?
1550 dylib.deinit(self.base.allocator);
1541 dylib.deinit(gpa);
15511542 return false;
15521543 }
15531544 }
15541545
1555 try self.dylibs.append(self.base.allocator, dylib);
1556 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
1546 try self.dylibs.append(gpa, dylib);
1547 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
15571548
15581549 const should_link_dylib_even_if_unreachable = blk: {
15591550 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
......@@ -1561,8 +1552,7 @@ pub fn parseDylib(
15611552 };
15621553
15631554 if (should_link_dylib_even_if_unreachable) {
1564 try self.addLoadDylibLC(dylib_id);
1565 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1555 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
15661556 }
15671557
15681558 return true;
......@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
15721562 for (files) |file_name| {
15731563 const full_path = full_path: {
15741564 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);
1565 break :full_path try fs.realpath(file_name, &buffer);
15771566 };
1578 defer self.base.allocator.free(full_path);
15791567 log.debug("parsing input file path '{s}'", .{full_path});
15801568
15811569 if (try self.parseObject(full_path)) continue;
......@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
15921580 for (files) |file_name| {
15931581 const full_path = full_path: {
15941582 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);
1583 break :full_path try fs.realpath(file_name, &buffer);
15971584 };
1598 defer self.base.allocator.free(full_path);
15991585 log.debug("parsing and force loading static archive '{s}'", .{full_path});
16001586
16011587 if (try self.parseArchive(full_path, true)) continue;
......@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
16691655 }
16701656}
16711657
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 {
1658pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
16871659 const segname = sect.segName();
16881660 const sectname = sect.sectName();
1689 const res: ?MatchingSection = blk: {
1690 switch (sect.type_()) {
1691 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
1692 if (self.text_const_section_index == null) {
1693 self.text_const_section_index = try self.initSection(
1694 self.text_segment_cmd_index.?,
1695 "__const",
1661 const res: ?u8 = blk: {
1662 if (mem.eql(u8, "__LLVM", segname)) {
1663 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1664 sect.flags, segname, sectname,
1665 });
1666 break :blk null;
1667 }
1668
1669 if (sect.isCode()) {
1670 if (self.text_section_index == null) {
1671 self.text_section_index = try self.initSection(
1672 "__TEXT",
1673 "__text",
1674 sect.size,
1675 sect.@"align",
1676 .{
1677 .flags = macho.S_REGULAR |
1678 macho.S_ATTR_PURE_INSTRUCTIONS |
1679 macho.S_ATTR_SOME_INSTRUCTIONS,
1680 },
1681 );
1682 }
1683 break :blk self.text_section_index.?;
1684 }
1685
1686 if (sect.isDebug()) {
1687 // TODO debug attributes
1688 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1689 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1690 sect.flags, segname, sectname,
1691 });
1692 }
1693 break :blk null;
1694 }
1695
1696 switch (sect.@"type"()) {
1697 macho.S_4BYTE_LITERALS,
1698 macho.S_8BYTE_LITERALS,
1699 macho.S_16BYTE_LITERALS,
1700 => {
1701 break :blk self.getSectionByName("__TEXT", "__const") orelse try self.initSection(
1702 "__TEXT",
1703 "__const",
1704 sect.size,
1705 sect.@"align",
1706 .{},
1707 );
1708 },
1709 macho.S_CSTRING_LITERALS => {
1710 if (mem.startsWith(u8, sectname, "__objc")) {
1711 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1712 segname,
1713 sectname,
16961714 sect.size,
16971715 sect.@"align",
16981716 .{},
16991717 );
17001718 }
1701
1702 break :blk .{
1703 .seg = self.text_segment_cmd_index.?,
1704 .sect = self.text_const_section_index.?,
1705 };
1719 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
1720 "__TEXT",
1721 "__cstring",
1722 sect.size,
1723 sect.@"align",
1724 .{ .flags = macho.S_CSTRING_LITERALS },
1725 );
17061726 },
1707 macho.S_CSTRING_LITERALS => {
1708 if (mem.eql(u8, sectname, "__objc_methname")) {
1709 // TODO it seems the common values within the sections in objects are deduplicated/merged
1710 // on merging the sections' contents.
1711 if (self.objc_methname_section_index == null) {
1712 self.objc_methname_section_index = try self.initSection(
1713 self.text_segment_cmd_index.?,
1714 "__objc_methname",
1727 macho.S_MOD_INIT_FUNC_POINTERS,
1728 macho.S_MOD_TERM_FUNC_POINTERS,
1729 => {
1730 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
1731 "__DATA_CONST",
1732 sectname,
1733 sect.size,
1734 sect.@"align",
1735 .{ .flags = sect.flags },
1736 );
1737 },
1738 macho.S_LITERAL_POINTERS,
1739 macho.S_ZEROFILL,
1740 macho.S_THREAD_LOCAL_VARIABLES,
1741 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1742 macho.S_THREAD_LOCAL_REGULAR,
1743 macho.S_THREAD_LOCAL_ZEROFILL,
1744 => {
1745 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1746 segname,
1747 sectname,
1748 sect.size,
1749 sect.@"align",
1750 .{ .flags = sect.flags },
1751 );
1752 },
1753 macho.S_COALESCED => {
1754 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1755 segname,
1756 sectname,
1757 sect.size,
1758 sect.@"align",
1759 .{},
1760 );
1761 },
1762 macho.S_REGULAR => {
1763 if (mem.eql(u8, segname, "__TEXT")) {
1764 if (mem.eql(u8, sectname, "__rodata") or
1765 mem.eql(u8, sectname, "__typelink") or
1766 mem.eql(u8, sectname, "__itablink") or
1767 mem.eql(u8, sectname, "__gosymtab") or
1768 mem.eql(u8, sectname, "__gopclntab"))
1769 {
1770 break :blk self.getSectionByName("__DATA_CONST", "__const") orelse try self.initSection(
1771 "__DATA_CONST",
1772 "__const",
17151773 sect.size,
17161774 sect.@"align",
17171775 .{},
17181776 );
17191777 }
1720
1721 break :blk .{
1722 .seg = self.text_segment_cmd_index.?,
1723 .sect = self.objc_methname_section_index.?,
1724 };
1725 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
1726 if (self.objc_methtype_section_index == null) {
1727 self.objc_methtype_section_index = try self.initSection(
1728 self.text_segment_cmd_index.?,
1729 "__objc_methtype",
1778 }
1779 if (mem.eql(u8, segname, "__DATA")) {
1780 if (mem.eql(u8, sectname, "__const") or
1781 mem.eql(u8, sectname, "__cfstring") or
1782 mem.eql(u8, sectname, "__objc_classlist") or
1783 mem.eql(u8, sectname, "__objc_imageinfo"))
1784 {
1785 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
1786 try self.initSection(
1787 "__DATA_CONST",
1788 sectname,
17301789 sect.size,
17311790 sect.@"align",
17321791 .{},
17331792 );
1793 } else if (mem.eql(u8, sectname, "__data")) {
1794 if (self.data_section_index == null) {
1795 self.data_section_index = try self.initSection(
1796 segname,
1797 sectname,
1798 sect.size,
1799 sect.@"align",
1800 .{},
1801 );
1802 }
1803 break :blk self.data_section_index.?;
17341804 }
1805 }
1806 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1807 segname,
1808 sectname,
1809 sect.size,
1810 sect.@"align",
1811 .{},
1812 );
1813 },
1814 else => break :blk null,
1815 }
1816 };
1817 return res;
1818}
17351819
1736 break :blk .{
1737 .seg = self.text_segment_cmd_index.?,
1738 .sect = self.objc_methtype_section_index.?,
1739 };
1740 } else if (mem.eql(u8, sectname, "__objc_classname")) {
1741 if (self.objc_classname_section_index == null) {
1742 self.objc_classname_section_index = try self.initSection(
1743 self.text_segment_cmd_index.?,
1744 "__objc_classname",
1745 sect.size,
1746 sect.@"align",
1747 .{},
1748 );
1749 }
1820pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1821 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1822 const atom = try gpa.create(Atom);
1823 errdefer gpa.destroy(atom);
1824 atom.* = Atom.empty;
1825 atom.sym_index = sym_index;
1826 atom.size = size;
1827 atom.alignment = alignment;
17501828
1751 break :blk .{
1752 .seg = self.text_segment_cmd_index.?,
1753 .sect = self.objc_classname_section_index.?,
1754 };
1755 }
1829 try atom.code.resize(gpa, size_usize);
1830 mem.set(u8, atom.code.items, 0);
17561831
1757 if (self.cstring_section_index == null) {
1758 self.cstring_section_index = try self.initSection(
1759 self.text_segment_cmd_index.?,
1760 "__cstring",
1761 sect.size,
1762 sect.@"align",
1763 .{
1764 .flags = macho.S_CSTRING_LITERALS,
1765 },
1766 );
1767 }
1832 return atom;
1833}
17681834
1769 break :blk .{
1770 .seg = self.text_segment_cmd_index.?,
1771 .sect = self.cstring_section_index.?,
1772 };
1773 },
1774 macho.S_LITERAL_POINTERS => {
1775 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
1776 if (self.objc_selrefs_section_index == null) {
1777 self.objc_selrefs_section_index = try self.initSection(
1778 self.data_segment_cmd_index.?,
1779 "__objc_selrefs",
1780 sect.size,
1781 sect.@"align",
1782 .{
1783 .flags = macho.S_LITERAL_POINTERS,
1784 },
1785 );
1786 }
1835pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1836 const section = self.sections.get(sect_id);
1837 const sym = atom.getSymbol(self);
1838 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1839 try atom.resolveRelocs(self);
1840 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1841 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1842}
17871843
1788 break :blk .{
1789 .seg = self.data_segment_cmd_index.?,
1790 .sect = self.objc_selrefs_section_index.?,
1791 };
1792 } else {
1793 // TODO investigate
1794 break :blk null;
1795 }
1796 },
1797 macho.S_MOD_INIT_FUNC_POINTERS => {
1798 if (self.mod_init_func_section_index == null) {
1799 self.mod_init_func_section_index = try self.initSection(
1800 self.data_const_segment_cmd_index.?,
1801 "__mod_init_func",
1802 sect.size,
1803 sect.@"align",
1804 .{
1805 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
1806 },
1807 );
1808 }
1844fn allocateSymbols(self: *MachO) !void {
1845 const slice = self.sections.slice();
1846 for (slice.items(.last_atom)) |last_atom, sect_id| {
1847 const header = slice.items(.header)[sect_id];
1848 var atom = last_atom orelse continue;
18091849
1810 break :blk .{
1811 .seg = self.data_const_segment_cmd_index.?,
1812 .sect = self.mod_init_func_section_index.?,
1813 };
1814 },
1815 macho.S_MOD_TERM_FUNC_POINTERS => {
1816 if (self.mod_term_func_section_index == null) {
1817 self.mod_term_func_section_index = try self.initSection(
1818 self.data_const_segment_cmd_index.?,
1819 "__mod_term_func",
1820 sect.size,
1821 sect.@"align",
1822 .{
1823 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
1824 },
1825 );
1826 }
1850 while (atom.prev) |prev| {
1851 atom = prev;
1852 }
18271853
1828 break :blk .{
1829 .seg = self.data_const_segment_cmd_index.?,
1830 .sect = self.mod_term_func_section_index.?,
1831 };
1832 },
1833 macho.S_ZEROFILL => {
1834 if (self.bss_section_index == null) {
1835 self.bss_section_index = try self.initSection(
1836 self.data_segment_cmd_index.?,
1837 "__bss",
1838 sect.size,
1839 sect.@"align",
1840 .{
1841 .flags = macho.S_ZEROFILL,
1842 },
1843 );
1844 }
1854 const n_sect = @intCast(u8, sect_id + 1);
1855 var base_vaddr = header.addr;
18451856
1846 break :blk .{
1847 .seg = self.data_segment_cmd_index.?,
1848 .sect = self.bss_section_index.?,
1849 };
1850 },
1851 macho.S_THREAD_LOCAL_VARIABLES => {
1852 if (self.tlv_section_index == null) {
1853 self.tlv_section_index = try self.initSection(
1854 self.data_segment_cmd_index.?,
1855 "__thread_vars",
1856 sect.size,
1857 sect.@"align",
1858 .{
1859 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1860 },
1861 );
1862 }
1857 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1858 n_sect,
1859 header.segName(),
1860 header.sectName(),
1861 });
18631862
1864 break :blk .{
1865 .seg = self.data_segment_cmd_index.?,
1866 .sect = self.tlv_section_index.?,
1867 };
1868 },
1869 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1870 if (self.tlv_ptrs_section_index == null) {
1871 self.tlv_ptrs_section_index = try self.initSection(
1872 self.data_segment_cmd_index.?,
1873 "__thread_ptrs",
1874 sect.size,
1875 sect.@"align",
1876 .{
1877 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1878 },
1879 );
1880 }
1863 while (true) {
1864 const alignment = try math.powi(u32, 2, atom.alignment);
1865 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
18811866
1882 break :blk .{
1883 .seg = self.data_segment_cmd_index.?,
1884 .sect = self.tlv_ptrs_section_index.?,
1885 };
1886 },
1887 macho.S_THREAD_LOCAL_REGULAR => {
1888 if (self.tlv_data_section_index == null) {
1889 self.tlv_data_section_index = try self.initSection(
1890 self.data_segment_cmd_index.?,
1891 "__thread_data",
1892 sect.size,
1893 sect.@"align",
1894 .{
1895 .flags = macho.S_THREAD_LOCAL_REGULAR,
1896 },
1897 );
1898 }
1867 const sym = atom.getSymbolPtr(self);
1868 sym.n_value = base_vaddr;
1869 sym.n_sect = n_sect;
18991870
1900 break :blk .{
1901 .seg = self.data_segment_cmd_index.?,
1902 .sect = self.tlv_data_section_index.?,
1903 };
1904 },
1905 macho.S_THREAD_LOCAL_ZEROFILL => {
1906 if (self.tlv_bss_section_index == null) {
1907 self.tlv_bss_section_index = try self.initSection(
1908 self.data_segment_cmd_index.?,
1909 "__thread_bss",
1910 sect.size,
1911 sect.@"align",
1912 .{
1913 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
1914 },
1915 );
1916 }
1871 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
19171872
1918 break :blk .{
1919 .seg = self.data_segment_cmd_index.?,
1920 .sect = self.tlv_bss_section_index.?,
1921 };
1922 },
1923 macho.S_COALESCED => {
1924 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
1925 // TODO I believe __eh_frame is currently part of __unwind_info section
1926 // in the latest ld64 output.
1927 if (self.eh_frame_section_index == null) {
1928 self.eh_frame_section_index = try self.initSection(
1929 self.text_segment_cmd_index.?,
1930 "__eh_frame",
1931 sect.size,
1932 sect.@"align",
1933 .{},
1934 );
1935 }
1873 // Update each symbol contained within the atom
1874 for (atom.contained.items) |sym_at_off| {
1875 const contained_sym = self.getSymbolPtr(.{
1876 .sym_index = sym_at_off.sym_index,
1877 .file = atom.file,
1878 });
1879 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1880 contained_sym.n_sect = n_sect;
1881 }
19361882
1937 break :blk .{
1938 .seg = self.text_segment_cmd_index.?,
1939 .sect = self.eh_frame_section_index.?,
1940 };
1941 }
1942
1943 // TODO audit this: is this the right mapping?
1944 if (self.data_const_section_index == null) {
1945 self.data_const_section_index = try self.initSection(
1946 self.data_const_segment_cmd_index.?,
1947 "__const",
1948 sect.size,
1949 sect.@"align",
1950 .{},
1951 );
1952 }
1953
1954 break :blk .{
1955 .seg = self.data_const_segment_cmd_index.?,
1956 .sect = self.data_const_section_index.?,
1957 };
1958 },
1959 macho.S_REGULAR => {
1960 if (sect.isCode()) {
1961 if (self.text_section_index == null) {
1962 self.text_section_index = try self.initSection(
1963 self.text_segment_cmd_index.?,
1964 "__text",
1965 sect.size,
1966 sect.@"align",
1967 .{
1968 .flags = macho.S_REGULAR |
1969 macho.S_ATTR_PURE_INSTRUCTIONS |
1970 macho.S_ATTR_SOME_INSTRUCTIONS,
1971 },
1972 );
1973 }
1974
1975 break :blk .{
1976 .seg = self.text_segment_cmd_index.?,
1977 .sect = self.text_section_index.?,
1978 };
1979 }
1980 if (sect.isDebug()) {
1981 // TODO debug attributes
1982 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1983 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1984 sect.flags, segname, sectname,
1985 });
1986 }
1987 break :blk null;
1988 }
1989
1990 if (mem.eql(u8, segname, "__TEXT")) {
1991 if (mem.eql(u8, sectname, "__ustring")) {
1992 if (self.ustring_section_index == null) {
1993 self.ustring_section_index = try self.initSection(
1994 self.text_segment_cmd_index.?,
1995 "__ustring",
1996 sect.size,
1997 sect.@"align",
1998 .{},
1999 );
2000 }
2001
2002 break :blk .{
2003 .seg = self.text_segment_cmd_index.?,
2004 .sect = self.ustring_section_index.?,
2005 };
2006 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
2007 if (self.gcc_except_tab_section_index == null) {
2008 self.gcc_except_tab_section_index = try self.initSection(
2009 self.text_segment_cmd_index.?,
2010 "__gcc_except_tab",
2011 sect.size,
2012 sect.@"align",
2013 .{},
2014 );
2015 }
2016
2017 break :blk .{
2018 .seg = self.text_segment_cmd_index.?,
2019 .sect = self.gcc_except_tab_section_index.?,
2020 };
2021 } else if (mem.eql(u8, sectname, "__objc_methlist")) {
2022 if (self.objc_methlist_section_index == null) {
2023 self.objc_methlist_section_index = try self.initSection(
2024 self.text_segment_cmd_index.?,
2025 "__objc_methlist",
2026 sect.size,
2027 sect.@"align",
2028 .{},
2029 );
2030 }
2031
2032 break :blk .{
2033 .seg = self.text_segment_cmd_index.?,
2034 .sect = self.objc_methlist_section_index.?,
2035 };
2036 } else if (mem.eql(u8, sectname, "__rodata") or
2037 mem.eql(u8, sectname, "__typelink") or
2038 mem.eql(u8, sectname, "__itablink") or
2039 mem.eql(u8, sectname, "__gosymtab") or
2040 mem.eql(u8, sectname, "__gopclntab"))
2041 {
2042 if (self.data_const_section_index == null) {
2043 self.data_const_section_index = try self.initSection(
2044 self.data_const_segment_cmd_index.?,
2045 "__const",
2046 sect.size,
2047 sect.@"align",
2048 .{},
2049 );
2050 }
2051
2052 break :blk .{
2053 .seg = self.data_const_segment_cmd_index.?,
2054 .sect = self.data_const_section_index.?,
2055 };
2056 } else {
2057 if (self.text_const_section_index == null) {
2058 self.text_const_section_index = try self.initSection(
2059 self.text_segment_cmd_index.?,
2060 "__const",
2061 sect.size,
2062 sect.@"align",
2063 .{},
2064 );
2065 }
2066
2067 break :blk .{
2068 .seg = self.text_segment_cmd_index.?,
2069 .sect = self.text_const_section_index.?,
2070 };
2071 }
2072 }
2073
2074 if (mem.eql(u8, segname, "__DATA_CONST")) {
2075 if (self.data_const_section_index == null) {
2076 self.data_const_section_index = try self.initSection(
2077 self.data_const_segment_cmd_index.?,
2078 "__const",
2079 sect.size,
2080 sect.@"align",
2081 .{},
2082 );
2083 }
2084
2085 break :blk .{
2086 .seg = self.data_const_segment_cmd_index.?,
2087 .sect = self.data_const_section_index.?,
2088 };
2089 }
2090
2091 if (mem.eql(u8, segname, "__DATA")) {
2092 if (mem.eql(u8, sectname, "__const")) {
2093 if (self.data_const_section_index == null) {
2094 self.data_const_section_index = try self.initSection(
2095 self.data_const_segment_cmd_index.?,
2096 "__const",
2097 sect.size,
2098 sect.@"align",
2099 .{},
2100 );
2101 }
2102
2103 break :blk .{
2104 .seg = self.data_const_segment_cmd_index.?,
2105 .sect = self.data_const_section_index.?,
2106 };
2107 } else if (mem.eql(u8, sectname, "__cfstring")) {
2108 if (self.objc_cfstring_section_index == null) {
2109 self.objc_cfstring_section_index = try self.initSection(
2110 self.data_const_segment_cmd_index.?,
2111 "__cfstring",
2112 sect.size,
2113 sect.@"align",
2114 .{},
2115 );
2116 }
2117
2118 break :blk .{
2119 .seg = self.data_const_segment_cmd_index.?,
2120 .sect = self.objc_cfstring_section_index.?,
2121 };
2122 } else if (mem.eql(u8, sectname, "__objc_classlist")) {
2123 if (self.objc_classlist_section_index == null) {
2124 self.objc_classlist_section_index = try self.initSection(
2125 self.data_const_segment_cmd_index.?,
2126 "__objc_classlist",
2127 sect.size,
2128 sect.@"align",
2129 .{},
2130 );
2131 }
2132
2133 break :blk .{
2134 .seg = self.data_const_segment_cmd_index.?,
2135 .sect = self.objc_classlist_section_index.?,
2136 };
2137 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
2138 if (self.objc_imageinfo_section_index == null) {
2139 self.objc_imageinfo_section_index = try self.initSection(
2140 self.data_const_segment_cmd_index.?,
2141 "__objc_imageinfo",
2142 sect.size,
2143 sect.@"align",
2144 .{},
2145 );
2146 }
2147
2148 break :blk .{
2149 .seg = self.data_const_segment_cmd_index.?,
2150 .sect = self.objc_imageinfo_section_index.?,
2151 };
2152 } else if (mem.eql(u8, sectname, "__objc_const")) {
2153 if (self.objc_const_section_index == null) {
2154 self.objc_const_section_index = try self.initSection(
2155 self.data_segment_cmd_index.?,
2156 "__objc_const",
2157 sect.size,
2158 sect.@"align",
2159 .{},
2160 );
2161 }
2162
2163 break :blk .{
2164 .seg = self.data_segment_cmd_index.?,
2165 .sect = self.objc_const_section_index.?,
2166 };
2167 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
2168 if (self.objc_classrefs_section_index == null) {
2169 self.objc_classrefs_section_index = try self.initSection(
2170 self.data_segment_cmd_index.?,
2171 "__objc_classrefs",
2172 sect.size,
2173 sect.@"align",
2174 .{},
2175 );
2176 }
2177
2178 break :blk .{
2179 .seg = self.data_segment_cmd_index.?,
2180 .sect = self.objc_classrefs_section_index.?,
2181 };
2182 } else if (mem.eql(u8, sectname, "__objc_data")) {
2183 if (self.objc_data_section_index == null) {
2184 self.objc_data_section_index = try self.initSection(
2185 self.data_segment_cmd_index.?,
2186 "__objc_data",
2187 sect.size,
2188 sect.@"align",
2189 .{},
2190 );
2191 }
2192
2193 break :blk .{
2194 .seg = self.data_segment_cmd_index.?,
2195 .sect = self.objc_data_section_index.?,
2196 };
2197 } else if (mem.eql(u8, sectname, ".rustc")) {
2198 if (self.rustc_section_index == null) {
2199 self.rustc_section_index = try self.initSection(
2200 self.data_segment_cmd_index.?,
2201 ".rustc",
2202 sect.size,
2203 sect.@"align",
2204 .{},
2205 );
2206 // We need to preserve the section size for rustc to properly
2207 // decompress the metadata.
2208 self.rustc_section_size = sect.size;
2209 }
2210
2211 break :blk .{
2212 .seg = self.data_segment_cmd_index.?,
2213 .sect = self.rustc_section_index.?,
2214 };
2215 } else {
2216 if (self.data_section_index == null) {
2217 self.data_section_index = try self.initSection(
2218 self.data_segment_cmd_index.?,
2219 "__data",
2220 sect.size,
2221 sect.@"align",
2222 .{},
2223 );
2224 }
2225
2226 break :blk .{
2227 .seg = self.data_segment_cmd_index.?,
2228 .sect = self.data_section_index.?,
2229 };
2230 }
2231 }
2232
2233 if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
2234 log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
2235 sect.flags, segname, sectname,
2236 });
2237 }
2238
2239 break :blk null;
2240 },
2241 else => break :blk null,
2242 }
2243 };
2244 return res;
2245}
2246
2247pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
2248 const size_usize = math.cast(usize, size) orelse return error.Overflow;
2249 const atom = try gpa.create(Atom);
2250 errdefer gpa.destroy(atom);
2251 atom.* = Atom.empty;
2252 atom.sym_index = sym_index;
2253 atom.size = size;
2254 atom.alignment = alignment;
2255
2256 try atom.code.resize(gpa, size_usize);
2257 mem.set(u8, atom.code.items, 0);
2258
2259 return atom;
2260}
2261
2262pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2263 const sect = self.getSection(match);
2264 const sym = atom.getSymbol(self);
2265 const file_offset = sect.offset + sym.n_value - sect.addr;
2266 try atom.resolveRelocs(self);
2267 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
2268 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2269}
2270
2271fn 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.*;
2276
2277 while (atom.prev) |prev| {
2278 atom = prev;
2279 }
2280
2281 const n_sect = self.getSectionOrdinal(match);
2282 const sect = self.getSection(match);
2283 var base_vaddr = sect.addr;
2284
2285 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ n_sect, sect.segName(), sect.sectName() });
2286
2287 while (true) {
2288 const alignment = try math.powi(u32, 2, atom.alignment);
2289 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
2290
2291 const sym = atom.getSymbolPtr(self);
2292 sym.n_value = base_vaddr;
2293 sym.n_sect = n_sect;
2294
2295 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
2296
2297 // Update each symbol contained within the atom
2298 for (atom.contained.items) |sym_at_off| {
2299 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2300 contained_sym.n_value = base_vaddr + sym_at_off.offset;
2301 contained_sym.n_sect = n_sect;
2302 }
2303
2304 base_vaddr += atom.size;
1883 base_vaddr += atom.size;
23051884
23061885 if (atom.next) |next| {
23071886 atom = next;
......@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {
23101889 }
23111890}
23121891
2313fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void {
2314 var atom = self.atoms.get(match) orelse return;
2315
2316 while (true) {
2317 const atom_sym = atom.getSymbolPtr(self);
2318 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
2319
2320 for (atom.contained.items) |sym_at_off| {
2321 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2322 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2323 }
2324
2325 if (atom.prev) |prev| {
2326 atom = prev;
2327 } else break;
2328 }
2329}
2330
23311892fn allocateSpecialSymbols(self: *MachO) !void {
23321893 for (&[_][]const u8{
23331894 "___dso_handle",
......@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23361897 const global = self.globals.get(name) orelse continue;
23371898 if (global.file != null) continue;
23381899 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;
1900 const seg = self.segments.items[self.text_segment_cmd_index.?];
1901 sym.n_sect = 1;
1902 sym.n_value = seg.vmaddr;
23451903
23461904 log.debug("allocating {s} at the start of {s}", .{
23471905 name,
2348 seg.inner.segName(),
1906 seg.segName(),
23491907 });
23501908 }
23511909}
......@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23531911fn writeAtomsOneShot(self: *MachO) !void {
23541912 assert(self.mode == .one_shot);
23551913
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.*;
1914 const gpa = self.base.allocator;
1915 const slice = self.sections.slice();
1916
1917 for (slice.items(.last_atom)) |last_atom, sect_id| {
1918 const header = slice.items(.header)[sect_id];
1919 if (header.size == 0) continue;
1920 var atom = last_atom.?;
23601921
2361 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
1922 if (header.isZerofill()) continue;
23621923
2363 var buffer = std.ArrayList(u8).init(self.base.allocator);
1924 var buffer = std.ArrayList(u8).init(gpa);
23641925 defer buffer.deinit();
2365 try buffer.ensureTotalCapacity(math.cast(usize, sect.size) orelse return error.Overflow);
1926 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
23661927
2367 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
1928 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
23681929
23691930 while (atom.prev) |prev| {
23701931 atom = prev;
......@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {
23991960 if (atom.next) |next| {
24001961 atom = next;
24011962 } 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);
1963 assert(buffer.items.len == header.size);
1964 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1965 try self.base.file.?.pwriteAll(buffer.items, header.offset);
24051966 break;
24061967 }
24071968 }
24081969 }
24091970}
24101971
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)
1972fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
1973 const header = self.sections.items(.header)[sect_id];
1974 const min_alignment: u3 = if (!header.isCode())
24141975 1
24151976 else switch (self.base.options.target.cpu.arch) {
24161977 .aarch64 => @sizeOf(u32),
......@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24211982 const len = @divExact(size, min_alignment);
24221983 var i: usize = 0;
24231984 while (i < len) : (i += 1) {
2424 if (!is_code) {
1985 if (!header.isCode()) {
24251986 try writer.writeByte(0);
24261987 } else switch (self.base.options.target.cpu.arch) {
24271988 .aarch64 => {
......@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24392000fn writeAtomsIncremental(self: *MachO) !void {
24402001 assert(self.mode == .incremental);
24412002
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.*;
2003 const slice = self.sections.slice();
2004 for (slice.items(.last_atom)) |last, i| {
2005 var atom: *Atom = last orelse continue;
2006 const sect_i = @intCast(u8, i);
2007 const header = slice.items(.header)[sect_i];
24472008
2448 // TODO handle zerofill in stage2
2449 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
2009 if (header.isZerofill()) continue;
24502010
2451 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
2011 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
24522012
24532013 while (true) {
24542014 if (atom.dirty) {
2455 try self.writeAtom(atom, match);
2015 try self.writeAtom(atom, sect_i);
24562016 atom.dirty = false;
24572017 }
24582018
......@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25032063 try self.managed_atoms.append(gpa, atom);
25042064 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25052065
2506 try self.allocateAtomCommon(atom, .{
2507 .seg = self.data_const_segment_cmd_index.?,
2508 .sect = self.got_section_index.?,
2509 });
2066 try self.allocateAtomCommon(atom, self.got_section_index.?);
25102067
25112068 return atom;
25122069}
......@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25352092 try self.managed_atoms.append(gpa, atom);
25362093 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25372094
2538 const match = (try self.getMatchingSection(.{
2095 const match = (try self.getOutputSection(.{
25392096 .segname = makeStaticString("__DATA"),
25402097 .sectname = makeStaticString("__thread_ptrs"),
25412098 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
......@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
25612118 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
25622119 self.dyld_private_atom = atom;
25632120
2564 try self.allocateAtomCommon(atom, .{
2565 .seg = self.data_segment_cmd_index.?,
2566 .sect = self.data_section_index.?,
2567 });
2121 try self.allocateAtomCommon(atom, self.data_section_index.?);
25682122
25692123 try self.managed_atoms.append(gpa, atom);
25702124 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
26922246 }
26932247 self.stub_helper_preamble_atom = atom;
26942248
2695 try self.allocateAtomCommon(atom, .{
2696 .seg = self.text_segment_cmd_index.?,
2697 .sect = self.stub_helper_section_index.?,
2698 });
2249 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
26992250
27002251 try self.managed_atoms.append(gpa, atom);
27012252 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
27712322 try self.managed_atoms.append(gpa, atom);
27722323 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
27732324
2774 try self.allocateAtomCommon(atom, .{
2775 .seg = self.text_segment_cmd_index.?,
2776 .sect = self.stub_helper_section_index.?,
2777 });
2325 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
27782326
27792327 return atom;
27802328}
......@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
28142362 try self.managed_atoms.append(gpa, atom);
28152363 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28162364
2817 try self.allocateAtomCommon(atom, .{
2818 .seg = self.data_segment_cmd_index.?,
2819 .sect = self.la_symbol_ptr_section_index.?,
2820 });
2365 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
28212366
28222367 return atom;
28232368}
......@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
28962441 try self.managed_atoms.append(gpa, atom);
28972442 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28982443
2899 try self.allocateAtomCommon(atom, .{
2900 .seg = self.text_segment_cmd_index.?,
2901 .sect = self.stubs_section_index.?,
2902 });
2444 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
29032445
29042446 return atom;
29052447}
......@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29172459
29182460 // Convert any tentative definition into a regular symbol and allocate
29192461 // 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
29262462 const size = sym.n_value;
29272463 const alignment = (sym.n_desc >> 8) & 0x0f;
2464 const n_sect = (try self.getOutputSection(.{
2465 .segname = makeStaticString("__DATA"),
2466 .sectname = makeStaticString("__bss"),
2467 .flags = macho.S_ZEROFILL,
2468 })).?;
29282469
29292470 sym.* = .{
29302471 .n_strx = sym.n_strx,
29312472 .n_type = macho.N_SECT | macho.N_EXT,
2932 .n_sect = 0,
2473 .n_sect = n_sect,
29332474 .n_desc = 0,
29342475 .n_value = 0,
29352476 };
......@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29372478 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
29382479 atom.file = global.file;
29392480
2940 try self.allocateAtomCommon(atom, match);
2481 try self.allocateAtomCommon(atom, n_sect);
29412482
29422483 if (global.file) |file| {
29432484 const object = &self.objects.items[file];
......@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
30602601 gop.value_ptr.* = current;
30612602}
30622603
3063fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2604fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2605 const object = &self.objects.items[object_id];
30642606 log.debug("resolving symbols in '{s}'", .{object.name});
30652607
30662608 for (object.symtab.items) |sym, index| {
......@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
31152657fn resolveSymbolsInArchives(self: *MachO) !void {
31162658 if (self.archives.items.len == 0) return;
31172659
2660 const gpa = self.base.allocator;
2661 const cpu_arch = self.base.options.target.cpu.arch;
31182662 var next_sym: usize = 0;
31192663 loop: while (next_sym < self.unresolved.count()) {
31202664 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
......@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
31292673 assert(offsets.items.len > 0);
31302674
31312675 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);
2676 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
2677 try self.objects.append(gpa, object);
2678 try self.resolveSymbolsInObject(object_id);
31392679
31402680 continue :loop;
31412681 }
......@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31592699
31602700 const dylib_id = @intCast(u16, id);
31612701 if (!self.referenced_dylibs.contains(dylib_id)) {
3162 try self.addLoadDylibLC(dylib_id);
31632702 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
31642703 }
31652704
......@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32572796
32582797 const dylib_id = @intCast(u16, id);
32592798 if (!self.referenced_dylibs.contains(dylib_id)) {
3260 try self.addLoadDylibLC(dylib_id);
32612799 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
32622800 }
32632801
......@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32802818 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
32812819}
32822820
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;
2821fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2822 const name_len = mem.sliceTo(default_dyld_path, 0).len;
2823 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2824 u64,
2825 @sizeOf(macho.dylinker_command) + name_len,
2826 @sizeOf(u64),
2827 ));
2828 try lc_writer.writeStruct(macho.dylinker_command{
2829 .cmd = .LOAD_DYLINKER,
2830 .cmdsize = cmdsize,
2831 .name = @sizeOf(macho.dylinker_command),
2832 });
2833 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
2834 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
2835 if (padding > 0) {
2836 try lc_writer.writeByteNTimes(0, padding);
2837 }
2838 ncmds.* += 1;
32972839}
32982840
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,
2841fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2842 if (self.base.options.output_mode != .Exe) return;
2843 const seg = self.segments.items[self.text_segment_cmd_index.?];
2844 const global = try self.getEntryPoint();
2845 const sym = self.getSymbol(global);
2846 try lc_writer.writeStruct(macho.entry_point_command{
2847 .cmd = .MAIN,
2848 .cmdsize = @sizeOf(macho.entry_point_command),
2849 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
2850 .stacksize = self.base.options.stack_size_override orelse 0,
2851 });
2852 ncmds.* += 1;
2853}
2854
2855const WriteDylibLCCtx = struct {
2856 cmd: macho.LC,
2857 name: []const u8,
2858 timestamp: u32 = 2,
2859 current_version: u32 = 0x10000,
2860 compatibility_version: u32 = 0x10000,
2861};
2862
2863fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2864 const name_len = ctx.name.len + 1;
2865 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2866 u64,
2867 @sizeOf(macho.dylib_command) + name_len,
2868 @sizeOf(u64),
2869 ));
2870 try lc_writer.writeStruct(macho.dylib_command{
2871 .cmd = ctx.cmd,
2872 .cmdsize = cmdsize,
2873 .dylib = .{
2874 .name = @sizeOf(macho.dylib_command),
2875 .timestamp = ctx.timestamp,
2876 .current_version = ctx.current_version,
2877 .compatibility_version = ctx.compatibility_version,
33082878 },
33092879 });
3310 self.load_commands_dirty = true;
2880 try lc_writer.writeAll(ctx.name);
2881 try lc_writer.writeByte(0);
2882 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
2883 if (padding > 0) {
2884 try lc_writer.writeByteNTimes(0, padding);
2885 }
2886 ncmds.* += 1;
33112887}
33122888
3313fn setEntryPoint(self: *MachO) !void {
3314 if (self.base.options.output_mode != .Exe) return;
2889fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2890 if (self.base.options.output_mode != .Lib) return;
2891 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
2892 const curr = self.base.options.version orelse std.builtin.Version{
2893 .major = 1,
2894 .minor = 0,
2895 .patch = 0,
2896 };
2897 const compat = self.base.options.compatibility_version orelse std.builtin.Version{
2898 .major = 1,
2899 .minor = 0,
2900 .patch = 0,
2901 };
2902 try writeDylibLC(.{
2903 .cmd = .ID_DYLIB,
2904 .name = install_name,
2905 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
2906 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
2907 }, ncmds, lc_writer);
2908}
33152909
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;
2910const RpathIterator = struct {
2911 buffer: []const []const u8,
2912 table: std.StringHashMap(void),
2913 count: usize = 0,
2914
2915 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
2916 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
2917 }
2918
2919 fn deinit(it: *RpathIterator) void {
2920 it.table.deinit();
2921 }
2922
2923 fn next(it: *RpathIterator) !?[]const u8 {
2924 while (true) {
2925 if (it.count >= it.buffer.len) return null;
2926 const rpath = it.buffer[it.count];
2927 it.count += 1;
2928 const gop = try it.table.getOrPut(rpath);
2929 if (gop.found_existing) continue;
2930 return rpath;
2931 }
2932 }
2933};
2934
2935fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2936 const gpa = self.base.allocator;
2937
2938 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
2939 defer it.deinit();
2940
2941 while (try it.next()) |rpath| {
2942 const rpath_len = rpath.len + 1;
2943 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2944 u64,
2945 @sizeOf(macho.rpath_command) + rpath_len,
2946 @sizeOf(u64),
2947 ));
2948 try lc_writer.writeStruct(macho.rpath_command{
2949 .cmdsize = cmdsize,
2950 .path = @sizeOf(macho.rpath_command),
2951 });
2952 try lc_writer.writeAll(rpath);
2953 try lc_writer.writeByte(0);
2954 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
2955 if (padding > 0) {
2956 try lc_writer.writeByteNTimes(0, padding);
2957 }
2958 ncmds.* += 1;
2959 }
2960}
2961
2962fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2963 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
2964 const platform_version = blk: {
2965 const ver = self.base.options.target.os.version_range.semver.min;
2966 const platform_version = ver.major << 16 | ver.minor << 8;
2967 break :blk platform_version;
2968 };
2969 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
2970 const ver = sdk.version;
2971 const sdk_version = ver.major << 16 | ver.minor << 8;
2972 break :blk sdk_version;
2973 } else platform_version;
2974 const is_simulator_abi = self.base.options.target.abi == .simulator;
2975 try lc_writer.writeStruct(macho.build_version_command{
2976 .cmdsize = cmdsize,
2977 .platform = switch (self.base.options.target.os.tag) {
2978 .macos => .MACOS,
2979 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
2980 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
2981 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
2982 else => unreachable,
2983 },
2984 .minos = platform_version,
2985 .sdk = sdk_version,
2986 .ntools = 1,
2987 });
2988 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
2989 .tool = .LD,
2990 .version = 0x0,
2991 }));
2992 ncmds.* += 1;
2993}
2994
2995fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2996 for (self.referenced_dylibs.keys()) |id| {
2997 const dylib = self.dylibs.items[id];
2998 const dylib_id = dylib.id orelse unreachable;
2999 try writeDylibLC(.{
3000 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3001 .name = dylib_id.name,
3002 .timestamp = dylib_id.timestamp,
3003 .current_version = dylib_id.current_version,
3004 .compatibility_version = dylib_id.compatibility_version,
3005 }, ncmds, lc_writer);
3006 }
33243007}
33253008
33263009pub fn deinit(self: *MachO) void {
......@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {
33343017 d_sym.deinit(gpa);
33353018 }
33363019
3337 self.section_ordinals.deinit(gpa);
33383020 self.tlv_ptr_entries.deinit(gpa);
33393021 self.tlv_ptr_entries_free_list.deinit(gpa);
33403022 self.tlv_ptr_entries_table.deinit(gpa);
......@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {
33713053 self.dylibs_map.deinit(gpa);
33723054 self.referenced_dylibs.deinit(gpa);
33733055
3374 for (self.load_commands.items) |*lc| {
3375 lc.deinit(gpa);
3056 self.segments.deinit(gpa);
3057
3058 for (self.sections.items(.free_list)) |*list| {
3059 list.deinit(gpa);
33763060 }
3377 self.load_commands.deinit(gpa);
3061 self.sections.deinit(gpa);
33783062
33793063 for (self.managed_atoms.items) |atom| {
33803064 atom.deinit(gpa);
33813065 gpa.destroy(atom);
33823066 }
33833067 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 }
3068
33923069 if (self.base.options.module) |mod| {
33933070 for (self.decls.keys()) |decl_index| {
33943071 const decl = mod.declPtr(decl_index);
......@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {
34083085 }
34093086
34103087 self.atom_by_index_table.deinit(gpa);
3411
3412 if (self.code_signature) |*csig| {
3413 csig.deinit(gpa);
3414 }
34153088}
34163089
34173090pub fn closeFiles(self: MachO) void {
3418 for (self.objects.items) |object| {
3419 object.file.close();
3420 }
34213091 for (self.archives.items) |archive| {
34223092 archive.file.close();
34233093 }
3424 for (self.dylibs.items) |dylib| {
3425 dylib.file.close();
3426 }
34273094 if (self.d_sym) |ds| {
34283095 ds.file.close();
34293096 }
34303097}
34313098
3432fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
3099fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
34333100 log.debug("freeAtom {*}", .{atom});
34343101 if (!owns_atom) {
34353102 atom.deinit(self.base.allocator);
34363103 }
34373104
3438 const free_list = self.atom_free_lists.getPtr(match).?;
3105 const free_list = &self.sections.items(.free_list)[sect_id];
34393106 var already_have_free_list_node = false;
34403107 {
34413108 var i: usize = 0;
......@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34523119 }
34533120 }
34543121
3455 if (self.atoms.getPtr(match)) |last_atom| {
3456 if (last_atom.* == atom) {
3122 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3123 if (maybe_last_atom.*) |last_atom| {
3124 if (last_atom == atom) {
34573125 if (atom.prev) |prev| {
34583126 // TODO shrink the section size here
3459 last_atom.* = prev;
3127 maybe_last_atom.* = prev;
34603128 } else {
3461 _ = self.atoms.fetchRemove(match);
3129 maybe_last_atom.* = null;
34623130 }
34633131 }
34643132 }
......@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34863154 }
34873155}
34883156
3489fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSection) void {
3157fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, sect_id: u8) void {
34903158 _ = self;
34913159 _ = atom;
34923160 _ = new_block_size;
3493 _ = match;
3161 _ = sect_id;
34943162 // TODO check the new capacity, and if it crosses the size threshold into a big enough
34953163 // capacity, insert a free list node for it.
34963164}
34973165
3498fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
3166fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u8) !u64 {
34993167 const sym = atom.getSymbol(self);
35003168 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
35013169 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
35023170 if (!need_realloc) return sym.n_value;
3503 return self.allocateAtom(atom, new_atom_size, alignment, match);
3171 return self.allocateAtom(atom, new_atom_size, alignment, sect_id);
35043172}
35053173
35063174fn allocateSymbol(self: *MachO) !u32 {
......@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
36713339}
36723340
36733341pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
3674 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3342 const gpa = self.base.allocator;
3343
3344 var code_buffer = std.ArrayList(u8).init(gpa);
36753345 defer code_buffer.deinit();
36763346
3677 const gpa = self.base.allocator;
36783347 const module = self.base.options.module.?;
36793348 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
36803349 if (!gop.found_existing) {
......@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
37253394 atom.code.clearRetainingCapacity();
37263395 try atom.code.appendSlice(gpa, code);
37273396
3728 const match = try self.getMatchingSectionAtom(
3397 const sect_id = try self.getOutputSectionAtom(
37293398 atom,
37303399 decl_name,
37313400 typed_value.ty,
37323401 typed_value.val,
37333402 required_alignment,
37343403 );
3735 const addr = try self.allocateAtom(atom, code.len, required_alignment, match);
3404 const addr = try self.allocateAtom(atom, code.len, required_alignment, sect_id);
37363405
37373406 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
37383407 log.debug(" (required alignment 0x{x})", .{required_alignment});
37393408
3740 errdefer self.freeAtom(atom, match, true);
3409 errdefer self.freeAtom(atom, sect_id, true);
37413410
37423411 const symbol = atom.getSymbolPtr(self);
37433412 symbol.* = .{
37443413 .n_strx = name_str_index,
37453414 .n_type = macho.N_SECT,
3746 .n_sect = self.getSectionOrdinal(match),
3415 .n_sect = sect_id + 1,
37473416 .n_desc = 0,
37483417 .n_value = addr,
37493418 };
......@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
38943563 }
38953564}
38963565
3897fn getMatchingSectionAtom(
3566fn getOutputSectionAtom(
38983567 self: *MachO,
38993568 atom: *Atom,
39003569 name: []const u8,
39013570 ty: Type,
39023571 val: Value,
39033572 alignment: u32,
3904) !MatchingSection {
3573) !u8 {
39053574 const code = atom.code.items;
39063575 const mod = self.base.options.module.?;
39073576 const align_log_2 = math.log2(alignment);
39083577 const zig_ty = ty.zigTypeTag();
39093578 const mode = self.base.options.optimize_mode;
3910 const match: MatchingSection = blk: {
3579 const sect_id: u8 = blk: {
39113580 // TODO finish and audit this function
39123581 if (val.isUndefDeep()) {
39133582 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3914 break :blk MatchingSection{
3915 .seg = self.data_segment_cmd_index.?,
3916 .sect = self.bss_section_index.?,
3917 };
3583 break :blk (try self.getOutputSection(.{
3584 .segname = makeStaticString("__DATA"),
3585 .sectname = makeStaticString("__bss"),
3586 .size = code.len,
3587 .@"align" = align_log_2,
3588 })).?;
39183589 } else {
3919 break :blk MatchingSection{
3920 .seg = self.data_segment_cmd_index.?,
3921 .sect = self.data_section_index.?,
3922 };
3590 break :blk self.data_section_index.?;
39233591 }
39243592 }
39253593
39263594 if (val.castTag(.variable)) |_| {
3927 break :blk MatchingSection{
3928 .seg = self.data_segment_cmd_index.?,
3929 .sect = self.data_section_index.?,
3930 };
3595 break :blk self.data_section_index.?;
39313596 }
39323597
39333598 if (needsPointerRebase(ty, val, mod)) {
3934 break :blk (try self.getMatchingSection(.{
3599 break :blk (try self.getOutputSection(.{
39353600 .segname = makeStaticString("__DATA_CONST"),
39363601 .sectname = makeStaticString("__const"),
39373602 .size = code.len,
......@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(
39413606
39423607 switch (zig_ty) {
39433608 .Fn => {
3944 break :blk MatchingSection{
3945 .seg = self.text_segment_cmd_index.?,
3946 .sect = self.text_section_index.?,
3947 };
3609 break :blk self.text_section_index.?;
39483610 },
39493611 .Array => {
39503612 if (val.tag() == .bytes) {
......@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(
39533615 .const_slice_u8_sentinel_0,
39543616 .manyptr_const_u8_sentinel_0,
39553617 => {
3956 break :blk (try self.getMatchingSection(.{
3618 break :blk (try self.getOutputSection(.{
39573619 .segname = makeStaticString("__TEXT"),
39583620 .sectname = makeStaticString("__cstring"),
39593621 .flags = macho.S_CSTRING_LITERALS,
......@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(
39673629 },
39683630 else => {},
39693631 }
3970 break :blk (try self.getMatchingSection(.{
3632 break :blk (try self.getOutputSection(.{
39713633 .segname = makeStaticString("__TEXT"),
39723634 .sectname = makeStaticString("__const"),
39733635 .size = code.len,
39743636 .@"align" = align_log_2,
39753637 })).?;
39763638 };
3977 const sect = self.getSection(match);
3978 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
3639 const header = self.sections.items(.header)[sect_id];
3640 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
39793641 name,
3980 sect.segName(),
3981 sect.sectName(),
3982 match.seg,
3983 match.sect,
3642 header.segName(),
3643 header.sectName(),
3644 sect_id,
39843645 });
3985 return match;
3646 return sect_id;
39863647}
39873648
39883649fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
......@@ -3996,7 +3657,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
39963657
39973658 const decl_ptr = self.decls.getPtr(decl_index).?;
39983659 if (decl_ptr.* == null) {
3999 decl_ptr.* = try self.getMatchingSectionAtom(
3660 decl_ptr.* = try self.getOutputSectionAtom(
40003661 &decl.link.macho,
40013662 sym_name,
40023663 decl.ty,
......@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
40453706 symbol.* = .{
40463707 .n_strx = name_str_index,
40473708 .n_type = macho.N_SECT,
4048 .n_sect = self.getSectionOrdinal(match),
3709 .n_sect = match + 1,
40493710 .n_desc = 0,
40503711 .n_value = addr,
40513712 };
......@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(
41343795 sym.* = .{
41353796 .n_strx = try self.strtab.insert(gpa, exp_name),
41363797 .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 }),
3798 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?
41413799 .n_desc = 0,
41423800 .n_value = decl_sym.n_value,
41433801 };
......@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
42083866fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
42093867 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
42103868 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);
3869 // TODO
3870 // const sect_id = atom.getSymbol(self).n_sect;
3871 const sect_id = self.getSectionByName("__TEXT", "__const").?;
3872 self.freeAtom(atom, sect_id, true);
42153873 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
42163874 self.locals.items[atom.sym_index].n_type = 0;
42173875 _ = self.atom_by_index_table.remove(atom.sym_index);
......@@ -4294,6 +3952,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
42943952}
42953953
42963954fn populateMissingMetadata(self: *MachO) !void {
3955 const gpa = self.base.allocator;
42973956 const cpu_arch = self.base.options.target.cpu.arch;
42983957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
42993958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
......@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {
43053964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
43063965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
43073966 }
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 },
3967 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
3968 try self.segments.append(gpa, .{
3969 .segname = makeStaticString("__PAGEZERO"),
3970 .vmsize = aligned_pagezero_vmsize,
3971 .cmdsize = @sizeOf(macho.segment_command_64),
43173972 });
4318 self.load_commands_dirty = true;
43193973 }
43203974
43213975 if (self.text_segment_cmd_index == null) {
4322 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3976 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);
43233977 const needed_size = if (self.mode == .incremental) blk: {
43243978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
43253979 const program_code_size_hint = self.base.options.program_code_size_hint;
......@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {
43293983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
43303984 break :blk needed_size;
43313985 } 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 },
3986 try self.segments.append(gpa, .{
3987 .segname = makeStaticString("__TEXT"),
3988 .vmaddr = aligned_pagezero_vmsize,
3989 .vmsize = needed_size,
3990 .filesize = needed_size,
3991 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
3992 .initprot = macho.PROT.READ | macho.PROT.EXEC,
3993 .cmdsize = @sizeOf(macho.segment_command_64),
43443994 });
4345 self.load_commands_dirty = true;
43463995 }
43473996
43483997 if (self.text_section_index == null) {
......@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43534002 };
43544003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
43554004 self.text_section_index = try self.initSection(
4356 self.text_segment_cmd_index.?,
4005 "__TEXT",
43574006 "__text",
43584007 needed_size,
43594008 alignment,
......@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43764025 };
43774026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
43784027 self.stubs_section_index = try self.initSection(
4379 self.text_segment_cmd_index.?,
4028 "__TEXT",
43804029 "__stubs",
43814030 needed_size,
43824031 alignment,
......@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44084057 else
44094058 0;
44104059 self.stub_helper_section_index = try self.initSection(
4411 self.text_segment_cmd_index.?,
4060 "__TEXT",
44124061 "__stub_helper",
44134062 needed_size,
44144063 alignment,
......@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44194068 }
44204069
44214070 if (self.data_const_segment_cmd_index == null) {
4422 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4071 self.data_const_segment_cmd_index = @intCast(u8, self.segments.items.len);
44234072 var vmaddr: u64 = 0;
44244073 var fileoff: u64 = 0;
44254074 var needed_size: u64 = 0;
......@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44344083 fileoff + needed_size,
44354084 });
44364085 }
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 },
4086 try self.segments.append(gpa, .{
4087 .segname = makeStaticString("__DATA_CONST"),
4088 .vmaddr = vmaddr,
4089 .vmsize = needed_size,
4090 .fileoff = fileoff,
4091 .filesize = needed_size,
4092 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4093 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4094 .cmdsize = @sizeOf(macho.segment_command_64),
44504095 });
4451 self.load_commands_dirty = true;
44524096 }
44534097
44544098 if (self.got_section_index == null) {
......@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44584102 0;
44594103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
44604104 self.got_section_index = try self.initSection(
4461 self.data_const_segment_cmd_index.?,
4105 "__DATA_CONST",
44624106 "__got",
44634107 needed_size,
44644108 alignment,
......@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44694113 }
44704114
44714115 if (self.data_segment_cmd_index == null) {
4472 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4116 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);
44734117 var vmaddr: u64 = 0;
44744118 var fileoff: u64 = 0;
44754119 var needed_size: u64 = 0;
......@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44844128 fileoff + needed_size,
44854129 });
44864130 }
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 },
4131 try self.segments.append(gpa, .{
4132 .segname = makeStaticString("__DATA"),
4133 .vmaddr = vmaddr,
4134 .vmsize = needed_size,
4135 .fileoff = fileoff,
4136 .filesize = needed_size,
4137 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4138 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4139 .cmdsize = @sizeOf(macho.segment_command_64),
45004140 });
4501 self.load_commands_dirty = true;
45024141 }
45034142
45044143 if (self.la_symbol_ptr_section_index == null) {
......@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45084147 0;
45094148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
45104149 self.la_symbol_ptr_section_index = try self.initSection(
4511 self.data_segment_cmd_index.?,
4150 "__DATA",
45124151 "__la_symbol_ptr",
45134152 needed_size,
45144153 alignment,
......@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45254164 0;
45264165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
45274166 self.data_section_index = try self.initSection(
4528 self.data_segment_cmd_index.?,
4167 "__DATA",
45294168 "__data",
45304169 needed_size,
45314170 alignment,
......@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {
45334172 );
45344173 }
45354174
4536 if (self.tlv_section_index == null) {
4537 const needed_size = if (self.mode == .incremental)
4538 @sizeOf(u64) * self.base.options.symbol_count_hint
4539 else
4540 0;
4541 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4542 self.tlv_section_index = try self.initSection(
4543 self.data_segment_cmd_index.?,
4544 "__thread_vars",
4545 needed_size,
4546 alignment,
4547 .{
4548 .flags = macho.S_THREAD_LOCAL_VARIABLES,
4549 },
4550 );
4551 }
4552
4553 if (self.tlv_data_section_index == null) {
4554 const needed_size = if (self.mode == .incremental)
4555 @sizeOf(u64) * self.base.options.symbol_count_hint
4556 else
4557 0;
4558 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4559 self.tlv_data_section_index = try self.initSection(
4560 self.data_segment_cmd_index.?,
4561 "__thread_data",
4562 needed_size,
4563 alignment,
4564 .{
4565 .flags = macho.S_THREAD_LOCAL_REGULAR,
4566 },
4567 );
4568 }
4569
4570 if (self.tlv_bss_section_index == null) {
4571 const needed_size = if (self.mode == .incremental)
4572 @sizeOf(u64) * self.base.options.symbol_count_hint
4573 else
4574 0;
4575 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4576 self.tlv_bss_section_index = try self.initSection(
4577 self.data_segment_cmd_index.?,
4578 "__thread_bss",
4579 needed_size,
4580 alignment,
4581 .{
4582 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
4583 },
4584 );
4585 }
4586
4587 if (self.bss_section_index == null) {
4588 const needed_size = if (self.mode == .incremental)
4589 @sizeOf(u64) * self.base.options.symbol_count_hint
4590 else
4591 0;
4592 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4593 self.bss_section_index = try self.initSection(
4594 self.data_segment_cmd_index.?,
4595 "__bss",
4596 needed_size,
4597 alignment,
4598 .{
4599 .flags = macho.S_ZEROFILL,
4600 },
4601 );
4602 }
4603
46044175 if (self.linkedit_segment_cmd_index == null) {
4605 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4176 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
46064177 var vmaddr: u64 = 0;
46074178 var fileoff: u64 = 0;
46084179 if (self.mode == .incremental) {
......@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {
46114182 fileoff = base.fileoff;
46124183 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
46134184 }
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 },
4625 });
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 },
4185 try self.segments.append(gpa, .{
4186 .segname = makeStaticString("__LINKEDIT"),
4187 .vmaddr = vmaddr,
4188 .fileoff = fileoff,
4189 .maxprot = macho.PROT.READ,
4190 .initprot = macho.PROT.READ,
4191 .cmdsize = @sizeOf(macho.segment_command_64),
46464192 });
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;
46904193 }
4194}
46914195
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 }
4196inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
4197 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
4198 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
4199}
47224200
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,
4737 );
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;
4201fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4202 const gpa = self.base.allocator;
4203 var sizeofcmds: u64 = 0;
4204 for (self.segments.items) |seg| {
4205 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
4206 }
4207
4208 // LC_DYLD_INFO_ONLY
4209 sizeofcmds += @sizeOf(macho.dyld_info_command);
4210 // LC_FUNCTION_STARTS
4211 if (self.text_section_index != null) {
4212 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4213 }
4214 // LC_DATA_IN_CODE
4215 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4216 // LC_SYMTAB
4217 sizeofcmds += @sizeOf(macho.symtab_command);
4218 // LC_DYSYMTAB
4219 sizeofcmds += @sizeOf(macho.dysymtab_command);
4220 // LC_LOAD_DYLINKER
4221 sizeofcmds += calcInstallNameLen(
4222 @sizeOf(macho.dylinker_command),
4223 mem.sliceTo(default_dyld_path, 0),
4224 false,
4225 );
4226 // LC_MAIN
4227 if (self.base.options.output_mode == .Exe) {
4228 sizeofcmds += @sizeOf(macho.entry_point_command);
4229 }
4230 // LC_ID_DYLIB
4231 if (self.base.options.output_mode == .Lib) {
4232 sizeofcmds += blk: {
4233 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
4234 break :blk calcInstallNameLen(
4235 @sizeOf(macho.dylib_command),
4236 install_name,
4237 assume_max_path_len,
4238 );
4239 };
47414240 }
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;
4241 // LC_RPATH
4242 {
4243 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
4244 defer it.deinit();
4245 while (try it.next()) |rpath| {
4246 sizeofcmds += calcInstallNameLen(
4247 @sizeOf(macho.rpath_command),
4248 rpath,
4249 assume_max_path_len,
4250 );
4251 }
47524252 }
4753
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,
4253 // LC_SOURCE_VERSION
4254 sizeofcmds += @sizeOf(macho.source_version_command);
4255 // LC_BUILD_VERSION
4256 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
4257 // LC_UUID
4258 sizeofcmds += @sizeOf(macho.uuid_command);
4259 // LC_LOAD_DYLIB
4260 for (self.referenced_dylibs.keys()) |id| {
4261 const dylib = self.dylibs.items[id];
4262 const dylib_id = dylib.id orelse unreachable;
4263 sizeofcmds += calcInstallNameLen(
4264 @sizeOf(macho.dylib_command),
4265 dylib_id.name,
4266 assume_max_path_len,
4267 );
4268 }
4269 // LC_CODE_SIGNATURE
4270 {
4271 const target = self.base.options.target;
4272 const requires_codesig = blk: {
4273 if (self.base.options.entitlements) |_| break :blk true;
4274 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
4275 break :blk true;
4276 break :blk false;
48014277 };
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;
4278 if (requires_codesig) {
4279 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4280 }
48184281 }
48194282
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 }
4283 return @intCast(u32, sizeofcmds);
48324284}
48334285
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);
4286fn calcMinHeaderPad(self: *MachO) !u64 {
4287 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
48424288 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
48434289
48444290 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 };
4291 var min_headerpad_size: u32 = try self.calcLCsSize(true);
48574292 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
48584293 min_headerpad_size + @sizeOf(macho.mach_header_64),
48594294 });
......@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {
48684303fn allocateSegments(self: *MachO) !void {
48694304 try self.allocateSegment(self.text_segment_cmd_index, &.{
48704305 self.pagezero_segment_cmd_index,
4871 }, self.calcMinHeaderpad());
4306 }, try self.calcMinHeaderPad());
48724307
48734308 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;
4309 const indexes = self.getSectionIndexes(index);
4310 if (indexes.start == indexes.end) break :blk;
4311 const seg = self.segments.items[index];
48764312
48774313 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
48784314 var min_alignment: u32 = 0;
4879 for (seg.sections.items) |sect| {
4880 const alignment = try math.powi(u32, 2, sect.@"align");
4315 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4316 const alignment = try math.powi(u32, 2, header.@"align");
48814317 min_alignment = math.max(min_alignment, alignment);
48824318 }
48834319
48844320 assert(min_alignment > 0);
4885 const last_sect_idx = seg.sections.items.len - 1;
4886 const last_sect = seg.sections.items[last_sect_idx];
4321 const last_header = self.sections.items(.header)[indexes.end - 1];
48874322 const shift: u32 = shift: {
4888 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
4323 const diff = seg.filesize - last_header.offset - last_header.size;
48894324 const factor = @divTrunc(diff, min_alignment);
48904325 break :shift @intCast(u32, factor * min_alignment);
48914326 };
48924327
48934328 if (shift > 0) {
4894 for (seg.sections.items) |*sect| {
4895 sect.offset += shift;
4896 sect.addr += shift;
4329 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4330 header.offset += shift;
4331 header.addr += shift;
48974332 }
48984333 }
48994334 }
......@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {
49174352 }, 0);
49184353}
49194354
4920fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_size: u64) !void {
4355fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
49214356 const index = maybe_index orelse return;
4922 const seg = &self.load_commands.items[index].segment;
4357 const seg = &self.segments.items[index];
49234358
49244359 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;
4360 seg.vmaddr = base.vmaddr;
4361 seg.fileoff = base.fileoff;
4362 seg.filesize = init_size;
4363 seg.vmsize = init_size;
49294364
49304365 // Allocate the sections according to their alignment at the beginning of the segment.
4366 const indexes = self.getSectionIndexes(index);
49314367 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");
4368 const slice = self.sections.slice();
4369 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4370 const alignment = try math.powi(u32, 2, header.@"align");
49374371 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
49384372
4939 // TODO handle zerofill sections in stage2
4940 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4373 header.offset = if (header.isZerofill())
49414374 0
49424375 else
4943 @intCast(u32, seg.inner.fileoff + start_aligned);
4944 sect.addr = seg.inner.vmaddr + start_aligned;
4376 @intCast(u32, seg.fileoff + start_aligned);
4377 header.addr = seg.vmaddr + start_aligned;
49454378
4946 start = start_aligned + sect.size;
4379 start = start_aligned + header.size;
49474380
4948 if (!(is_zerofill and (use_stage1 or use_llvm))) {
4949 seg.inner.filesize = start;
4381 if (!header.isZerofill()) {
4382 seg.filesize = start;
49504383 }
4951 seg.inner.vmsize = start;
4384 seg.vmsize = start;
49524385 }
49534386
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);
4387 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4388 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
49564389}
49574390
49584391const InitSectionOpts = struct {
......@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {
49634396
49644397fn initSection(
49654398 self: *MachO,
4966 segment_id: u16,
4399 segname: []const u8,
49674400 sectname: []const u8,
49684401 size: u64,
49694402 alignment: u32,
49704403 opts: InitSectionOpts,
4971) !u16 {
4972 const seg = &self.load_commands.items[segment_id].segment;
4973 var sect = macho.section_64{
4404) !u8 {
4405 const segment_id = self.getSegmentByName(segname).?;
4406 const seg = &self.segments.items[segment_id];
4407 const index = try self.insertSection(segment_id, .{
49744408 .sectname = makeStaticString(sectname),
4975 .segname = seg.inner.segname,
4976 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
4977 .@"align" = alignment,
4409 .segname = seg.segname,
49784410 .flags = opts.flags,
49794411 .reserved1 = opts.reserved1,
49804412 .reserved2 = opts.reserved2,
4981 };
4413 });
4414 seg.cmdsize += @sizeOf(macho.section_64);
4415 seg.nsects += 1;
49824416
49834417 if (self.mode == .incremental) {
4418 const header = &self.sections.items(.header)[index];
4419 header.size = size;
4420 header.@"align" = alignment;
4421
4422 const prev_end_off = if (index > 0) blk: {
4423 const prev_section = self.sections.get(index - 1);
4424 if (prev_section.segment_index == segment_id) {
4425 const prev_header = prev_section.header;
4426 break :blk prev_header.offset + padToIdeal(prev_header.size);
4427 } else break :blk seg.fileoff;
4428 } else 0;
49844429 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)
4987 else
4988 null;
4989 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
4990 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4991 sect.segName(),
4992 sect.sectName(),
4993 off,
4994 off + size,
4995 });
4430 // TODO better prealloc for __text section
4431 // const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4432 const padding: u64 = if (index == 0) 0x1000 else 0;
4433 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
49964434
4997 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
4435 if (!header.isZerofill()) {
4436 header.offset = @intCast(u32, off);
4437 }
4438 header.addr = seg.vmaddr + off - seg.fileoff;
49984439
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;
4440 // TODO Will this break if we are inserting section that is not the last section
4441 // in a segment?
4442 const max_size = self.allocatedSize(segment_id, off);
50024443
5003 // TODO handle zerofill in stage2
5004 if (!(is_zerofill and (use_stage1 or use_llvm))) {
5005 sect.offset = @intCast(u32, off);
4444 if (size > max_size) {
4445 try self.growSection(index, @intCast(u32, size));
50064446 }
4447
4448 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
4449
4450 self.updateSectionOrdinals(index + 1);
4451 }
4452
4453 return index;
4454}
4455
4456fn getSectionPrecedence(header: macho.section_64) u4 {
4457 if (header.isCode()) {
4458 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
4459 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
4460 return 0x2;
4461 }
4462 switch (header.@"type"()) {
4463 macho.S_NON_LAZY_SYMBOL_POINTERS,
4464 macho.S_LAZY_SYMBOL_POINTERS,
4465 => return 0x0,
4466 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
4467 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
4468 macho.S_ZEROFILL => return 0xf,
4469 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
4470 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
4471 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
4472 return 0xf
4473 else
4474 return 0x3,
50074475 }
4476}
50084477
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;
4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
4479 const precedence = getSectionPrecedence(header);
4480 const indexes = self.getSectionIndexes(segment_index);
4481 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
4482 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
4483 } else indexes.end;
4484 log.debug("inserting section '{s},{s}' at index {d}", .{
4485 header.segName(),
4486 header.sectName(),
4487 insertion_index,
4488 });
4489 for (&[_]*?u8{
4490 &self.text_section_index,
4491 &self.stubs_section_index,
4492 &self.stub_helper_section_index,
4493 &self.got_section_index,
4494 &self.la_symbol_ptr_section_index,
4495 &self.data_section_index,
4496 }) |maybe_index| {
4497 const index = maybe_index.* orelse continue;
4498 if (insertion_index <= index) maybe_index.* = index + 1;
4499 }
4500 try self.sections.insert(self.base.allocator, insertion_index, .{
4501 .segment_index = segment_index,
4502 .header = header,
4503 });
4504 return insertion_index;
4505}
50134506
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, .{});
4507fn updateSectionOrdinals(self: *MachO, start: u8) void {
4508 const tracy = trace(@src());
4509 defer tracy.end();
50204510
5021 self.load_commands_dirty = true;
5022 self.sections_order_dirty = true;
4511 const slice = self.sections.slice();
4512 for (slice.items(.last_atom)[start..]) |last_atom| {
4513 var atom = last_atom orelse continue;
50234514
5024 return index;
4515 while (true) {
4516 const sym = atom.getSymbolPtr(self);
4517 sym.n_sect = start + 1;
4518
4519 for (atom.contained.items) |sym_at_off| {
4520 const contained_sym = self.getSymbolPtr(.{
4521 .sym_index = sym_at_off.sym_index,
4522 .file = atom.file,
4523 });
4524 contained_sym.n_sect = start + 1;
4525 }
4526
4527 if (atom.prev) |prev| {
4528 atom = prev;
4529 } else break;
4530 }
4531 }
50254532}
50264533
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;
4534fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
4535 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
4536
4537 while (true) {
4538 const atom_sym = atom.getSymbolPtr(self);
4539 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
4540
4541 for (atom.contained.items) |sym_at_off| {
4542 const contained_sym = self.getSymbolPtr(.{
4543 .sym_index = sym_at_off.sym_index,
4544 .file = atom.file,
4545 });
4546 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
4547 }
4548
4549 if (atom.prev) |prev| {
4550 atom = prev;
4551 } else break;
50314552 }
5032 const last_sect = seg.sections.items[seg.sections.items.len - 1];
5033 const final_off = last_sect.offset + padToIdeal(last_sect.size);
5034 return mem.alignForwardGeneric(u64, final_off, alignment);
50354553}
50364554
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;
4555fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
4556 const segment = &self.segments.items[segment_index];
4557 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
4558 assert(new_segment_size > segment.filesize);
4559 const offset_amt = new_segment_size - segment.filesize;
50424560 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
5043 seg.inner.segname,
5044 seg.inner.filesize,
5045 new_seg_size,
4561 segment.segname,
4562 segment.filesize,
4563 new_segment_size,
50464564 });
5047 seg.inner.filesize = new_seg_size;
5048 seg.inner.vmsize = new_seg_size;
4565 segment.filesize = new_segment_size;
4566 segment.vmsize = new_segment_size;
50494567
50504568 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,
4569 segment.fileoff,
4570 segment.fileoff + segment.filesize,
4571 segment.vmaddr,
4572 segment.vmaddr + segment.vmsize,
50554573 });
50564574
5057 var next: usize = seg_id + 1;
4575 var next: u8 = segment_index + 1;
50584576 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
5059 const next_seg = &self.load_commands.items[next].segment;
4577 const next_segment = &self.segments.items[next];
50604578
50614579 try MachO.copyRangeAllOverlappingAlloc(
50624580 self.base.allocator,
50634581 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,
4582 next_segment.fileoff,
4583 next_segment.fileoff + offset_amt,
4584 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
50674585 );
50684586
5069 next_seg.inner.fileoff += offset_amt;
5070 next_seg.inner.vmaddr += offset_amt;
4587 next_segment.fileoff += offset_amt;
4588 next_segment.vmaddr += offset_amt;
50714589
50724590 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,
4591 next_segment.segname,
4592 next_segment.fileoff,
4593 next_segment.fileoff + next_segment.filesize,
4594 next_segment.vmaddr,
4595 next_segment.vmaddr + next_segment.vmsize,
50784596 });
50794597
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;
5083
5084 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,
4598 const indexes = self.getSectionIndexes(next);
4599 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
4600 header.offset += @intCast(u32, offset_amt);
4601 header.addr += offset_amt;
4602
4603 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4604 header.segName(),
4605 header.sectName(),
4606 header.offset,
4607 header.offset + header.size,
4608 header.addr,
4609 header.addr + header.size,
50914610 });
50924611
5093 try self.shiftLocalsByOffset(.{
5094 .seg = @intCast(u16, next),
5095 .sect = @intCast(u16, moved_sect_id),
5096 }, @intCast(i64, offset_amt));
4612 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
50974613 }
50984614 }
50994615}
51004616
5101fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
51024618 const tracy = trace(@src());
51034619 defer tracy.end();
51044620
5105 const seg = &self.load_commands.items[match.seg].segment;
5106 const sect = &seg.sections.items[match.sect];
4621 const section = self.sections.get(sect_id);
4622 const segment_index = section.segment_index;
4623 const header = section.header;
4624 const segment = self.segments.items[segment_index];
51074625
5108 const alignment = try math.powi(u32, 2, sect.@"align");
5109 const max_size = self.allocatedSize(match.seg, sect.offset);
4626 const alignment = try math.powi(u32, 2, header.@"align");
4627 const max_size = self.allocatedSize(segment_index, header.offset);
51104628 const ideal_size = padToIdeal(new_size);
51114629 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
51124630
51134631 if (needed_size > max_size) blk: {
51144632 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });
51154633
5116 if (match.sect == seg.sections.items.len - 1) {
4634 const indexes = self.getSectionIndexes(segment_index);
4635 if (sect_id == indexes.end - 1) {
51174636 // Last section, just grow segments
5118 try self.growSegment(match.seg, seg.inner.filesize + needed_size - max_size);
4637 try self.growSegment(segment_index, segment.filesize + needed_size - max_size);
51194638 break :blk;
51204639 }
51214640
51224641 // Need to move all sections below in file and address spaces.
51234642 const offset_amt = offset: {
5124 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);
4643 const max_alignment = try self.getSectionMaxAlignment(sect_id + 1, indexes.end);
51254644 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
51264645 };
51274646
51284647 // Before we commit to this, check if the segment needs to grow too.
51294648 // We assume that each section header is growing linearly with the increasing
51304649 // 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;
4650 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
4651 const last_sect_off = last_sect_header.offset + last_sect_header.size;
4652 const seg_off = segment.fileoff + segment.filesize;
51344653
51354654 if (last_sect_off + offset_amt > seg_off) {
51364655 // Need to grow segment first.
51374656 const spill_size = (last_sect_off + offset_amt) - seg_off;
5138 try self.growSegment(match.seg, seg.inner.filesize + spill_size);
4657 try self.growSegment(segment_index, segment.filesize + spill_size);
51394658 }
51404659
51414660 // We have enough space to expand within the segment, so move all sections by
51424661 // the required amount and update their header offsets.
5143 const next_sect = seg.sections.items[match.sect + 1];
4662 const next_sect = self.sections.items(.header)[sect_id + 1];
51444663 const total_size = last_sect_off - next_sect.offset;
51454664
51464665 try MachO.copyRangeAllOverlappingAlloc(
......@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51514670 math.cast(usize, total_size) orelse return error.Overflow,
51524671 );
51534672
5154 var next = match.sect + 1;
5155 while (next < seg.sections.items.len) : (next += 1) {
5156 const moved_sect = &seg.sections.items[next];
4673 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
51574674 moved_sect.offset += @intCast(u32, offset_amt);
51584675 moved_sect.addr += offset_amt;
51594676
......@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51664683 moved_sect.addr + moved_sect.size,
51674684 });
51684685
5169 try self.shiftLocalsByOffset(.{
5170 .seg = match.seg,
5171 .sect = next,
5172 }, @intCast(i64, offset_amt));
4686 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
51734687 }
51744688 }
51754689}
51764690
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;
4691fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
4692 const segment = self.segments.items[segment_id];
4693 const indexes = self.getSectionIndexes(segment_id);
4694 assert(start >= segment.fileoff);
4695 var min_pos: u64 = segment.fileoff + segment.filesize;
51814696 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;
4697 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4698 if (header.offset <= start) continue;
4699 if (header.offset < min_pos) min_pos = header.offset;
51854700 }
51864701 return min_pos - start;
51874702}
51884703
5189fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
5190 const seg = self.load_commands.items[segment_id].segment;
4704fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
51914705 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");
4706 const slice = self.sections.slice();
4707 for (slice.items(.header)[start..end]) |header| {
4708 const alignment = try math.powi(u32, 2, header.@"align");
51964709 max_alignment = math.max(max_alignment, alignment);
51974710 }
51984711 return max_alignment;
51994712}
52004713
5201fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
4714fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
52024715 const sym = atom.getSymbolPtr(self);
52034716 if (self.mode == .incremental) {
52044717 const size = atom.size;
52054718 const alignment = try math.powi(u32, 2, atom.alignment);
5206 const vaddr = try self.allocateAtom(atom, size, alignment, match);
4719 const vaddr = try self.allocateAtom(atom, size, alignment, sect_id);
52074720 const sym_name = atom.getName(self);
52084721 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
52094722 sym.n_value = vaddr;
5210 } else try self.addAtomToSection(atom, match);
5211 sym.n_sect = self.getSectionOrdinal(match);
4723 } else try self.addAtomToSection(atom, sect_id);
4724 sym.n_sect = sect_id + 1;
52124725}
52134726
52144727fn allocateAtom(
......@@ -5216,15 +4729,15 @@ fn allocateAtom(
52164729 atom: *Atom,
52174730 new_atom_size: u64,
52184731 alignment: u64,
5219 match: MatchingSection,
4732 sect_id: u8,
52204733) !u64 {
52214734 const tracy = trace(@src());
52224735 defer tracy.end();
52234736
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;
4737 const header = &self.sections.items(.header)[sect_id];
4738 const free_list = &self.sections.items(.free_list)[sect_id];
4739 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
4740 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
52284741
52294742 // We use these to indicate our intention to update metadata, placing the new atom,
52304743 // and possibly removing a free list node.
......@@ -5244,7 +4757,7 @@ fn allocateAtom(
52444757 // Is it enough that we could fit this new atom?
52454758 const sym = big_atom.getSymbol(self);
52464759 const capacity = big_atom.capacity(self);
5247 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
4760 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
52484761 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
52494762 const capacity_end_vaddr = sym.n_value + capacity;
52504763 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
......@@ -5272,30 +4785,28 @@ fn allocateAtom(
52724785 free_list_removal = i;
52734786 }
52744787 break :blk new_start_vaddr;
5275 } else if (self.atoms.get(match)) |last| {
4788 } else if (maybe_last_atom.*) |last| {
52764789 const last_symbol = last.getSymbol(self);
5277 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
4790 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
52784791 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
52794792 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
52804793 atom_placement = last;
52814794 break :blk new_start_vaddr;
52824795 } else {
5283 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);
4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
52844797 }
52854798 };
52864799
52874800 const expand_section = atom_placement == null or atom_placement.?.next == null;
52884801 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;
4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
4803 try self.growSection(sect_id, needed_size);
4804 maybe_last_atom.* = atom;
4805 header.size = needed_size;
52944806 }
52954807 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;
4808 if (header.@"align" < align_pow) {
4809 header.@"align" = align_pow;
52994810 }
53004811 atom.size = new_atom_size;
53014812 atom.alignment = align_pow;
......@@ -5322,20 +4833,19 @@ fn allocateAtom(
53224833 return vaddr;
53234834}
53244835
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);
4836pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
4837 var section = self.sections.get(sect_id);
4838 if (section.header.size > 0) {
4839 section.last_atom.?.next = atom;
4840 atom.prev = section.last_atom.?;
53324841 }
5333 const sect = self.getSectionPtr(match);
4842 section.last_atom = atom;
53344843 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);
4844 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
4845 const padding = aligned_end_addr - section.header.size;
4846 section.header.size += padding + atom.size;
4847 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
4848 self.sections.set(sect_id, section);
53394849}
53404850
53414851pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
......@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
53684878 return sym_index;
53694879}
53704880
5371fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
4881fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
53724882 for (indices) |maybe_prev_id| {
53734883 const prev_id = maybe_prev_id orelse continue;
5374 const prev = self.load_commands.items[prev_id].segment;
4884 const prev = self.segments.items[prev_id];
53754885 return .{
5376 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,
5377 .fileoff = prev.inner.fileoff + prev.inner.filesize,
4886 .vmaddr = prev.vmaddr + prev.vmsize,
4887 .fileoff = prev.fileoff + prev.filesize,
53784888 };
53794889 }
53804890 return .{ .vmaddr = 0, .fileoff = 0 };
53814891}
53824892
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.*);
4893fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4894 for (self.segments.items) |seg, i| {
4895 const indexes = self.getSectionIndexes(@intCast(u8, i));
4896 var out_seg = seg;
4897 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
4898 out_seg.nsects = 0;
4899
4900 // Update section headers count; any section with size of 0 is excluded
4901 // since it doesn't have any data in the final binary file.
4902 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4903 if (header.size == 0) continue;
4904 out_seg.cmdsize += @sizeOf(macho.section_64);
4905 out_seg.nsects += 1;
54054906 }
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 }
5424
5425 while (atoms.popOrNull()) |next| {
5426 try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom);
5427 }
5428
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}
54364907
5437fn pruneAndSortSections(self: *MachO) !void {
5438 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{
5439 &self.text_section_index,
5440 &self.stubs_section_index,
5441 &self.stub_helper_section_index,
5442 &self.gcc_except_tab_section_index,
5443 &self.cstring_section_index,
5444 &self.ustring_section_index,
5445 &self.text_const_section_index,
5446 &self.objc_methlist_section_index,
5447 &self.objc_methname_section_index,
5448 &self.objc_methtype_section_index,
5449 &self.objc_classname_section_index,
5450 &self.eh_frame_section_index,
5451 });
5452
5453 try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{
5454 &self.got_section_index,
5455 &self.mod_init_func_section_index,
5456 &self.mod_term_func_section_index,
5457 &self.data_const_section_index,
5458 &self.objc_cfstring_section_index,
5459 &self.objc_classlist_section_index,
5460 &self.objc_imageinfo_section_index,
5461 });
5462
5463 try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{
5464 &self.rustc_section_index,
5465 &self.la_symbol_ptr_section_index,
5466 &self.objc_const_section_index,
5467 &self.objc_selrefs_section_index,
5468 &self.objc_classrefs_section_index,
5469 &self.objc_data_section_index,
5470 &self.data_section_index,
5471 &self.tlv_section_index,
5472 &self.tlv_ptrs_section_index,
5473 &self.tlv_data_section_index,
5474 &self.tlv_bss_section_index,
5475 &self.bss_section_index,
5476 });
4908 if (out_seg.nsects == 0 and
4909 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
4910 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
54774911
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),
5496 });
5497 assert(!res.found_existing);
5498 }
5499 }
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);
4912 try writer.writeStruct(out_seg);
4913 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4914 if (header.size == 0) continue;
4915 try writer.writeStruct(header);
55084916 }
4917
4918 ncmds.* += 1;
55094919 }
5510 self.sections_order_dirty = false;
55114920}
55124921
5513fn updateSectionOrdinals(self: *MachO) !void {
5514 if (!self.sections_order_dirty) return;
5515
5516 const tracy = trace(@src());
5517 defer tracy.end();
5518
5519 log.debug("updating section ordinals", .{});
5520
5521 const gpa = self.base.allocator;
5522
5523 var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5524 defer ordinal_remap.deinit();
5525 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
5526
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, {});
5550 }
5551 }
4922fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4923 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4924 seg.filesize = 0;
4925 seg.vmsize = 0;
55524926
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 }
5566 }
4927 try self.writeDyldInfoData(ncmds, lc_writer);
4928 try self.writeFunctionStarts(ncmds, lc_writer);
4929 try self.writeDataInCode(ncmds, lc_writer);
4930 try self.writeSymtabs(ncmds, lc_writer);
55674931
5568 self.section_ordinals.deinit(gpa);
5569 self.section_ordinals = ordinals;
4932 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
55704933}
55714934
5572fn writeDyldInfoData(self: *MachO) !void {
4935fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
55734936 const tracy = trace(@src());
55744937 defer tracy.end();
55754938
......@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {
55824945 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
55834946 defer lazy_bind_pointers.deinit();
55844947
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.*;
4948 const slice = self.sections.slice();
4949 for (slice.items(.last_atom)) |last_atom, sect_id| {
4950 var atom = last_atom orelse continue;
4951 const segment_index = slice.items(.segment_index)[sect_id];
4952 const header = slice.items(.header)[sect_id];
55904953
5591 if (self.text_segment_cmd_index) |seg| {
5592 if (match.seg == seg) continue; // __TEXT is non-writable
5593 }
4954 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
55944955
5595 const seg = self.getSegment(match);
5596 const sect = self.getSection(match);
5597 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
5598
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;
5603
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 }
4956 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
56114957
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 }
4958 const seg = self.segments.items[segment_index];
56374959
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 });
4960 while (true) {
4961 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
4962 const sym = atom.getSymbol(self);
4963 const base_offset = sym.n_value - seg.vmaddr;
4964
4965 for (atom.rebases.items) |offset| {
4966 log.debug(" | rebase at {x}", .{base_offset + offset});
4967 try rebase_pointers.append(.{
4968 .offset = base_offset + offset,
4969 .segment_id = segment_index,
4970 });
4971 }
4972
4973 for (atom.bindings.items) |binding| {
4974 const bind_sym = self.getSymbol(binding.target);
4975 const bind_sym_name = self.getSymbolName(binding.target);
4976 const dylib_ordinal = @divTrunc(
4977 @bitCast(i16, bind_sym.n_desc),
4978 macho.N_SYMBOL_RESOLVER,
4979 );
4980 var flags: u4 = 0;
4981 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4982 binding.offset + base_offset,
4983 bind_sym_name,
4984 dylib_ordinal,
4985 });
4986 if (bind_sym.weakRef()) {
4987 log.debug(" | marking as weak ref ", .{});
4988 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
56624989 }
4990 try bind_pointers.append(.{
4991 .offset = binding.offset + base_offset,
4992 .segment_id = segment_index,
4993 .dylib_ordinal = dylib_ordinal,
4994 .name = bind_sym_name,
4995 .bind_flags = flags,
4996 });
4997 }
56634998
5664 if (atom.prev) |prev| {
5665 atom = prev;
5666 } else break;
4999 for (atom.lazy_bindings.items) |binding| {
5000 const bind_sym = self.getSymbol(binding.target);
5001 const bind_sym_name = self.getSymbolName(binding.target);
5002 const dylib_ordinal = @divTrunc(
5003 @bitCast(i16, bind_sym.n_desc),
5004 macho.N_SYMBOL_RESOLVER,
5005 );
5006 var flags: u4 = 0;
5007 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5008 binding.offset + base_offset,
5009 bind_sym_name,
5010 dylib_ordinal,
5011 });
5012 if (bind_sym.weakRef()) {
5013 log.debug(" | marking as weak ref ", .{});
5014 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5015 }
5016 try lazy_bind_pointers.append(.{
5017 .offset = binding.offset + base_offset,
5018 .segment_id = segment_index,
5019 .dylib_ordinal = dylib_ordinal,
5020 .name = bind_sym_name,
5021 .bind_flags = flags,
5022 });
56675023 }
5024
5025 if (atom.prev) |prev| {
5026 atom = prev;
5027 } else break;
56685028 }
56695029 }
56705030
......@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {
56755035 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
56765036 log.debug("generating export trie", .{});
56775037
5678 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5679 const base_address = text_segment.inner.vmaddr;
5038 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5039 const base_address = text_segment.vmaddr;
56805040
56815041 if (self.base.options.output_mode == .Exe) {
56825042 for (&[_]SymbolWithLoc{
......@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {
57145074 try trie.finalize(gpa);
57155075 }
57165076
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));
5077 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5078 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
5079 assert(rebase_off == link_seg.fileoff);
57215080 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 });
5081 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
57285082
5729 const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64));
5083 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
57305084 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 });
5085 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
57375086
5738 const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64));
5087 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
57395088 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 });
5089 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
57465090
5747 const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64));
5091 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
57485092 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 });
5093 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
57555094
5756 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
5095 const needed_size = export_off + export_size - rebase_off;
5096 link_seg.filesize = needed_size;
57575097
5758 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;
5759 var buffer = try gpa.alloc(u8, needed_size);
5098 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
57605099 defer gpa.free(buffer);
57615100 mem.set(u8, buffer, 0);
57625101
57635102 var stream = std.io.fixedBufferStream(buffer);
57645103 const writer = stream.writer();
57655104
5766 const base_off = dyld_info.rebase_off;
57675105 try bind.writeRebaseInfo(rebase_pointers.items, writer);
5768 try stream.seekTo(dyld_info.bind_off - base_off);
5106 try stream.seekTo(bind_off - rebase_off);
57695107
57705108 try bind.writeBindInfo(bind_pointers.items, writer);
5771 try stream.seekTo(dyld_info.lazy_bind_off - base_off);
5109 try stream.seekTo(lazy_bind_off - rebase_off);
57725110
57735111 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
5774 try stream.seekTo(dyld_info.export_off - base_off);
5112 try stream.seekTo(export_off - rebase_off);
57755113
57765114 _ = try trie.write(writer);
57775115
57785116 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
5779 dyld_info.rebase_off,
5780 dyld_info.rebase_off + needed_size,
5117 rebase_off,
5118 rebase_off + needed_size,
57815119 });
57825120
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;
5121 try self.base.file.?.pwriteAll(buffer, rebase_off);
5122 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
5123 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
5124 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
5125
5126 try lc_writer.writeStruct(macho.dyld_info_command{
5127 .cmd = .DYLD_INFO_ONLY,
5128 .cmdsize = @sizeOf(macho.dyld_info_command),
5129 .rebase_off = @intCast(u32, rebase_off),
5130 .rebase_size = @intCast(u32, rebase_size),
5131 .bind_off = @intCast(u32, bind_off),
5132 .bind_size = @intCast(u32, bind_size),
5133 .weak_bind_off = 0,
5134 .weak_bind_size = 0,
5135 .lazy_bind_off = @intCast(u32, lazy_bind_off),
5136 .lazy_bind_size = @intCast(u32, lazy_bind_size),
5137 .export_off = @intCast(u32, export_off),
5138 .export_size = @intCast(u32, export_size),
5139 });
5140 ncmds.* += 1;
57895141}
57905142
57915143fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
57925144 const gpa = self.base.allocator;
5793 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
5145
57945146 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;
57995147 if (self.stub_helper_preamble_atom == null) return;
5800 if (last_atom == self.stub_helper_preamble_atom.?) return;
5148
5149 const section = self.sections.get(stub_helper_section_index);
5150 const last_atom = section.last_atom orelse return;
5151 if (last_atom == self.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
58015152
58025153 var table = std.AutoHashMap(i64, *Atom).init(gpa);
58035154 defer table.deinit();
58045155
58055156 {
58065157 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 }).?;
5158 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
58115159 const base_addr = blk: {
5812 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
5813 break :blk seg.inner.vmaddr;
5160 const seg = self.segments.items[self.data_segment_cmd_index.?];
5161 break :blk seg.vmaddr;
58145162 };
58155163
58165164 while (true) {
......@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58715219 }
58725220 }
58735221
5874 const sect = self.getSection(.{
5875 .seg = text_segment_cmd_index,
5876 .sect = stub_helper_section_index,
5877 });
5222 const header = self.sections.items(.header)[stub_helper_section_index];
58785223 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
58795224 .x86_64 => 1,
58805225 .aarch64 => 2 * @sizeOf(u32),
......@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58865231 while (offsets.popOrNull()) |bind_offset| {
58875232 const atom = table.get(bind_offset.sym_offset).?;
58885233 const sym = atom.getSymbol(self);
5889 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
5234 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
58905235 mem.writeIntLittle(u32, &buf, bind_offset.offset);
58915236 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
58925237 bind_offset.offset,
......@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58995244
59005245const asc_u64 = std.sort.asc(u64);
59015246
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
5247fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59075248 const tracy = trace(@src());
59085249 defer tracy.end();
59095250
5251 const text_seg_index = self.text_segment_cmd_index orelse return;
5252 const text_sect_index = self.text_section_index orelse return;
5253 const text_seg = self.segments.items[text_seg_index];
5254
59105255 const gpa = self.base.allocator;
59115256
59125257 // We need to sort by address first
......@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {
59185263 const sym = self.getSymbol(global);
59195264 if (sym.undf()) continue;
59205265 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;
5266 const sect_id = sym.n_sect - 1;
5267 if (sect_id != text_sect_index) continue;
59235268
59245269 addresses.appendAssumeCapacity(sym.n_value);
59255270 }
......@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {
59325277
59335278 var last_off: u32 = 0;
59345279 for (addresses.items) |addr| {
5935 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);
5280 const offset = @intCast(u32, addr - text_seg.vmaddr);
59365281 const diff = offset - last_off;
59375282
59385283 if (diff == 0) continue;
......@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {
59515296 try std.leb.writeULEB128(buffer.writer(), offset);
59525297 }
59535298
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;
5299 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5300 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
5301 const needed_size = buffer.items.len;
5302 link_seg.filesize = offset + needed_size - link_seg.fileoff;
59565303
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;
5304 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
59625305
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 });
5306 try self.base.file.?.pwriteAll(buffer.items, offset);
59675307
5968 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);
5969 self.load_commands_dirty = true;
5308 try lc_writer.writeStruct(macho.linkedit_data_command{
5309 .cmd = .FUNCTION_STARTS,
5310 .cmdsize = @sizeOf(macho.linkedit_data_command),
5311 .dataoff = @intCast(u32, offset),
5312 .datasize = @intCast(u32, needed_size),
5313 });
5314 ncmds.* += 1;
59705315}
59715316
59725317fn filterDataInCode(
......@@ -5988,17 +5333,15 @@ fn filterDataInCode(
59885333 return dices[start..end];
59895334}
59905335
5991fn writeDataInCode(self: *MachO) !void {
5336fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59925337 const tracy = trace(@src());
59935338 defer tracy.end();
59945339
59955340 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
59965341 defer out_dice.deinit();
59975342
5998 const text_sect = self.getSection(.{
5999 .seg = self.text_segment_cmd_index orelse return,
6000 .sect = self.text_section_index orelse return,
6001 });
5343 const text_sect_id = self.text_section_index orelse return;
5344 const text_sect_header = self.sections.items(.header)[text_sect_id];
60025345
60035346 for (self.objects.items) |object| {
60045347 const dice = object.parseDataInCode() orelse continue;
......@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {
60085351 const sym = atom.getSymbol(self);
60095352 if (sym.n_desc == N_DESC_GCED) continue;
60105353
6011 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
6012 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {
5354 const sect_id = sym.n_sect - 1;
5355 if (sect_id != self.text_section_index.?) {
60135356 continue;
60145357 }
60155358
60165359 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
60175360 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
60185361 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
5362 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
60205363 return error.Overflow;
60215364
60225365 for (filtered_dice) |single| {
......@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {
60305373 }
60315374 }
60325375
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;
5376 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5377 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5378 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
5379 seg.filesize = offset + needed_size - seg.fileoff;
60355380
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;
5381 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
60415382
6042 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
6043 dice_cmd.dataoff,
6044 dice_cmd.dataoff + dice_cmd.datasize,
5383 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
5384 try lc_writer.writeStruct(macho.linkedit_data_command{
5385 .cmd = .DATA_IN_CODE,
5386 .cmdsize = @sizeOf(macho.linkedit_data_command),
5387 .dataoff = @intCast(u32, offset),
5388 .datasize = @intCast(u32, needed_size),
60455389 });
6046
6047 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6048 self.load_commands_dirty = true;
5390 ncmds.* += 1;
60495391}
60505392
6051fn writeSymtab(self: *MachO) !void {
6052 const tracy = trace(@src());
6053 defer tracy.end();
5393fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5394 var symtab_cmd = macho.symtab_command{
5395 .cmdsize = @sizeOf(macho.symtab_command),
5396 .symoff = 0,
5397 .nsyms = 0,
5398 .stroff = 0,
5399 .strsize = 0,
5400 };
5401 var dysymtab_cmd = macho.dysymtab_command{
5402 .cmdsize = @sizeOf(macho.dysymtab_command),
5403 .ilocalsym = 0,
5404 .nlocalsym = 0,
5405 .iextdefsym = 0,
5406 .nextdefsym = 0,
5407 .iundefsym = 0,
5408 .nundefsym = 0,
5409 .tocoff = 0,
5410 .ntoc = 0,
5411 .modtaboff = 0,
5412 .nmodtab = 0,
5413 .extrefsymoff = 0,
5414 .nextrefsyms = 0,
5415 .indirectsymoff = 0,
5416 .nindirectsyms = 0,
5417 .extreloff = 0,
5418 .nextrel = 0,
5419 .locreloff = 0,
5420 .nlocrel = 0,
5421 };
5422 var ctx = try self.writeSymtab(&symtab_cmd);
5423 defer ctx.imports_table.deinit();
5424 try self.writeDysymtab(ctx, &dysymtab_cmd);
5425 try self.writeStrtab(&symtab_cmd);
5426 try lc_writer.writeStruct(symtab_cmd);
5427 try lc_writer.writeStruct(dysymtab_cmd);
5428 ncmds.* += 2;
5429}
60545430
5431fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
60555432 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);
60605433
60615434 var locals = std.ArrayList(macho.nlist_64).init(gpa);
60625435 defer locals.deinit();
......@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {
61015474
61025475 var imports = std.ArrayList(macho.nlist_64).init(gpa);
61035476 defer imports.deinit();
5477
61045478 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6105 defer imports_table.deinit();
61065479
61075480 for (self.globals.values()) |global| {
61085481 const sym = self.getSymbol(global);
......@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {
61155488 try imports_table.putNoClobber(global, new_index);
61165489 }
61175490
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);
5491 const nlocals = @intCast(u32, locals.items.len);
5492 const nexports = @intCast(u32, exports.items.len);
5493 const nimports = @intCast(u32, imports.items.len);
5494 const nsyms = nlocals + nexports + nimports;
5495
5496 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5497 const offset = mem.alignForwardGeneric(
5498 u64,
5499 seg.fileoff + seg.filesize,
5500 @alignOf(macho.nlist_64),
5501 );
5502 const needed_size = nsyms * @sizeOf(macho.nlist_64);
5503 seg.filesize = offset + needed_size - seg.fileoff;
61225504
61235505 var buffer = std.ArrayList(u8).init(gpa);
61245506 defer buffer.deinit();
6125 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
5507 try buffer.ensureTotalCapacityPrecise(needed_size);
61265508 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
61275509 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
61285510 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
61295511
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);
5512 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5513 try self.base.file.?.pwriteAll(buffer.items, offset);
5514
5515 lc.symoff = @intCast(u32, offset);
5516 lc.nsyms = nsyms;
61325517
6133 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
5518 return SymtabCtx{
5519 .nlocalsym = nlocals,
5520 .nextdefsym = nexports,
5521 .nundefsym = nimports,
5522 .imports_table = imports_table,
5523 };
5524}
5525
5526fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
5527 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5528 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5529 const needed_size = self.strtab.buffer.items.len;
5530 seg.filesize = offset + needed_size - seg.fileoff;
5531
5532 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
61345533
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);
5534 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
5535
5536 lc.stroff = @intCast(u32, offset);
5537 lc.strsize = @intCast(u32, needed_size);
5538}
5539
5540const SymtabCtx = struct {
5541 nlocalsym: u32,
5542 nextdefsym: u32,
5543 nundefsym: u32,
5544 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
5545};
61425546
5547fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
5548 const gpa = self.base.allocator;
61435549 const nstubs = @intCast(u32, self.stubs_table.count());
61445550 const ngot_entries = @intCast(u32, self.got_entries_table.count());
5551 const nindirectsyms = nstubs * 2 + ngot_entries;
5552 const iextdefsym = ctx.nlocalsym;
5553 const iundefsym = iextdefsym + ctx.nextdefsym;
61455554
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;
6149
6150 seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff;
5555 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5556 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5557 const needed_size = nindirectsyms * @sizeOf(u32);
5558 seg.filesize = offset + needed_size - seg.fileoff;
61515559
6152 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
6153 dysymtab.indirectsymoff,
6154 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6155 });
5560 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
61565561
61575562 var buf = std.ArrayList(u8).init(gpa);
61585563 defer buf.deinit();
6159 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
5564 try buf.ensureTotalCapacity(needed_size);
61605565 const writer = buf.writer();
61615566
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 });
5567 if (self.stubs_section_index) |sect_id| {
5568 const stubs = &self.sections.items(.header)[sect_id];
61685569 stubs.reserved1 = 0;
61695570 for (self.stubs.items) |entry| {
61705571 if (entry.sym_index == 0) continue;
......@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {
61725573 if (atom_sym.n_desc == N_DESC_GCED) continue;
61735574 const target_sym = self.getSymbol(entry.target);
61745575 assert(target_sym.undf());
6175 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5576 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61765577 }
61775578 }
61785579
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 });
5580 if (self.got_section_index) |sect_id| {
5581 const got = &self.sections.items(.header)[sect_id];
61855582 got.reserved1 = nstubs;
61865583 for (self.got_entries.items) |entry| {
61875584 if (entry.sym_index == 0) continue;
......@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {
61895586 if (atom_sym.n_desc == N_DESC_GCED) continue;
61905587 const target_sym = self.getSymbol(entry.target);
61915588 if (target_sym.undf()) {
6192 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5589 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61935590 } else {
61945591 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
61955592 }
61965593 }
61975594 }
61985595
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 });
5596 if (self.la_symbol_ptr_section_index) |sect_id| {
5597 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
62055598 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
62065599 for (self.stubs.items) |entry| {
62075600 if (entry.sym_index == 0) continue;
......@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {
62095602 if (atom_sym.n_desc == N_DESC_GCED) continue;
62105603 const target_sym = self.getSymbol(entry.target);
62115604 assert(target_sym.undf());
6212 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5605 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
62135606 }
62145607 }
62155608
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;
6234
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();
5609 assert(buf.items.len == needed_size);
5610 try self.base.file.?.pwriteAll(buf.items, offset);
62455611
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);
5612 lc.nlocalsym = ctx.nlocalsym;
5613 lc.iextdefsym = iextdefsym;
5614 lc.nextdefsym = ctx.nextdefsym;
5615 lc.iundefsym = iundefsym;
5616 lc.nundefsym = ctx.nundefsym;
5617 lc.indirectsymoff = @intCast(u32, offset);
5618 lc.nindirectsyms = nindirectsyms;
62565619}
62575620
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;
5621fn writeCodeSignaturePadding(
5622 self: *MachO,
5623 code_sig: *CodeSignature,
5624 ncmds: *u32,
5625 lc_writer: anytype,
5626) !u32 {
5627 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
62645628 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
62655629 // 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 });
5630 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
5631 const needed_size = code_sig.estimateSize(offset);
5632 seg.filesize = offset + needed_size - seg.fileoff;
5633 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
5634 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
62755635 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
62765636 // except for code signature data.
6277 try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1);
6278 self.load_commands_dirty = true;
6279}
5637 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
62805638
6281fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
6282 const tracy = trace(@src());
6283 defer tracy.end();
5639 try lc_writer.writeStruct(macho.linkedit_data_command{
5640 .cmd = .CODE_SIGNATURE,
5641 .cmdsize = @sizeOf(macho.linkedit_data_command),
5642 .dataoff = @intCast(u32, offset),
5643 .datasize = @intCast(u32, needed_size),
5644 });
5645 ncmds.* += 1;
5646
5647 return @intCast(u32, offset);
5648}
62845649
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;
5650fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
5651 const seg = self.segments.items[self.text_segment_cmd_index.?];
62875652
62885653 var buffer = std.ArrayList(u8).init(self.base.allocator);
62895654 defer buffer.deinit();
62905655 try buffer.ensureTotalCapacityPrecise(code_sig.size());
62915656 try code_sig.writeAdhocSignature(self.base.allocator, .{
62925657 .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,
5658 .exec_seg_base = seg.fileoff,
5659 .exec_seg_limit = seg.filesize,
5660 .file_size = offset,
62965661 .output_mode = self.base.options.output_mode,
62975662 }, buffer.writer());
62985663 assert(buffer.items.len == code_sig.size());
62995664
63005665 log.debug("writing code signature from 0x{x} to 0x{x}", .{
6301 code_sig_cmd.dataoff,
6302 code_sig_cmd.dataoff + buffer.items.len,
5666 offset,
5667 offset + buffer.items.len,
63035668 });
63045669
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;
5670 try self.base.file.?.pwriteAll(buffer.items, offset);
63335671}
63345672
63355673/// Writes Mach-O file header.
6336fn writeHeader(self: *MachO) !void {
5674fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
63375675 var header: macho.mach_header_64 = .{};
63385676 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
63395677
......@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {
63615699 else => unreachable,
63625700 }
63635701
6364 if (self.tlv_section_index) |_| {
5702 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {
63655703 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
63665704 }
63675705
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 }
5706 header.ncmds = ncmds;
5707 header.sizeofcmds = sizeofcmds;
63765708
63775709 log.debug("writing Mach-O header {}", .{header});
63785710
......@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
63925724 return buf;
63935725}
63945726
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).*;
5727fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
5728 for (self.segments.items) |seg, i| {
5729 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
5730 } else return null;
64125731}
64135732
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];
5733pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
5734 // TODO investigate caching with a hashmap
5735 for (self.sections.items(.header)) |header, i| {
5736 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
5737 return @intCast(u8, i);
5738 } else return null;
64185739}
64195740
6420pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6421 return self.getSectionPtr(match).*;
5741pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
5742 var start: u8 = 0;
5743 const nsects = for (self.segments.items) |seg, i| {
5744 if (i == segment_index) break @intCast(u8, seg.nsects);
5745 start += @intCast(u8, seg.nsects);
5746 } else 0;
5747 return .{ .start = start, .end = start + nsects };
64225748}
64235749
64245750pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
......@@ -6512,72 +5838,6 @@ pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate:
65125838 return i;
65135839}
65145840
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
65815841pub fn generateSymbolStabs(
65825842 self: *MachO,
65835843 object: Object,
......@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(
65855845) !void {
65865846 assert(!self.base.options.strip);
65875847
6588 const gpa = self.base.allocator;
6589
65905848 log.debug("parsing debug info in '{s}'", .{object.name});
65915849
6592 var debug_info = (try DebugInfo.parse(gpa, object)) orelse return;
5850 const gpa = self.base.allocator;
5851 var debug_info = try object.parseDwarfInfo();
5852 defer debug_info.deinit(gpa);
5853 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
65935854
65945855 // We assume there is only one CU.
6595 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
5856 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
65965857 error.MissingDebugInfo => {
65975858 // TODO audit cases with missing debug info and audit our dwarf.zig module.
65985859 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
......@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(
66005861 },
66015862 else => |e| return e,
66025863 };
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);
5864 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name);
5865 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir);
66055866
66065867 // Open scope
66075868 try locals.ensureUnusedCapacity(3);
......@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(
66645925fn generateSymbolStabsForSymbol(
66655926 self: *MachO,
66665927 sym_loc: SymbolWithLoc,
6667 debug_info: DebugInfo,
5928 debug_info: dwarf.DwarfInfo,
66685929 buf: *[4]macho.nlist_64,
66695930) ![]const macho.nlist_64 {
66705931 const gpa = self.base.allocator;
......@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(
66795940 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
66805941 const size: ?u64 = size: {
66815942 if (source_sym.tentative()) break :size null;
6682 for (debug_info.inner.func_list.items) |func| {
5943 for (debug_info.func_list.items) |func| {
66835944 if (func.pc_range) |range| {
66845945 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
66855946 break :size range.end - range.start;
......@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(
67315992 }
67325993}
67335994
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 = .{},
5995// fn snapshotState(self: *MachO) !void {
5996// const emit = self.base.options.emit orelse {
5997// log.debug("no emit directory found; skipping snapshot...", .{});
5998// return;
5999// };
6000
6001// const Snapshot = struct {
6002// const Node = struct {
6003// const Tag = enum {
6004// section_start,
6005// section_end,
6006// atom_start,
6007// atom_end,
6008// relocation,
6009
6010// pub fn jsonStringify(
6011// tag: Tag,
6012// options: std.json.StringifyOptions,
6013// out_stream: anytype,
6014// ) !void {
6015// _ = options;
6016// switch (tag) {
6017// .section_start => try out_stream.writeAll("\"section_start\""),
6018// .section_end => try out_stream.writeAll("\"section_end\""),
6019// .atom_start => try out_stream.writeAll("\"atom_start\""),
6020// .atom_end => try out_stream.writeAll("\"atom_end\""),
6021// .relocation => try out_stream.writeAll("\"relocation\""),
6022// }
6023// }
6024// };
6025// const Payload = struct {
6026// name: []const u8 = "",
6027// aliases: [][]const u8 = &[0][]const u8{},
6028// is_global: bool = false,
6029// target: u64 = 0,
6030// };
6031// address: u64,
6032// tag: Tag,
6033// payload: Payload,
6034// };
6035// timestamp: i128,
6036// nodes: []Node,
6037// };
6038
6039// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6040// defer arena_allocator.deinit();
6041// const arena = arena_allocator.allocator();
6042
6043// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6044// .truncate = false,
6045// .read = true,
6046// });
6047// defer out_file.close();
6048
6049// if (out_file.seekFromEnd(-1)) {
6050// try out_file.writer().writeByte(',');
6051// } else |err| switch (err) {
6052// error.Unseekable => try out_file.writer().writeByte('['),
6053// else => |e| return e,
6054// }
6055// const writer = out_file.writer();
6056
6057// var snapshot = Snapshot{
6058// .timestamp = std.time.nanoTimestamp(),
6059// .nodes = undefined,
6060// };
6061// var nodes = std.ArrayList(Snapshot.Node).init(arena);
6062
6063// for (self.section_ordinals.keys()) |key| {
6064// const sect = self.getSection(key);
6065// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6066// try nodes.append(.{
6067// .address = sect.addr,
6068// .tag = .section_start,
6069// .payload = .{ .name = sect_name },
6070// });
6071
6072// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6073
6074// var atom: *Atom = self.atoms.get(key) orelse {
6075// try nodes.append(.{
6076// .address = sect.addr + sect.size,
6077// .tag = .section_end,
6078// .payload = .{},
6079// });
6080// continue;
6081// };
6082
6083// while (atom.prev) |prev| {
6084// atom = prev;
6085// }
6086
6087// while (true) {
6088// const atom_sym = atom.getSymbol(self);
6089// var node = Snapshot.Node{
6090// .address = atom_sym.n_value,
6091// .tag = .atom_start,
6092// .payload = .{
6093// .name = atom.getName(self),
6094// .is_global = self.globals.contains(atom.getName(self)),
6095// },
6096// };
6097
6098// var aliases = std.ArrayList([]const u8).init(arena);
6099// for (atom.contained.items) |sym_off| {
6100// if (sym_off.offset == 0) {
6101// try aliases.append(self.getSymbolName(.{
6102// .sym_index = sym_off.sym_index,
6103// .file = atom.file,
6104// }));
6105// }
6106// }
6107// node.payload.aliases = aliases.toOwnedSlice();
6108// try nodes.append(node);
6109
6110// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6111// for (atom.relocs.items) |rel| {
6112// const source_addr = blk: {
6113// const source_sym = atom.getSymbol(self);
6114// break :blk source_sym.n_value + rel.offset;
6115// };
6116// const target_addr = blk: {
6117// const target_atom = rel.getTargetAtom(self) orelse {
6118// // If there is no atom for target, we still need to check for special, atom-less
6119// // symbols such as `___dso_handle`.
6120// const target_name = self.getSymbolName(rel.target);
6121// if (self.globals.contains(target_name)) {
6122// const atomless_sym = self.getSymbol(rel.target);
6123// break :blk atomless_sym.n_value;
6124// }
6125// break :blk 0;
6126// };
6127// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6128// self.getSymbol(rel.target)
6129// else
6130// target_atom.getSymbol(self);
6131// const base_address: u64 = if (is_tlv) base_address: {
6132// const sect_id: u16 = sect_id: {
6133// if (self.tlv_data_section_index) |i| {
6134// break :sect_id i;
6135// } else if (self.tlv_bss_section_index) |i| {
6136// break :sect_id i;
6137// } else unreachable;
6138// };
6139// break :base_address self.getSection(.{
6140// .seg = self.data_segment_cmd_index.?,
6141// .sect = sect_id,
6142// }).addr;
6143// } else 0;
6144// break :blk target_sym.n_value - base_address;
6145// };
6146
6147// relocs.appendAssumeCapacity(.{
6148// .address = source_addr,
6149// .tag = .relocation,
6150// .payload = .{ .target = target_addr },
6151// });
6152// }
6153
6154// if (atom.contained.items.len == 0) {
6155// try nodes.appendSlice(relocs.items);
6156// } else {
6157// // Need to reverse iteration order of relocs since by default for relocatable sources
6158// // they come in reverse. For linking, this doesn't matter in any way, however, for
6159// // arranging the memoryline for displaying it does.
6160// std.mem.reverse(Snapshot.Node, relocs.items);
6161
6162// var next_i: usize = 0;
6163// var last_rel: usize = 0;
6164// while (next_i < atom.contained.items.len) : (next_i += 1) {
6165// const loc = SymbolWithLoc{
6166// .sym_index = atom.contained.items[next_i].sym_index,
6167// .file = atom.file,
6168// };
6169// const cont_sym = self.getSymbol(loc);
6170// const cont_sym_name = self.getSymbolName(loc);
6171// var contained_node = Snapshot.Node{
6172// .address = cont_sym.n_value,
6173// .tag = .atom_start,
6174// .payload = .{
6175// .name = cont_sym_name,
6176// .is_global = self.globals.contains(cont_sym_name),
6177// },
6178// };
6179
6180// // Accumulate aliases
6181// var inner_aliases = std.ArrayList([]const u8).init(arena);
6182// while (true) {
6183// if (next_i + 1 >= atom.contained.items.len) break;
6184// const next_sym_loc = SymbolWithLoc{
6185// .sym_index = atom.contained.items[next_i + 1].sym_index,
6186// .file = atom.file,
6187// };
6188// const next_sym = self.getSymbol(next_sym_loc);
6189// if (next_sym.n_value != cont_sym.n_value) break;
6190// const next_sym_name = self.getSymbolName(next_sym_loc);
6191// if (self.globals.contains(next_sym_name)) {
6192// try inner_aliases.append(contained_node.payload.name);
6193// contained_node.payload.name = next_sym_name;
6194// contained_node.payload.is_global = true;
6195// } else try inner_aliases.append(next_sym_name);
6196// next_i += 1;
6197// }
6198
6199// const cont_size = if (next_i + 1 < atom.contained.items.len)
6200// self.getSymbol(.{
6201// .sym_index = atom.contained.items[next_i + 1].sym_index,
6202// .file = atom.file,
6203// }).n_value - cont_sym.n_value
6204// else
6205// atom_sym.n_value + atom.size - cont_sym.n_value;
6206
6207// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6208// try nodes.append(contained_node);
6209
6210// for (relocs.items[last_rel..]) |rel| {
6211// if (rel.address >= cont_sym.n_value + cont_size) {
6212// break;
6213// }
6214// try nodes.append(rel);
6215// last_rel += 1;
6216// }
6217
6218// try nodes.append(.{
6219// .address = cont_sym.n_value + cont_size,
6220// .tag = .atom_end,
6221// .payload = .{},
6222// });
6223// }
6224// }
6225
6226// try nodes.append(.{
6227// .address = atom_sym.n_value + atom.size,
6228// .tag = .atom_end,
6229// .payload = .{},
6230// });
6231
6232// if (atom.next) |next| {
6233// atom = next;
6234// } else break;
6235// }
6236
6237// try nodes.append(.{
6238// .address = sect.addr + sect.size,
6239// .tag = .section_end,
6240// .payload = .{},
6241// });
6242// }
6243
6244// snapshot.nodes = nodes.toOwnedSlice();
6245
6246// try std.json.stringify(snapshot, .{}, writer);
6247// try writer.writeByte(']');
6248// }
6249
6250fn logSections(self: *MachO) void {
6251 log.debug("sections:", .{});
6252 for (self.sections.items(.header)) |header, i| {
6253 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
6254 i + 1,
6255 header.segName(),
6256 header.sectName(),
6257 header.offset,
6258 header.size,
69806259 });
69816260 }
6982
6983 snapshot.nodes = nodes.toOwnedSlice();
6984
6985 try std.json.stringify(snapshot, .{}, writer);
6986 try writer.writeByte(']');
69876261}
69886262
69896263fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
......@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {
71046378 }
71056379}
71066380
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
71146381fn logAtoms(self: *MachO) void {
71156382 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.*;
6383
6384 const slice = self.sections.slice();
6385 for (slice.items(.last_atom)) |last, i| {
6386 var atom = last orelse continue;
6387 const header = slice.items(.header)[i];
71206388
71216389 while (atom.prev) |prev| {
71226390 atom = prev;
71236391 }
71246392
7125 const sect = self.getSection(match);
7126 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
6393 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
71276394
71286395 while (true) {
71296396 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+21-19
......@@ -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 }
......@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
524529 if (context.macho_file.stubs_table.contains(target)) return;
525530
526531 const stub_index = try context.macho_file.allocateStubEntry(target);
532
527533 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
528534 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
529535 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
......@@ -565,9 +571,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
565571 };
566572 const is_tlv = is_tlv: {
567573 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;
574 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
575 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
571576 };
572577 const target_addr = blk: {
573578 const target_atom = rel.getTargetAtom(macho_file) orelse {
......@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
597602 // * wrt to __thread_data if defined, then
598603 // * wrt to __thread_bss
599604 const sect_id: u16 = sect_id: {
600 if (macho_file.tlv_data_section_index) |i| {
605 if (macho_file.getSectionByName("__DATA", "__thread_data")) |i| {
601606 break :sect_id i;
602 } else if (macho_file.tlv_bss_section_index) |i| {
607 } else if (macho_file.getSectionByName("__DATA", "__thread_bss")) |i| {
603608 break :sect_id i;
604609 } else {
605610 log.err("threadlocal variables present but no initializer sections found", .{});
......@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
608613 return error.FailedToResolveRelocationTarget;
609614 }
610615 };
611 break :base_address macho_file.getSection(.{
612 .seg = macho_file.data_segment_cmd_index.?,
613 .sect = sect_id,
614 }).addr;
616 break :base_address macho_file.sections.items(.header)[sect_id].addr;
615617 } else 0;
616618 break :blk target_sym.n_value - base_address;
617619 };
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+199-331
......@@ -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,44 @@ 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);
66 log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{
67 self.base.page_size,
68 self.base.page_size * 2,
69 });
14170 // 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;
71 try self.segments.append(allocator, .{
72 .segname = makeStaticString("__LINKEDIT"),
73 .vmaddr = self.base.page_size,
74 .vmsize = self.base.page_size,
75 .fileoff = self.base.page_size,
76 .filesize = self.base.page_size,
77 .maxprot = macho.PROT.READ,
78 .initprot = macho.PROT.READ,
79 .cmdsize = @sizeOf(macho.segment_command_64),
80 });
14781 }
14882
14983 if (self.dwarf_segment_cmd_index == null) {
150 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
84 self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
15185
152 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
86 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
15387 const ideal_size: u16 = 200 + 128 + 160 + 250;
15488 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;
89 const fileoff = linkedit.fileoff + linkedit.filesize;
90 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
15791
15892 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
15993
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 },
94 try self.segments.append(allocator, .{
95 .segname = makeStaticString("__DWARF"),
96 .vmaddr = vmaddr,
97 .vmsize = needed_size,
98 .fileoff = fileoff,
99 .filesize = needed_size,
100 .cmdsize = @sizeOf(macho.segment_command_64),
171101 });
172 self.load_commands_dirty = true;
173102 }
174103
175104 if (self.debug_str_section_index == null) {
......@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
203132 }
204133}
205134
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;
135fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
136 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
208137 var sect = macho.section_64{
209138 .sectname = makeStaticString(sectname),
210 .segname = seg.inner.segname,
139 .segname = segment.segname,
211140 .size = @intCast(u32, size),
212141 .@"align" = alignment,
213142 };
214143 const alignment_pow_2 = try math.powi(u32, 2, alignment);
215144 const off = self.findFreeSpace(size, alignment_pow_2);
216145
217 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
146 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
218147
219148 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
220149 sect.segName(),
......@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
223152 off + size,
224153 });
225154
226 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
155 sect.addr = segment.vmaddr + off - segment.fileoff;
227156 sect.offset = @intCast(u32, off);
228157
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;
158 const index = @intCast(u8, self.sections.items.len);
159 try self.sections.append(self.base.base.allocator, sect);
160 segment.cmdsize += @sizeOf(macho.section_64);
161 segment.nsects += 1;
243162
244163 return index;
245164}
246165
247166fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
248 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
249167 const end = start + padToIdeal(size);
250 for (seg.sections.items) |section| {
168 for (self.sections.items) |section| {
251169 const increased_size = padToIdeal(section.size);
252170 const test_end = section.offset + increased_size;
253171 if (end > section.offset and start < test_end) {
......@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
258176}
259177
260178pub 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;
179 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
180 var offset: u64 = segment.fileoff;
263181 while (self.detectAllocCollision(offset, object_size)) |item_end| {
264182 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
265183 }
......@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
296214 break :blk got_entry.getName(self.base);
297215 },
298216 };
299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
300 const sect = &seg.sections.items[self.debug_info_section_index.?];
217 const sect = &self.sections.items[self.debug_info_section_index.?];
301218 const file_offset = sect.offset + reloc.offset;
302219 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
303220 reloc.target,
......@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
311228
312229 if (self.debug_abbrev_section_dirty) {
313230 try self.dwarf.writeDbgAbbrev(&self.base.base);
314 self.load_commands_dirty = true;
315231 self.debug_abbrev_section_dirty = false;
316232 }
317233
318234 if (self.debug_info_header_dirty) {
319235 // Currently only one compilation unit is supported, so the address range is simply
320236 // 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.?];
237 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
323238 const low_pc = text_section.addr;
324239 const high_pc = text_section.addr + text_section.size;
325240 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
......@@ -329,10 +244,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
329244 if (self.debug_aranges_section_dirty) {
330245 // Currently only one compilation unit is supported, so the address range is simply
331246 // 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.?];
247 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
334248 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
335 self.load_commands_dirty = true;
336249 self.debug_aranges_section_dirty = false;
337250 }
338251
......@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
342255 }
343256
344257 {
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.?];
258 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
259 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
347260 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
348261 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
349262 const needed_size = self.dwarf.strtab.items.len;
......@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
351264 if (needed_size > allocated_size) {
352265 debug_strtab_sect.size = 0; // free the space
353266 const new_offset = self.findFreeSpace(needed_size, 1);
354 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
267 debug_strtab_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
355268 debug_strtab_sect.offset = @intCast(u32, new_offset);
356269 }
357270 debug_strtab_sect.size = @intCast(u32, needed_size);
......@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
362275 });
363276
364277 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
365 self.load_commands_dirty = true;
366278 self.debug_string_table_dirty = false;
367279 }
368280 }
369281
282 var lc_buffer = std.ArrayList(u8).init(allocator);
283 defer lc_buffer.deinit();
284 const lc_writer = lc_buffer.writer();
285 var ncmds: u32 = 0;
286
287 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
370288 self.updateDwarfSegment();
371 try self.writeLinkeditSegment();
372 try self.updateVirtualMemoryMapping();
373 try self.writeLoadCommands(allocator);
374 try self.writeHeader();
375289
376 assert(!self.load_commands_dirty);
290 {
291 try lc_writer.writeStruct(self.base.uuid);
292 ncmds += 1;
293 }
294
295 var headers_buf = std.ArrayList(u8).init(allocator);
296 defer headers_buf.deinit();
297 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
298
299 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
300 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
301
302 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
303
377304 assert(!self.debug_abbrev_section_dirty);
378305 assert(!self.debug_aranges_section_dirty);
379306 assert(!self.debug_string_table_dirty);
380307}
381308
382309pub 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);
310 self.segments.deinit(allocator);
311 self.sections.deinit(allocator);
387312 self.dwarf.deinit();
388313 self.strtab.deinit(allocator);
389314 self.relocs.deinit(allocator);
......@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
402327 }
403328}
404329
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 }
330fn updateDwarfSegment(self: *DebugSymbols) void {
331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
446333
447 cmd.sections.appendAssumeCapacity(sect);
334 const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
335 const old_start_aligned = dwarf_segment.vmaddr;
336 const diff = new_start_aligned - old_start_aligned;
337 if (diff > 0) {
338 dwarf_segment.vmaddr = new_start_aligned;
448339 }
449340
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
456341 var max_offset: u64 = 0;
457 for (dwarf_segment.sections.items) |sect| {
342 for (self.sections.items) |*sect| {
343 sect.addr += diff;
458344 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
459345 sect.segName(),
460346 sect.sectName(),
......@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
468354 }
469355 }
470356
471 const file_size = max_offset - dwarf_segment.inner.fileoff;
357 const file_size = max_offset - dwarf_segment.fileoff;
472358 log.debug("__DWARF size 0x{x}", .{file_size});
473359
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;
360 if (file_size != dwarf_segment.filesize) {
361 dwarf_segment.filesize = file_size;
362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
480363 }
481364}
482365
483/// Writes all load commands and section headers.
484fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
485 if (!self.load_commands_dirty) return;
366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {
367 // Write segment/section headers from the binary file first.
368 const end = self.base.linkedit_segment_cmd_index.?;
369 for (self.base.segments.items[0..end]) |seg, i| {
370 const indexes = self.base.getSectionIndexes(@intCast(u8, i));
371 var out_seg = seg;
372 out_seg.fileoff = 0;
373 out_seg.filesize = 0;
374 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
375 out_seg.nsects = 0;
376
377 // Update section headers count; any section with size of 0 is excluded
378 // since it doesn't have any data in the final binary file.
379 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
380 if (header.size == 0) continue;
381 out_seg.cmdsize += @sizeOf(macho.section_64);
382 out_seg.nsects += 1;
383 }
486384
487 var sizeofcmds: u32 = 0;
488 for (self.load_commands.items) |lc| {
489 sizeofcmds += lc.cmdsize();
490 }
385 if (out_seg.nsects == 0 and
386 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
387 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
491388
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 }
389 try writer.writeStruct(out_seg);
390 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
391 if (header.size == 0) continue;
392 var out_header = header;
393 out_header.offset = 0;
394 try writer.writeStruct(out_header);
395 }
499396
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;
397 ncmds.* += 1;
398 }
399 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
400 for (self.segments.items) |seg| {
401 try writer.writeStruct(seg);
402 ncmds.* += 1;
403 }
404 for (self.sections.items) |header| {
405 try writer.writeStruct(header);
406 }
504407}
505408
506fn writeHeader(self: *DebugSymbols) !void {
409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
507410 var header: macho.mach_header_64 = .{};
508411 header.filetype = macho.MH_DSYM;
509412
......@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {
519422 else => return error.UnsupportedCpuArchitecture,
520423 }
521424
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 }
425 header.ncmds = ncmds;
426 header.sizeofcmds = sizeofcmds;
528427
529428 log.debug("writing Mach-O header {}", .{header});
530429
......@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {
532431}
533432
534433pub 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);
434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
435 assert(start >= seg.fileoff);
537436 var min_pos: u64 = std.math.maxInt(u64);
538 for (seg.sections.items) |section| {
437 for (self.sections.items) |section| {
539438 if (section.offset <= start) continue;
540439 if (section.offset < min_pos) min_pos = section.offset;
541440 }
542441 return min_pos - start;
543442}
544443
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 {
444fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
588445 const tracy = trace(@src());
589446 defer tracy.end();
590447
591 try self.writeSymbolTable();
592 try self.writeStringTable();
448 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
449 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
450 seg.vmaddr = source_vmaddr;
593451
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;
452 var symtab_cmd = macho.symtab_command{
453 .cmdsize = @sizeOf(macho.symtab_command),
454 .symoff = 0,
455 .nsyms = 0,
456 .stroff = 0,
457 .strsize = 0,
458 };
459 try self.writeSymtab(&symtab_cmd);
460 try self.writeStrtab(&symtab_cmd);
461 try lc_writer.writeStruct(symtab_cmd);
462 ncmds.* += 1;
463
464 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
465 seg.filesize = aligned_size;
466 seg.vmsize = aligned_size;
598467}
599468
600fn writeSymbolTable(self: *DebugSymbols) !void {
469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601470 const tracy = trace(@src());
602471 defer tracy.end();
603472
604473 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);
608474
609475 var locals = std.ArrayList(macho.nlist_64).init(gpa);
610476 defer locals.deinit();
......@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
634500
635501 const nlocals = locals.items.len;
636502 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);
503 const nsyms = nlocals + nexports;
641504
642 symtab.nsyms = @intCast(u32, nlocals + nexports);
643 const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);
505 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
506 const offset = mem.alignForwardGeneric(u64, seg.fileoff, @alignOf(macho.nlist_64));
507 const needed_size = nsyms * @sizeOf(macho.nlist_64);
644508
645 if (needed_size > seg.inner.filesize) {
509 if (needed_size > seg.filesize) {
646510 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;
511 const diff = @intCast(u32, aligned_size - seg.filesize);
512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513 seg.filesize = aligned_size;
650514
651515 try MachO.copyRangeAllOverlappingAlloc(
652516 self.base.base.allocator,
653517 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,
518 dwarf_seg.fileoff,
519 dwarf_seg.fileoff + diff,
520 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
657521 );
658522
659 const old_seg_fileoff = dwarf_seg.inner.fileoff;
660 dwarf_seg.inner.fileoff += diff;
523 const old_seg_fileoff = dwarf_seg.fileoff;
524 dwarf_seg.fileoff += diff;
661525
662 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
526 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
663527
664 for (dwarf_seg.sections.items) |*sect| {
528 for (self.sections.items) |*sect| {
665529 const old_offset = sect.offset;
666530 sect.offset += diff;
667531
......@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
674538 }
675539 }
676540
541 lc.symoff = @intCast(u32, offset);
542 lc.nsyms = @intCast(u32, nsyms);
543
544 const locals_off = lc.symoff;
545 const locals_size = nlocals * @sizeOf(macho.nlist_64);
546 const exports_off = locals_off + locals_size;
547 const exports_size = nexports * @sizeOf(macho.nlist_64);
548
677549 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
678550 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
679551
680552 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
681553 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
682
683 self.load_commands_dirty = true;
684554}
685555
686fn writeStringTable(self: *DebugSymbols) !void {
556fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
687557 const tracy = trace(@src());
688558 defer tracy.end();
689559
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;
560 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
561 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
562 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
563 lc.stroff = @intCast(u32, offset);
694564
695565 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
696 symtab.strsize = @intCast(u32, needed_size);
566 lc.strsize = @intCast(u32, needed_size);
697567
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;
568 if (symtab_size + needed_size > seg.filesize) {
569 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
570 const diff = @intCast(u32, aligned_size - seg.filesize);
571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572 seg.filesize = aligned_size;
703573
704574 try MachO.copyRangeAllOverlappingAlloc(
705575 self.base.base.allocator,
706576 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,
577 dwarf_seg.fileoff,
578 dwarf_seg.fileoff + diff,
579 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
710580 );
711581
712 const old_seg_fileoff = dwarf_seg.inner.fileoff;
713 dwarf_seg.inner.fileoff += diff;
582 const old_seg_fileoff = dwarf_seg.fileoff;
583 dwarf_seg.fileoff += diff;
714584
715 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
585 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
716586
717 for (dwarf_seg.sections.items) |*sect| {
587 for (self.sections.items) |*sect| {
718588 const old_offset = sect.offset;
719589 sect.offset += diff;
720590
......@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
727597 }
728598 }
729599
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);
600 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
733601
734 self.load_commands_dirty = true;
602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
735603}
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+151-176
......@@ -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.
......@@ -285,6 +214,23 @@ fn filterRelocs(
285214 return relocs[start..end];
286215}
287216
217pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
218 for (self.sections.items) |sect| {
219 const match = (try macho_file.getOutputSection(sect)) orelse {
220 log.debug(" unhandled section", .{});
221 continue;
222 };
223 const output = macho_file.sections.items(.header)[match];
224 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
225 sect.segName(),
226 sect.sectName(),
227 match + 1,
228 output.segName(),
229 output.sectName(),
230 });
231 }
232}
233
288234/// Splits object into atoms assuming one-shot linking mode.
289235pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
290236 assert(macho_file.mode == .one_shot);
......@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
293239 defer tracy.end();
294240
295241 const gpa = macho_file.base.allocator;
296 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
297242
298243 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
299244
......@@ -302,13 +247,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
302247 // the GO compiler does not necessarily respect that therefore we sort immediately by type
303248 // and address within.
304249 const context = Context{
305 .symtab = self.getSourceSymtab(),
306 .strtab = self.strtab,
250 .object = self,
307251 };
308 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
252 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, self.in_symtab.len);
309253 defer sorted_all_syms.deinit();
310254
311 for (context.symtab) |_, index| {
255 for (self.in_symtab) |_, index| {
312256 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
313257 }
314258
......@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
320264
321265 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
322266 // 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;
267 const iundefsym = blk: {
268 const dysymtab = self.parseDysymtab() orelse {
269 var iundefsym: usize = sorted_all_syms.items.len;
270 while (iundefsym > 0) : (iundefsym -= 1) {
271 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
272 if (sym.sect()) break;
273 }
274 break :blk iundefsym;
275 };
325276 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;
333277 };
334278
335279 // We only care about defined symbols, so filter every other out.
336280 const sorted_syms = sorted_all_syms.items[0..iundefsym];
337281 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
338282
339 for (seg.sections.items) |sect, id| {
283 for (self.sections.items) |sect, id| {
340284 const sect_id = @intCast(u8, id);
341285 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
342286
343287 // Get matching segment/section in the final artifact.
344 const match = (try macho_file.getMatchingSection(sect)) orelse {
288 const match = (try macho_file.getOutputSection(sect)) orelse {
345289 log.debug(" unhandled section", .{});
346290 continue;
347291 };
348292
349293 log.debug(" output sect({d}, '{s},{s}')", .{
350 macho_file.getSectionOrdinal(match),
351 macho_file.getSection(match).segName(),
352 macho_file.getSection(match).sectName(),
294 match + 1,
295 macho_file.sections.items(.header)[match].segName(),
296 macho_file.sections.items(.header)[match].sectName(),
353297 });
354298
355299 const cpu_arch = macho_file.base.options.target.cpu.arch;
356 const is_zerofill = blk: {
357 const section_type = sect.type_();
358 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
359 };
360300
361301 // Read section's code
362 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
302 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
363303
364304 // 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 );
305 const relocs = @ptrCast(
306 [*]const macho.relocation_info,
307 @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
308 )[0..sect.nreloc];
370309
371310 // Symbols within this section only.
372311 const filtered_syms = filterSymbolsByAddress(
......@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
387326 try self.symtab.append(gpa, .{
388327 .n_strx = 0,
389328 .n_type = macho.N_SECT,
390 .n_sect = macho_file.getSectionOrdinal(match),
329 .n_sect = match + 1,
391330 .n_desc = 0,
392331 .n_value = sect.addr,
393332 });
......@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
476415 try self.symtab.append(gpa, .{
477416 .n_strx = 0,
478417 .n_type = macho.N_SECT,
479 .n_sect = macho_file.getSectionOrdinal(match),
418 .n_sect = match + 1,
480419 .n_desc = 0,
481420 .n_value = addr,
482421 });
......@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
501440 try self.symtab.append(gpa, .{
502441 .n_strx = 0,
503442 .n_type = macho.N_SECT,
504 .n_sect = macho_file.getSectionOrdinal(match),
443 .n_sect = match + 1,
505444 .n_desc = 0,
506445 .n_value = sect.addr,
507446 });
......@@ -535,21 +474,21 @@ fn createAtomFromSubsection(
535474 code: ?[]const u8,
536475 relocs: []const macho.relocation_info,
537476 indexes: []const SymbolAtIndex,
538 match: MatchingSection,
477 match: u8,
539478 sect: macho.section_64,
540479) !*Atom {
541480 const gpa = macho_file.base.allocator;
542481 const sym = self.symtab.items[sym_index];
543482 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
544483 atom.file = object_id;
545 self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
484 self.symtab.items[sym_index].n_sect = match + 1;
546485
547486 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
548487 sym_index,
549488 self.getString(sym.n_strx),
550 macho_file.getSectionOrdinal(match),
551 macho_file.getSection(match).segName(),
552 macho_file.getSection(match).sectName(),
489 match + 1,
490 macho_file.sections.items(.header)[match].segName(),
491 macho_file.sections.items(.header)[match].sectName(),
553492 object_id,
554493 });
555494
......@@ -577,7 +516,7 @@ fn createAtomFromSubsection(
577516 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
578517 for (indexes) |inner_sym_index| {
579518 const inner_sym = &self.symtab.items[inner_sym_index.index];
580 inner_sym.n_sect = macho_file.getSectionOrdinal(match);
519 inner_sym.n_sect = match + 1;
581520 atom.contained.appendAssumeCapacity(.{
582521 .sym_index = inner_sym_index.index,
583522 .offset = inner_sym.n_value - sym.n_value,
......@@ -589,48 +528,84 @@ fn createAtomFromSubsection(
589528 return atom;
590529}
591530
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];
531pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
532 if (index >= self.in_symtab.len) return null;
533 return self.in_symtab[index];
597534}
598535
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 );
536pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
537 assert(index < self.sections.items.len);
538 return self.sections.items[index];
608539}
609540
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];
541pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
542 var it = LoadCommandIterator{
543 .ncmds = self.header.ncmds,
544 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
545 };
546 while (it.next()) |cmd| {
547 switch (cmd.cmd()) {
548 .DATA_IN_CODE => {
549 const dice = cmd.cast(macho.linkedit_data_command).?;
550 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
551 return @ptrCast(
552 [*]const macho.data_in_code_entry,
553 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
554 )[0..ndice];
555 },
556 else => {},
557 }
558 } else return null;
614559}
615560
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];
561fn parseDysymtab(self: Object) ?macho.dysymtab_command {
562 var it = LoadCommandIterator{
563 .ncmds = self.header.ncmds,
564 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
565 };
566 while (it.next()) |cmd| {
567 switch (cmd.cmd()) {
568 .DYSYMTAB => {
569 return cmd.cast(macho.dysymtab_command).?;
570 },
571 else => {},
572 }
573 } else return null;
620574}
621575
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 );
576pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
577 var di = dwarf.DwarfInfo{
578 .endian = .Little,
579 .debug_info = &[0]u8{},
580 .debug_abbrev = &[0]u8{},
581 .debug_str = &[0]u8{},
582 .debug_line = &[0]u8{},
583 .debug_line_str = &[0]u8{},
584 .debug_ranges = &[0]u8{},
585 };
586 for (self.sections.items) |sect| {
587 const segname = sect.segName();
588 const sectname = sect.sectName();
589 if (mem.eql(u8, segname, "__DWARF")) {
590 if (mem.eql(u8, sectname, "__debug_info")) {
591 di.debug_info = try self.getSectionContents(sect);
592 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
593 di.debug_abbrev = try self.getSectionContents(sect);
594 } else if (mem.eql(u8, sectname, "__debug_str")) {
595 di.debug_str = try self.getSectionContents(sect);
596 } else if (mem.eql(u8, sectname, "__debug_line")) {
597 di.debug_line = try self.getSectionContents(sect);
598 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
599 di.debug_line_str = try self.getSectionContents(sect);
600 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
601 di.debug_ranges = try self.getSectionContents(sect);
602 }
603 }
604 }
605 return di;
630606}
631607
632pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
633 const sect = self.getSourceSection(index);
608pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
634609 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635610 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636611 sect.segName(),
......@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
642617}
643618
644619pub 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);
620 assert(off < self.in_strtab.len);
621 return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
647622}
648623
649624pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
src/link/MachO/dead_strip.zig+26-24
......@@ -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 {
......@@ -93,7 +93,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
9393 const is_gc_root = blk: {
9494 if (source_sect.isDontDeadStrip()) break :blk true;
9595 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {
96 switch (source_sect.@"type"()) {
9797 macho.S_MOD_INIT_FUNC_POINTERS,
9898 macho.S_MOD_TERM_FUNC_POINTERS,
9999 => break :blk true,
......@@ -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}