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 {...@@ -283,7 +283,14 @@ fn make(step: *Step) !void {
283283
284 const gpa = self.builder.allocator;284 const gpa = self.builder.allocator;
285 const src_path = self.source.getPath(self.builder);285 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
288 const output = switch (self.obj_format) {295 const output = switch (self.obj_format) {
289 .macho => try MachODumper.parseAndDump(contents, .{296 .macho => try MachODumper.parseAndDump(contents, .{
...@@ -370,9 +377,10 @@ const Opts = struct {...@@ -370,9 +377,10 @@ const Opts = struct {
370};377};
371378
372const MachODumper = struct {379const MachODumper = struct {
380 const LoadCommandIterator = macho.LoadCommandIterator;
373 const symtab_label = "symtab";381 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 {
376 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator384 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
377 var stream = std.io.fixedBufferStream(bytes);385 var stream = std.io.fixedBufferStream(bytes);
378 const reader = stream.reader();386 const reader = stream.reader();
...@@ -385,55 +393,54 @@ const MachODumper = struct {...@@ -385,55 +393,54 @@ const MachODumper = struct {
385 var output = std.ArrayList(u8).init(gpa);393 var output = std.ArrayList(u8).init(gpa);
386 const writer = output.writer();394 const writer = output.writer();
387395
388 var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);396 var symtab: []const macho.nlist_64 = undefined;
389 try load_commands.ensureTotalCapacity(hdr.ncmds);397 var strtab: []const u8 = undefined;
390398 var sections = std.ArrayList(macho.section_64).init(gpa);
391 var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);399 var imports = std.ArrayList([]const u8).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);
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| {
400 switch (cmd.cmd()) {407 switch (cmd.cmd()) {
401 .SEGMENT_64 => {408 .SEGMENT_64 => {
402 const seg = cmd.segment;409 const seg = cmd.cast(macho.segment_command_64).?;
403 for (seg.sections.items) |_, j| {410 try sections.ensureUnusedCapacity(seg.nsects);
404 try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });411 for (cmd.getSections()) |sect| {
412 sections.appendAssumeCapacity(sect);
405 }413 }
406 },414 },
407 .SYMTAB => {415 .SYMTAB => if (opts.dump_symtab) {
408 symtab_cmd = i;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];
409 },422 },
410 .LOAD_DYLIB,423 .LOAD_DYLIB,
411 .LOAD_WEAK_DYLIB,424 .LOAD_WEAK_DYLIB,
412 .REEXPORT_DYLIB,425 .REEXPORT_DYLIB,
413 => {426 => {
414 try imports.append(i);427 try imports.append(cmd.getDylibPathName());
415 },428 },
416 else => {},429 else => {},
417 }430 }
418431
419 try dumpLoadCommand(cmd, i, writer);432 try dumpLoadCommand(cmd, i, writer);
420 try writer.writeByte('\n');433 try writer.writeByte('\n');
434
435 i += 1;
421 }436 }
422437
423 if (opts.dump_symtab) {438 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
430 for (symtab) |sym| {439 for (symtab) |sym| {
431 if (sym.stab()) continue;440 if (sym.stab()) continue;
432 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);441 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
433 if (sym.sect()) {442 if (sym.sect()) {
434 const map = sections.items[sym.n_sect - 1];443 const sect = sections.items[sym.n_sect - 1];
435 const seg = load_commands.items[map.seg].segment;
436 const sect = seg.sections.items[map.sect];
437 try writer.print("{x} ({s},{s})", .{444 try writer.print("{x} ({s},{s})", .{
438 sym.n_value,445 sym.n_value,
439 sect.segName(),446 sect.segName(),
...@@ -455,9 +462,7 @@ const MachODumper = struct {...@@ -455,9 +462,7 @@ const MachODumper = struct {
455 break :blk "flat lookup";462 break :blk "flat lookup";
456 unreachable;463 unreachable;
457 }464 }
458 const import_id = imports.items[@bitCast(u16, ordinal) - 1];465 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
459 const import = load_commands.items[import_id].dylib;
460 const full_path = mem.sliceTo(import.data, 0);
461 const basename = fs.path.basename(full_path);466 const basename = fs.path.basename(full_path);
462 assert(basename.len > 0);467 assert(basename.len > 0);
463 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;468 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
...@@ -481,7 +486,7 @@ const MachODumper = struct {...@@ -481,7 +486,7 @@ const MachODumper = struct {
481 return output.toOwnedSlice();486 return output.toOwnedSlice();
482 }487 }
483488
484 fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {489 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
485 // print header first490 // print header first
486 try writer.print(491 try writer.print(
487 \\LC {d}492 \\LC {d}
...@@ -491,8 +496,7 @@ const MachODumper = struct {...@@ -491,8 +496,7 @@ const MachODumper = struct {
491496
492 switch (lc.cmd()) {497 switch (lc.cmd()) {
493 .SEGMENT_64 => {498 .SEGMENT_64 => {
494 // TODO dump section headers499 const seg = lc.cast(macho.segment_command_64).?;
495 const seg = lc.segment.inner;
496 try writer.writeByte('\n');500 try writer.writeByte('\n');
497 try writer.print(501 try writer.print(
498 \\segname {s}502 \\segname {s}
...@@ -508,7 +512,7 @@ const MachODumper = struct {...@@ -508,7 +512,7 @@ const MachODumper = struct {
508 seg.filesize,512 seg.filesize,
509 });513 });
510514
511 for (lc.segment.sections.items) |sect| {515 for (lc.getSections()) |sect| {
512 try writer.writeByte('\n');516 try writer.writeByte('\n');
513 try writer.print(517 try writer.print(
514 \\sectname {s}518 \\sectname {s}
...@@ -531,7 +535,7 @@ const MachODumper = struct {...@@ -531,7 +535,7 @@ const MachODumper = struct {
531 .LOAD_WEAK_DYLIB,535 .LOAD_WEAK_DYLIB,
532 .REEXPORT_DYLIB,536 .REEXPORT_DYLIB,
533 => {537 => {
534 const dylib = lc.dylib.inner.dylib;538 const dylib = lc.cast(macho.dylib_command).?;
535 try writer.writeByte('\n');539 try writer.writeByte('\n');
536 try writer.print(540 try writer.print(
537 \\name {s}541 \\name {s}
...@@ -539,19 +543,20 @@ const MachODumper = struct {...@@ -539,19 +543,20 @@ const MachODumper = struct {
539 \\current version {x}543 \\current version {x}
540 \\compatibility version {x}544 \\compatibility version {x}
541 , .{545 , .{
542 mem.sliceTo(lc.dylib.data, 0),546 lc.getDylibPathName(),
543 dylib.timestamp,547 dylib.dylib.timestamp,
544 dylib.current_version,548 dylib.dylib.current_version,
545 dylib.compatibility_version,549 dylib.dylib.compatibility_version,
546 });550 });
547 },551 },
548552
549 .MAIN => {553 .MAIN => {
554 const main = lc.cast(macho.entry_point_command).?;
550 try writer.writeByte('\n');555 try writer.writeByte('\n');
551 try writer.print(556 try writer.print(
552 \\entryoff {x}557 \\entryoff {x}
553 \\stacksize {x}558 \\stacksize {x}
554 , .{ lc.main.entryoff, lc.main.stacksize });559 , .{ main.entryoff, main.stacksize });
555 },560 },
556561
557 .RPATH => {562 .RPATH => {
...@@ -559,7 +564,7 @@ const MachODumper = struct {...@@ -559,7 +564,7 @@ const MachODumper = struct {
559 try writer.print(564 try writer.print(
560 \\path {s}565 \\path {s}
561 , .{566 , .{
562 mem.sliceTo(lc.rpath.data, 0),567 lc.getRpathPathName(),
563 });568 });
564 },569 },
565570
lib/std/macho.zig+55-409
...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
780 return parseName(&sect.segname);780 return parseName(&sect.segname);
781 }781 }
782782
783 pub fn type_(sect: section_64) u8 {783 pub fn @"type"(sect: section_64) u8 {
784 return @truncate(u8, sect.flags & 0xff);784 return @truncate(u8, sect.flags & 0xff);
785 }785 }
786786
...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
794 }794 }
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
796 pub fn isDebug(sect: section_64) bool {801 pub fn isDebug(sect: section_64) bool {
797 return sect.attrs() & S_ATTR_DEBUG != 0;802 return sect.attrs() & S_ATTR_DEBUG != 0;
798 }803 }
...@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {...@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {
1835 kind: u16,1840 kind: u16,
1836};1841};
18371842
1838/// A Zig wrapper for all known MachO load commands.1843pub const LoadCommandIterator = struct {
1839/// Provides interface to read and write the load command data to a buffer.1844 ncmds: usize,
1840pub const LoadCommand = union(enum) {1845 buffer: []align(@alignOf(u64)) const u8,
1841 segment: SegmentCommand,1846 index: usize = 0,
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) = .{},
20091847
2010 pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {1848 pub const LoadCommand = struct {
2011 const inner = try reader.readStruct(segment_command_64);1849 hdr: load_command,
2012 var segment = SegmentCommand{1850 data: []const u8,
2013 .inner = inner,
2014 };
2015 try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
20161851
2017 var i: usize = 0;1852 pub fn cmd(lc: LoadCommand) LC {
2018 while (i < inner.nsects) : (i += 1) {1853 return lc.hdr.cmd;
2019 const sect = try reader.readStruct(section_64);
2020 segment.sections.appendAssumeCapacity(sect);
2021 }1854 }
20221855
2023 return segment;1856 pub fn cmdsize(lc: LoadCommand) u32 {
2024 }1857 return lc.hdr.cmdsize;
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;
2044 }1858 }
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.1860 pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
2054pub fn GenericCommandWithData(comptime Cmd: type) type {1861 if (lc.data.len < @sizeOf(Cmd)) return null;
2055 return struct {1862 return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
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 };
2071 }1863 }
20721864
2073 pub fn write(self: Self, writer: anytype) !void {1865 /// Asserts LoadCommand is of type segment_command_64.
2074 try writer.writeAll(mem.asBytes(&self.inner));1866 pub fn getSections(lc: LoadCommand) []const section_64 {
2075 try writer.writeAll(self.data);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;
2076 }1875 }
20771876
2078 pub fn deinit(self: *Self, allocator: Allocator) void {1877 /// Asserts LoadCommand is of type dylib_command.
2079 allocator.free(self.data);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);
2080 }1882 }
20811883
2082 pub fn eql(self: Self, other: Self) bool {1884 /// Asserts LoadCommand is of type rpath_command.
2083 if (!meta.eql(self.inner, other.inner)) return false;1885 pub fn getRpathPathName(lc: LoadCommand) []const u8 {
2084 return mem.eql(u8, self.data, other.data);1886 const rpath_lc = lc.cast(rpath_command).?;
1887 const data = lc.data[rpath_lc.path..];
1888 return mem.sliceTo(data, 0);
2085 }1889 }
2086 };1890 };
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" {1892 pub fn next(it: *LoadCommandIterator) ?LoadCommand {
2201 // TODO compiling for macOS from big-endian arch1893 if (it.index >= it.ncmds) return null;
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}
22411894
2242test "read-write C struct command" {1895 const hdr = @ptrCast(
2243 // TODO compiling for macOS from big-endian arch1896 *const load_command,
2244 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;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;1904 it.buffer = @alignCast(@alignOf(u64), it.buffer[hdr.cmdsize..]);
2247 const in_buffer = &[_]u8{1905 it.index += 1;
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 });
22601906
2261 var out_buffer: [in_buffer.len]u8 = undefined;1907 return cmd;
2262 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);1908 }
2263}1909};
src/link/Dwarf.zig+18-26
...@@ -853,8 +853,7 @@ pub fn commitDeclState(...@@ -853,8 +853,7 @@ pub fn commitDeclState(
853 .macho => {853 .macho => {
854 const macho_file = file.cast(File.MachO).?;854 const macho_file = file.cast(File.MachO).?;
855 const d_sym = &macho_file.d_sym.?;855 const d_sym = &macho_file.d_sym.?;
856 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;856 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
857 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
858 const file_pos = debug_line_sect.offset + src_fn.off;857 const file_pos = debug_line_sect.offset + src_fn.off;
859 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);858 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
860 },859 },
...@@ -933,8 +932,8 @@ pub fn commitDeclState(...@@ -933,8 +932,8 @@ pub fn commitDeclState(
933 .macho => {932 .macho => {
934 const macho_file = file.cast(File.MachO).?;933 const macho_file = file.cast(File.MachO).?;
935 const d_sym = &macho_file.d_sym.?;934 const d_sym = &macho_file.d_sym.?;
936 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;935 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
937 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];936 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
938 if (needed_size != debug_line_sect.size) {937 if (needed_size != debug_line_sect.size) {
939 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {938 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
940 const new_offset = d_sym.findFreeSpace(needed_size, 1);939 const new_offset = d_sym.findFreeSpace(needed_size, 1);
...@@ -955,10 +954,9 @@ pub fn commitDeclState(...@@ -955,10 +954,9 @@ pub fn commitDeclState(
955 );954 );
956955
957 debug_line_sect.offset = @intCast(u32, new_offset);956 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;
959 }958 }
960 debug_line_sect.size = needed_size;959 debug_line_sect.size = needed_size;
961 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
962 d_sym.debug_line_header_dirty = true;960 d_sym.debug_line_header_dirty = true;
963 }961 }
964 const file_pos = debug_line_sect.offset + src_fn.off;962 const file_pos = debug_line_sect.offset + src_fn.off;
...@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3...@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
1137 .macho => {1135 .macho => {
1138 const macho_file = file.cast(File.MachO).?;1136 const macho_file = file.cast(File.MachO).?;
1139 const d_sym = &macho_file.d_sym.?;1137 const d_sym = &macho_file.d_sym.?;
1140 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1138 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
1141 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1142 const file_pos = debug_info_sect.offset + atom.off;1139 const file_pos = debug_info_sect.offset + atom.off;
1143 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);1140 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
1144 },1141 },
...@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1235 .macho => {1232 .macho => {
1236 const macho_file = file.cast(File.MachO).?;1233 const macho_file = file.cast(File.MachO).?;
1237 const d_sym = &macho_file.d_sym.?;1234 const d_sym = &macho_file.d_sym.?;
1238 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1235 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1239 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];1236 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
1240 if (needed_size != debug_info_sect.size) {1237 if (needed_size != debug_info_sect.size) {
1241 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {1238 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
1242 const new_offset = d_sym.findFreeSpace(needed_size, 1);1239 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...@@ -1257,10 +1254,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1257 );1254 );
12581255
1259 debug_info_sect.offset = @intCast(u32, new_offset);1256 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;
1261 }1258 }
1262 debug_info_sect.size = needed_size;1259 debug_info_sect.size = needed_size;
1263 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
1264 d_sym.debug_line_header_dirty = true;1260 d_sym.debug_line_header_dirty = true;
1265 }1261 }
1266 const file_pos = debug_info_sect.offset + atom.off;1262 const file_pos = debug_info_sect.offset + atom.off;
...@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)...@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
1330 .macho => {1326 .macho => {
1331 const macho_file = file.cast(File.MachO).?;1327 const macho_file = file.cast(File.MachO).?;
1332 const d_sym = macho_file.d_sym.?;1328 const d_sym = macho_file.d_sym.?;
1333 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1329 const sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
1334 const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
1335 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();1330 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();
1336 try d_sym.file.pwriteAll(&data, file_pos);1331 try d_sym.file.pwriteAll(&data, file_pos);
1337 },1332 },
...@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {...@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1557 .macho => {1552 .macho => {
1558 const macho_file = file.cast(File.MachO).?;1553 const macho_file = file.cast(File.MachO).?;
1559 const d_sym = &macho_file.d_sym.?;1554 const d_sym = &macho_file.d_sym.?;
1560 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1555 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1561 const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?];1556 const debug_abbrev_sect = &d_sym.sections.items[d_sym.debug_abbrev_section_index.?];
1562 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);1557 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);
1563 if (needed_size > allocated_size) {1558 if (needed_size > allocated_size) {
1564 debug_abbrev_sect.size = 0; // free the space1559 debug_abbrev_sect.size = 0; // free the space
1565 const offset = d_sym.findFreeSpace(needed_size, 1);1560 const offset = d_sym.findFreeSpace(needed_size, 1);
1566 debug_abbrev_sect.offset = @intCast(u32, offset);1561 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;
1568 }1563 }
1569 debug_abbrev_sect.size = needed_size;1564 debug_abbrev_sect.size = needed_size;
1570 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{1565 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...@@ -1681,8 +1676,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
1681 .macho => {1676 .macho => {
1682 const macho_file = file.cast(File.MachO).?;1677 const macho_file = file.cast(File.MachO).?;
1683 const d_sym = &macho_file.d_sym.?;1678 const d_sym = &macho_file.d_sym.?;
1684 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1679 const debug_info_sect = d_sym.sections.items[d_sym.debug_info_section_index.?];
1685 const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?];
1686 const file_pos = debug_info_sect.offset;1680 const file_pos = debug_info_sect.offset;
1687 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);1681 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
1688 },1682 },
...@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {...@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
1998 .macho => {1992 .macho => {
1999 const macho_file = file.cast(File.MachO).?;1993 const macho_file = file.cast(File.MachO).?;
2000 const d_sym = &macho_file.d_sym.?;1994 const d_sym = &macho_file.d_sym.?;
2001 const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1995 const dwarf_seg = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
2002 const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?];1996 const debug_aranges_sect = &d_sym.sections.items[d_sym.debug_aranges_section_index.?];
2003 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);1997 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);
2004 if (needed_size > allocated_size) {1998 if (needed_size > allocated_size) {
2005 debug_aranges_sect.size = 0; // free the space1999 debug_aranges_sect.size = 0; // free the space
2006 const new_offset = d_sym.findFreeSpace(needed_size, 16);2000 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;
2008 debug_aranges_sect.offset = @intCast(u32, new_offset);2002 debug_aranges_sect.offset = @intCast(u32, new_offset);
2009 }2003 }
2010 debug_aranges_sect.size = needed_size;2004 debug_aranges_sect.size = needed_size;
...@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
2134 .macho => {2128 .macho => {
2135 const macho_file = file.cast(File.MachO).?;2129 const macho_file = file.cast(File.MachO).?;
2136 const d_sym = &macho_file.d_sym.?;2130 const d_sym = &macho_file.d_sym.?;
2137 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;2131 const debug_line_sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
2138 const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
2139 const file_pos = debug_line_sect.offset;2132 const file_pos = debug_line_sect.offset;
2140 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);2133 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
2141 },2134 },
...@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2264 .macho => {2257 .macho => {
2265 const macho_file = file.cast(File.MachO).?;2258 const macho_file = file.cast(File.MachO).?;
2266 const d_sym = &macho_file.d_sym.?;2259 const d_sym = &macho_file.d_sym.?;
2267 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;2260 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
2268 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
2269 break :blk debug_info_sect.offset;2261 break :blk debug_info_sect.offset;
2270 },2262 },
2271 // for wasm, the offset is always 0 as we write to memory first2263 // 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");...@@ -17,6 +17,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig");
17const bind = @import("MachO/bind.zig");17const bind = @import("MachO/bind.zig");
18const codegen = @import("../codegen.zig");18const codegen = @import("../codegen.zig");
19const dead_strip = @import("MachO/dead_strip.zig");19const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");
20const link = @import("../link.zig");21const link = @import("../link.zig");
21const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
22const target_util = @import("../target.zig");23const target_util = @import("../target.zig");
...@@ -60,6 +61,32 @@ const SystemLib = struct {...@@ -60,6 +61,32 @@ const SystemLib = struct {
60 weak: bool = false,61 weak: bool = false,
61};62};
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
63base: File,90base: File,
6491
65/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.92/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
...@@ -77,81 +104,32 @@ page_size: u16,...@@ -77,81 +104,32 @@ page_size: u16,
77/// fashion (default for LLVM backend).104/// fashion (default for LLVM backend).
78mode: enum { incremental, one_shot },105mode: enum { incremental, one_shot },
79106
80/// The absolute address of the entry point.107uuid: macho.uuid_command = .{
81entry_addr: ?u64 = null,108 .cmdsize = @sizeOf(macho.uuid_command),
82109 .uuid = undefined,
83/// Code signature (if any)110},
84code_signature: ?CodeSignature = null,
85111
86objects: std.ArrayListUnmanaged(Object) = .{},112objects: std.ArrayListUnmanaged(Object) = .{},
87archives: std.ArrayListUnmanaged(Archive) = .{},113archives: std.ArrayListUnmanaged(Archive) = .{},
88
89dylibs: std.ArrayListUnmanaged(Dylib) = .{},114dylibs: std.ArrayListUnmanaged(Dylib) = .{},
90dylibs_map: std.StringHashMapUnmanaged(u16) = .{},115dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
91referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},116referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
92117
93load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},118segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
94119sections: std.MultiArrayList(Section) = .{},
95pagezero_segment_cmd_index: ?u16 = null,120
96text_segment_cmd_index: ?u16 = null,121pagezero_segment_cmd_index: ?u8 = null,
97data_const_segment_cmd_index: ?u16 = null,122text_segment_cmd_index: ?u8 = null,
98data_segment_cmd_index: ?u16 = null,123data_const_segment_cmd_index: ?u8 = null,
99linkedit_segment_cmd_index: ?u16 = null,124data_segment_cmd_index: ?u8 = null,
100dyld_info_cmd_index: ?u16 = null,125linkedit_segment_cmd_index: ?u8 = null,
101symtab_cmd_index: ?u16 = null,126
102dysymtab_cmd_index: ?u16 = null,127text_section_index: ?u8 = null,
103dylinker_cmd_index: ?u16 = null,128stubs_section_index: ?u8 = null,
104data_in_code_cmd_index: ?u16 = null,129stub_helper_section_index: ?u8 = null,
105function_starts_cmd_index: ?u16 = null,130got_section_index: ?u8 = null,
106main_cmd_index: ?u16 = null,131la_symbol_ptr_section_index: ?u8 = null,
107dylib_id_cmd_index: ?u16 = null,132data_section_index: ?u8 = 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,
155133
156locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},134locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
157globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},135globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
...@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},...@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188166
189error_flags: File.ErrorFlags = File.ErrorFlags{},167error_flags: File.ErrorFlags = File.ErrorFlags{},
190168
191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,
193
194/// A helper var to indicate if we are at the start of the incremental updates, or169/// A helper var to indicate if we are at the start of the incremental updates, or
195/// already somewhere further along the update-and-run chain.170/// already somewhere further along the update-and-run chain.
196/// TODO once we add opening a prelinked output binary from file, this will become171/// TODO once we add opening a prelinked output binary from file, this will become
197/// obsolete as we will carry on where we left off.172/// obsolete as we will carry on where we left off.
198cold_start: bool = true,173cold_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
222/// List of atoms that are either synthetic or map directly to the Zig source program.175/// List of atoms that are either synthetic or map directly to the Zig source program.
223managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},176managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224177
...@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},
250/// We store them here so that we can properly dispose of any allocated203/// We store them here so that we can properly dispose of any allocated
251/// memory within the atom in the incremental linker.204/// memory within the atom in the incremental linker.
252/// TODO consolidate this.205/// TODO consolidate this.
253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},206decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
254207
255const Entry = struct {208const Entry = struct {
256 target: SymbolWithLoc,209 target: SymbolWithLoc,
...@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
408361
409pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {362pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
410 const cpu_arch = options.target.cpu.arch;363 const cpu_arch = options.target.cpu.arch;
411 const os_tag = options.target.os.tag;
412 const abi = options.target.abi;
413 const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;364 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);
417 const use_llvm = build_options.have_llvm and options.use_llvm;365 const use_llvm = build_options.have_llvm and options.use_llvm;
418 const use_stage1 = build_options.is_stage1 and options.use_stage1;366 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 {...@@ -428,10 +376,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
428 .file = null,376 .file = null,
429 },377 },
430 .page_size = page_size,378 .page_size = page_size,
431 .code_signature = if (requires_adhoc_codesig)
432 CodeSignature.init(page_size)
433 else
434 null,
435 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)379 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436 .one_shot380 .one_shot
437 else381 else
...@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
562 var dependent_libs = std.fifo.LinearFifo(struct {506 var dependent_libs = std.fifo.LinearFifo(struct {
563 id: Dylib.Id,507 id: Dylib.Id,
564 parent: u16,508 parent: u16,
565 }, .Dynamic).init(self.base.allocator);509 }, .Dynamic).init(arena);
566 defer dependent_libs.deinit();510
567 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);511 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);512 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569 }513 }
...@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
573 try self.createDyldPrivateAtom();517 try self.createDyldPrivateAtom();
574 try self.createStubHelperPreambleAtom();518 try self.createStubHelperPreambleAtom();
575 try self.resolveSymbolsInDylibs();519 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577520
578 if (self.unresolved.count() > 0) {521 if (self.unresolved.count() > 0) {
579 return error.UndefinedSymbolReference;522 return error.UndefinedSymbolReference;
...@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
583526
584 if (build_options.enable_logging) {527 if (build_options.enable_logging) {
585 self.logSymtab();528 self.logSymtab();
586 self.logSectionOrdinals();529 self.logSections();
587 self.logAtoms();530 self.logAtoms();
588 }531 }
589532
590 try self.writeAtomsIncremental();533 try self.writeAtomsIncremental();
591534
592 try self.setEntryPoint();535 var lc_buffer = std.ArrayList(u8).init(arena);
593 try self.updateSectionOrdinals();536 const lc_writer = lc_buffer.writer();
594 try self.writeLinkeditSegment();537 var ncmds: u32 = 0;
595538
596 if (self.d_sym) |*d_sym| {539 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
597 // Flush debug symbols bundle.540 try writeDylinkerLC(&ncmds, lc_writer);
598 try d_sym.flushModule(self.base.allocator, self.base.options);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;
599 }558 }
600559
601 // code signature and entitlements560 try self.writeBuildVersionLC(&ncmds, lc_writer);
602 if (self.base.options.entitlements) |path| {561
603 if (self.code_signature) |*csig| {562 {
604 try csig.addEntitlements(self.base.allocator, path);563 std.crypto.random.bytes(&self.uuid.uuid);
605 csig.code_directory.ident = self.base.options.emit.?.sub_path;564 try lc_writer.writeStruct(self.uuid);
606 } else {565 ncmds += 1;
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 }
612 }566 }
613567
614 if (self.code_signature) |*csig| {568 try self.writeLoadDylibLCs(&ncmds, lc_writer);
615 csig.clear(self.base.allocator);569
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;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: {
617 // Preallocate space for the code signature.579 // Preallocate space for the code signature.
618 // We need to do this at this stage so that we have the load commands with proper values580 // We need to do this at this stage so that we have the load commands with proper values
619 // written out to the file.581 // written out to the file.
620 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment582 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621 // where the code signature goes into.583 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);584 var codesig = CodeSignature.init(self.page_size);
623 }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();593 var headers_buf = std.ArrayList(u8).init(arena);
626 try self.writeHeader();594 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
627595
628 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {596 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
629 log.debug("flushing. no_entry_point_found = true", .{});597 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
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 }
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| {601 if (codesig) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last602 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
640 }603 }
641604
642 if (build_options.enable_link_snapshots) {605 if (self.d_sym) |*d_sym| {
643 if (self.base.options.enable_link_snapshots)606 // Flush debug symbols bundle.
644 try self.snapshotState();607 try d_sym.flushModule(self.base.allocator, self.base.options);
645 }608 }
646609
610 // if (build_options.enable_link_snapshots) {
611 // if (self.base.options.enable_link_snapshots)
612 // try self.snapshotState();
613 // }
614
647 if (cache_miss) {615 if (cache_miss) {
648 // Update the file with the digest. If it fails we can continue; it only616 // Update the file with the digest. If it fails we can continue; it only
649 // means that the next invocation will have an unnecessary cache miss.617 // 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)...@@ -708,6 +676,9 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
708 sub_prog_node.context.refresh();676 sub_prog_node.context.refresh();
709 defer sub_prog_node.end();677 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;
711 const is_lib = self.base.options.output_mode == .Lib;682 const is_lib = self.base.options.output_mode == .Lib;
712 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;683 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
713 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;684 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)...@@ -990,40 +961,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
990 }961 }
991 }962 }
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
1027 if (self.base.options.verbose_link) {964 if (self.base.options.verbose_link) {
1028 var argv = std.ArrayList([]const u8).init(arena);965 var argv = std.ArrayList([]const u8).init(arena);
1029966
...@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1048 try argv.append(syslibroot);985 try argv.append(syslibroot);
1049 }986 }
1050987
1051 for (rpath_table.keys()) |rpath| {988 for (self.base.options.rpath_list) |rpath| {
1052 try argv.append("-rpath");989 try argv.append("-rpath");
1053 try argv.append(rpath);990 try argv.append(rpath);
1054 }991 }
...@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1157 var dependent_libs = std.fifo.LinearFifo(struct {1094 var dependent_libs = std.fifo.LinearFifo(struct {
1158 id: Dylib.Id,1095 id: Dylib.Id,
1159 parent: u16,1096 parent: u16,
1160 }, .Dynamic).init(gpa);1097 }, .Dynamic).init(arena);
1161 defer dependent_libs.deinit();1098
1162 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);1099 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1163 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());1100 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1164 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);1101 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1165 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);1102 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
11661103
1167 for (self.objects.items) |*object, object_id| {1104 for (self.objects.items) |_, object_id| {
1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));1105 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1169 }1106 }
11701107
1171 try self.resolveSymbolsInArchives();1108 try self.resolveSymbolsInArchives();
1172 try self.resolveDyldStubBinder();1109 try self.resolveDyldStubBinder();
1173 try self.createDyldPrivateAtom();
1174 try self.createStubHelperPreambleAtom();
1175 try self.resolveSymbolsInDylibs();1110 try self.resolveSymbolsInDylibs();
1176 try self.createMhExecuteHeaderSymbol();1111 try self.createMhExecuteHeaderSymbol();
1177 try self.createDsoHandleSymbol();1112 try self.createDsoHandleSymbol();
1178 try self.addCodeSignatureLC();
1179 try self.resolveSymbolsAtLoading();1113 try self.resolveSymbolsAtLoading();
11801114
1181 if (self.unresolved.count() > 0) {1115 if (self.unresolved.count() > 0) {
...@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1188 return error.FrameworkNotFound;1122 return error.FrameworkNotFound;
1189 }1123 }
11901124
1125 for (self.objects.items) |*object| {
1126 try object.scanInputSections(self);
1127 }
1128
1129 try self.createDyldPrivateAtom();
1191 try self.createTentativeDefAtoms();1130 try self.createTentativeDefAtoms();
1131 try self.createStubHelperPreambleAtom();
11921132
1193 for (self.objects.items) |*object, object_id| {1133 for (self.objects.items) |*object, object_id| {
1194 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));1134 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
...@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1198 try dead_strip.gcAtoms(self);1138 try dead_strip.gcAtoms(self);
1199 }1139 }
12001140
1201 try self.pruneAndSortSections();
1202 try self.allocateSegments();1141 try self.allocateSegments();
1203 try self.allocateSymbols();1142 try self.allocateSymbols();
12041143
1205 try self.allocateSpecialSymbols();1144 try self.allocateSpecialSymbols();
12061145
1207 if (build_options.enable_logging) {1146 if (build_options.enable_logging or true) {
1208 self.logSymtab();1147 self.logSymtab();
1209 self.logSectionOrdinals();1148 self.logSections();
1210 self.logAtoms();1149 self.logAtoms();
1211 }1150 }
12121151
1213 try self.writeAtomsOneShot();1152 try self.writeAtomsOneShot();
12141153
1215 if (self.rustc_section_index) |id| {1154 var lc_buffer = std.ArrayList(u8).init(arena);
1216 const sect = self.getSectionPtr(.{1155 const lc_writer = lc_buffer.writer();
1217 .seg = self.data_segment_cmd_index.?,1156 var ncmds: u32 = 0;
1218 .sect = id,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,
1219 });1168 });
1220 sect.size = self.rustc_section_size;1169 ncmds += 1;
1221 }1170 }
12221171
1223 try self.setEntryPoint();1172 try self.writeBuildVersionLC(&ncmds, lc_writer);
1224 try self.writeLinkeditSegment();
12251173
1226 if (self.code_signature) |*csig| {1174 {
1227 csig.clear(gpa);1175 var uuid_lc = macho.uuid_command{
1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;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: {
1229 // Preallocate space for the code signature.1193 // Preallocate space for the code signature.
1230 // We need to do this at this stage so that we have the load commands with proper values1194 // We need to do this at this stage so that we have the load commands with proper values
1231 // written out to the file.1195 // written out to the file.
1232 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment1196 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
1233 // where the code signature goes into.1197 // where the code signature goes into.
1234 try self.writeCodeSignaturePadding(csig);1198 var codesig = CodeSignature.init(self.page_size);
1235 }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();1210 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1238 try self.writeHeader();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| {1215 if (codesig) |*csig| {
1243 try self.writeCodeSignature(csig); // code signing always comes last1216 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
1244 }1217 }
1245 }1218 }
12461219
...@@ -1395,66 +1368,77 @@ fn resolveFramework(...@@ -1395,66 +1368,77 @@ fn resolveFramework(
1395}1368}
13961369
1397fn parseObject(self: *MachO, path: []const u8) !bool {1370fn parseObject(self: *MachO, path: []const u8) !bool {
1371 const gpa = self.base.allocator;
1398 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1372 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1399 error.FileNotFound => return false,1373 error.FileNotFound => return false,
1400 else => |e| return e,1374 else => |e| return e,
1401 };1375 };
1402 errdefer file.close();1376 defer file.close();
1403
1404 const name = try self.base.allocator.dupe(u8, path);
1405 errdefer self.base.allocator.free(name);
14061377
1378 const name = try gpa.dupe(u8, path);
1379 errdefer gpa.free(name);
1380 const cpu_arch = self.base.options.target.cpu.arch;
1407 const mtime: u64 = mtime: {1381 const mtime: u64 = mtime: {
1408 const stat = file.stat() catch break :mtime 0;1382 const stat = file.stat() catch break :mtime 0;
1409 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));1383 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
1410 };1384 };
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
1412 var object = Object{1389 var object = Object{
1413 .name = name,1390 .name = name,
1414 .file = file,
1415 .mtime = mtime,1391 .mtime = mtime,
1392 .contents = contents,
1416 };1393 };
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) {
1419 error.EndOfStream, error.NotObject => {1396 error.EndOfStream, error.NotObject => {
1420 object.deinit(self.base.allocator);1397 object.deinit(gpa);
1421 return false;1398 return false;
1422 },1399 },
1423 else => |e| return e,1400 else => |e| return e,
1424 };1401 };
14251402
1426 try self.objects.append(self.base.allocator, object);1403 try self.objects.append(gpa, object);
14271404
1428 return true;1405 return true;
1429}1406}
14301407
1431fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {1408fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1409 const gpa = self.base.allocator;
1432 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1410 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1433 error.FileNotFound => return false,1411 error.FileNotFound => return false,
1434 else => |e| return e,1412 else => |e| return e,
1435 };1413 };
1436 errdefer file.close();1414 errdefer file.close();
14371415
1438 const name = try self.base.allocator.dupe(u8, path);1416 const name = try gpa.dupe(u8, path);
1439 errdefer self.base.allocator.free(name);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
1441 var archive = Archive{1423 var archive = Archive{
1442 .name = name,1424 .name = name,
1425 .fat_offset = fat_offset,
1443 .file = file,1426 .file = file,
1444 };1427 };
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) {
1447 error.EndOfStream, error.NotArchive => {1430 error.EndOfStream, error.NotArchive => {
1448 archive.deinit(self.base.allocator);1431 archive.deinit(gpa);
1449 return false;1432 return false;
1450 },1433 },
1451 else => |e| return e,1434 else => |e| return e,
1452 };1435 };
14531436
1454 if (force_load) {1437 if (force_load) {
1455 defer archive.deinit(self.base.allocator);1438 defer archive.deinit(gpa);
1439 defer file.close();
1456 // Get all offsets from the ToC1440 // 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);
1458 defer offsets.deinit();1442 defer offsets.deinit();
1459 for (archive.toc.values()) |offs| {1443 for (archive.toc.values()) |offs| {
1460 for (offs.items) |off| {1444 for (offs.items) |off| {
...@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {...@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1462 }1446 }
1463 }1447 }
1464 for (offsets.keys()) |off| {1448 for (offsets.keys()) |off| {
1465 const object = try self.objects.addOne(self.base.allocator);1449 const object = try archive.parseObject(gpa, cpu_arch, off);
1466 object.* = try archive.parseObject(1450 try self.objects.append(gpa, object);
1467 self.base.allocator,
1468 self.base.options.target.cpu.arch,
1469 off,
1470 );
1471 }1451 }
1472 } else {1452 } else {
1473 try self.archives.append(self.base.allocator, archive);1453 try self.archives.append(gpa, archive);
1474 }1454 }
14751455
1476 return true;1456 return true;
...@@ -1481,6 +1461,7 @@ const ParseDylibError = error{...@@ -1481,6 +1461,7 @@ const ParseDylibError = error{
1481 EmptyStubFile,1461 EmptyStubFile,
1482 MismatchedCpuArchitecture,1462 MismatchedCpuArchitecture,
1483 UnsupportedCpuArchitecture,1463 UnsupportedCpuArchitecture,
1464 EndOfStream,
1484} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;1465} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
14851466
1486const DylibCreateOpts = struct {1467const DylibCreateOpts = struct {
...@@ -1497,43 +1478,53 @@ pub fn parseDylib(...@@ -1497,43 +1478,53 @@ pub fn parseDylib(
1497 dependent_libs: anytype,1478 dependent_libs: anytype,
1498 opts: DylibCreateOpts,1479 opts: DylibCreateOpts,
1499) ParseDylibError!bool {1480) ParseDylibError!bool {
1481 const gpa = self.base.allocator;
1500 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1482 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1501 error.FileNotFound => return false,1483 error.FileNotFound => return false,
1502 else => |e| return e,1484 else => |e| return e,
1503 };1485 };
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);1492 const reader = file.reader();
1507 errdefer self.base.allocator.free(name);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
1509 const dylib_id = @intCast(u16, self.dylibs.items.len);1501 const dylib_id = @intCast(u16, self.dylibs.items.len);
1510 var dylib = Dylib{1502 var dylib = Dylib{ .weak = opts.weak };
1511 .name = name,
1512 .file = file,
1513 .weak = opts.weak,
1514 };
15151503
1516 dylib.parse(1504 dylib.parseFromBinary(
1517 self.base.allocator,1505 gpa,
1518 self.base.options.target.cpu.arch,1506 cpu_arch,
1519 dylib_id,1507 dylib_id,
1520 dependent_libs,1508 dependent_libs,
1509 path,
1510 contents,
1521 ) catch |err| switch (err) {1511 ) catch |err| switch (err) {
1522 error.EndOfStream, error.NotDylib => {1512 error.EndOfStream, error.NotDylib => {
1523 try file.seekTo(0);1513 try file.seekTo(0);
15241514
1525 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {1515 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
1526 dylib.deinit(self.base.allocator);1516 dylib.deinit(gpa);
1527 return false;1517 return false;
1528 };1518 };
1529 defer lib_stub.deinit();1519 defer lib_stub.deinit();
15301520
1531 try dylib.parseFromStub(1521 try dylib.parseFromStub(
1532 self.base.allocator,1522 gpa,
1533 self.base.options.target,1523 self.base.options.target,
1534 lib_stub,1524 lib_stub,
1535 dylib_id,1525 dylib_id,
1536 dependent_libs,1526 dependent_libs,
1527 path,
1537 );1528 );
1538 },1529 },
1539 else => |e| return e,1530 else => |e| return e,
...@@ -1547,13 +1538,13 @@ pub fn parseDylib(...@@ -1547,13 +1538,13 @@ pub fn parseDylib(
1547 log.warn(" dylib version: {}", .{dylib.id.?.current_version});1538 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
15481539
1549 // TODO maybe this should be an error and facilitate auto-cleanup?1540 // TODO maybe this should be an error and facilitate auto-cleanup?
1550 dylib.deinit(self.base.allocator);1541 dylib.deinit(gpa);
1551 return false;1542 return false;
1552 }1543 }
1553 }1544 }
15541545
1555 try self.dylibs.append(self.base.allocator, dylib);1546 try self.dylibs.append(gpa, dylib);
1556 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);1547 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
15571548
1558 const should_link_dylib_even_if_unreachable = blk: {1549 const should_link_dylib_even_if_unreachable = blk: {
1559 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;1550 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
...@@ -1561,8 +1552,7 @@ pub fn parseDylib(...@@ -1561,8 +1552,7 @@ pub fn parseDylib(
1561 };1552 };
15621553
1563 if (should_link_dylib_even_if_unreachable) {1554 if (should_link_dylib_even_if_unreachable) {
1564 try self.addLoadDylibLC(dylib_id);1555 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
1565 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1566 }1556 }
15671557
1568 return true;1558 return true;
...@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1572 for (files) |file_name| {1562 for (files) |file_name| {
1573 const full_path = full_path: {1563 const full_path = full_path: {
1574 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1564 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1575 const path = try fs.realpath(file_name, &buffer);1565 break :full_path try fs.realpath(file_name, &buffer);
1576 break :full_path try self.base.allocator.dupe(u8, path);
1577 };1566 };
1578 defer self.base.allocator.free(full_path);
1579 log.debug("parsing input file path '{s}'", .{full_path});1567 log.debug("parsing input file path '{s}'", .{full_path});
15801568
1581 if (try self.parseObject(full_path)) continue;1569 if (try self.parseObject(full_path)) continue;
...@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1592 for (files) |file_name| {1580 for (files) |file_name| {
1593 const full_path = full_path: {1581 const full_path = full_path: {
1594 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1582 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1595 const path = try fs.realpath(file_name, &buffer);1583 break :full_path try fs.realpath(file_name, &buffer);
1596 break :full_path try self.base.allocator.dupe(u8, path);
1597 };1584 };
1598 defer self.base.allocator.free(full_path);
1599 log.debug("parsing and force loading static archive '{s}'", .{full_path});1585 log.debug("parsing and force loading static archive '{s}'", .{full_path});
16001586
1601 if (try self.parseArchive(full_path, true)) continue;1587 if (try self.parseArchive(full_path, true)) continue;
...@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1669 }1655 }
1670}1656}
16711657
1672pub const MatchingSection = struct {1658pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
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 {
1687 const segname = sect.segName();1659 const segname = sect.segName();
1688 const sectname = sect.sectName();1660 const sectname = sect.sectName();
1689 const res: ?MatchingSection = blk: {1661 const res: ?u8 = blk: {
1690 switch (sect.type_()) {1662 if (mem.eql(u8, "__LLVM", segname)) {
1691 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {1663 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1692 if (self.text_const_section_index == null) {1664 sect.flags, segname, sectname,
1693 self.text_const_section_index = try self.initSection(1665 });
1694 self.text_segment_cmd_index.?,1666 break :blk null;
1695 "__const",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,
1696 sect.size,1714 sect.size,
1697 sect.@"align",1715 sect.@"align",
1698 .{},1716 .{},
1699 );1717 );
1700 }1718 }
17011719 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
1702 break :blk .{1720 "__TEXT",
1703 .seg = self.text_segment_cmd_index.?,1721 "__cstring",
1704 .sect = self.text_const_section_index.?,1722 sect.size,
1705 };1723 sect.@"align",
1724 .{ .flags = macho.S_CSTRING_LITERALS },
1725 );
1706 },1726 },
1707 macho.S_CSTRING_LITERALS => {1727 macho.S_MOD_INIT_FUNC_POINTERS,
1708 if (mem.eql(u8, sectname, "__objc_methname")) {1728 macho.S_MOD_TERM_FUNC_POINTERS,
1709 // TODO it seems the common values within the sections in objects are deduplicated/merged1729 => {
1710 // on merging the sections' contents.1730 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
1711 if (self.objc_methname_section_index == null) {1731 "__DATA_CONST",
1712 self.objc_methname_section_index = try self.initSection(1732 sectname,
1713 self.text_segment_cmd_index.?,1733 sect.size,
1714 "__objc_methname",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",
1715 sect.size,1773 sect.size,
1716 sect.@"align",1774 sect.@"align",
1717 .{},1775 .{},
1718 );1776 );
1719 }1777 }
17201778 }
1721 break :blk .{1779 if (mem.eql(u8, segname, "__DATA")) {
1722 .seg = self.text_segment_cmd_index.?,1780 if (mem.eql(u8, sectname, "__const") or
1723 .sect = self.objc_methname_section_index.?,1781 mem.eql(u8, sectname, "__cfstring") or
1724 };1782 mem.eql(u8, sectname, "__objc_classlist") or
1725 } else if (mem.eql(u8, sectname, "__objc_methtype")) {1783 mem.eql(u8, sectname, "__objc_imageinfo"))
1726 if (self.objc_methtype_section_index == null) {1784 {
1727 self.objc_methtype_section_index = try self.initSection(1785 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
1728 self.text_segment_cmd_index.?,1786 try self.initSection(
1729 "__objc_methtype",1787 "__DATA_CONST",
1788 sectname,
1730 sect.size,1789 sect.size,
1731 sect.@"align",1790 sect.@"align",
1732 .{},1791 .{},
1733 );1792 );
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.?;
1734 }1804 }
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 .{1820pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1737 .seg = self.text_segment_cmd_index.?,1821 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1738 .sect = self.objc_methtype_section_index.?,1822 const atom = try gpa.create(Atom);
1739 };1823 errdefer gpa.destroy(atom);
1740 } else if (mem.eql(u8, sectname, "__objc_classname")) {1824 atom.* = Atom.empty;
1741 if (self.objc_classname_section_index == null) {1825 atom.sym_index = sym_index;
1742 self.objc_classname_section_index = try self.initSection(1826 atom.size = size;
1743 self.text_segment_cmd_index.?,1827 atom.alignment = alignment;
1744 "__objc_classname",
1745 sect.size,
1746 sect.@"align",
1747 .{},
1748 );
1749 }
17501828
1751 break :blk .{1829 try atom.code.resize(gpa, size_usize);
1752 .seg = self.text_segment_cmd_index.?,1830 mem.set(u8, atom.code.items, 0);
1753 .sect = self.objc_classname_section_index.?,
1754 };
1755 }
17561831
1757 if (self.cstring_section_index == null) {1832 return atom;
1758 self.cstring_section_index = try self.initSection(1833}
1759 self.text_segment_cmd_index.?,
1760 "__cstring",
1761 sect.size,
1762 sect.@"align",
1763 .{
1764 .flags = macho.S_CSTRING_LITERALS,
1765 },
1766 );
1767 }
17681834
1769 break :blk .{1835pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1770 .seg = self.text_segment_cmd_index.?,1836 const section = self.sections.get(sect_id);
1771 .sect = self.cstring_section_index.?,1837 const sym = atom.getSymbol(self);
1772 };1838 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1773 },1839 try atom.resolveRelocs(self);
1774 macho.S_LITERAL_POINTERS => {1840 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1775 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {1841 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1776 if (self.objc_selrefs_section_index == null) {1842}
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 }
17871843
1788 break :blk .{1844fn allocateSymbols(self: *MachO) !void {
1789 .seg = self.data_segment_cmd_index.?,1845 const slice = self.sections.slice();
1790 .sect = self.objc_selrefs_section_index.?,1846 for (slice.items(.last_atom)) |last_atom, sect_id| {
1791 };1847 const header = slice.items(.header)[sect_id];
1792 } else {1848 var atom = last_atom orelse continue;
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 }
18091849
1810 break :blk .{1850 while (atom.prev) |prev| {
1811 .seg = self.data_const_segment_cmd_index.?,1851 atom = prev;
1812 .sect = self.mod_init_func_section_index.?,1852 }
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 }
18271853
1828 break :blk .{1854 const n_sect = @intCast(u8, sect_id + 1);
1829 .seg = self.data_const_segment_cmd_index.?,1855 var base_vaddr = header.addr;
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 }
18451856
1846 break :blk .{1857 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1847 .seg = self.data_segment_cmd_index.?,1858 n_sect,
1848 .sect = self.bss_section_index.?,1859 header.segName(),
1849 };1860 header.sectName(),
1850 },1861 });
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 }
18631862
1864 break :blk .{1863 while (true) {
1865 .seg = self.data_segment_cmd_index.?,1864 const alignment = try math.powi(u32, 2, atom.alignment);
1866 .sect = self.tlv_section_index.?,1865 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
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 }
18811866
1882 break :blk .{1867 const sym = atom.getSymbolPtr(self);
1883 .seg = self.data_segment_cmd_index.?,1868 sym.n_value = base_vaddr;
1884 .sect = self.tlv_ptrs_section_index.?,1869 sym.n_sect = n_sect;
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 }
18991870
1900 break :blk .{1871 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
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 }
19171872
1918 break :blk .{1873 // Update each symbol contained within the atom
1919 .seg = self.data_segment_cmd_index.?,1874 for (atom.contained.items) |sym_at_off| {
1920 .sect = self.tlv_bss_section_index.?,1875 const contained_sym = self.getSymbolPtr(.{
1921 };1876 .sym_index = sym_at_off.sym_index,
1922 },1877 .file = atom.file,
1923 macho.S_COALESCED => {1878 });
1924 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {1879 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1925 // TODO I believe __eh_frame is currently part of __unwind_info section1880 contained_sym.n_sect = n_sect;
1926 // in the latest ld64 output.1881 }
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 }
19361882
1937 break :blk .{1883 base_vaddr += atom.size;
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;
23051884
2306 if (atom.next) |next| {1885 if (atom.next) |next| {
2307 atom = next;1886 atom = next;
...@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {...@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {
2310 }1889 }
2311}1890}
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
2331fn allocateSpecialSymbols(self: *MachO) !void {1892fn allocateSpecialSymbols(self: *MachO) !void {
2332 for (&[_][]const u8{1893 for (&[_][]const u8{
2333 "___dso_handle",1894 "___dso_handle",
...@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2336 const global = self.globals.get(name) orelse continue;1897 const global = self.globals.get(name) orelse continue;
2337 if (global.file != null) continue;1898 if (global.file != null) continue;
2338 const sym = self.getSymbolPtr(global);1899 const sym = self.getSymbolPtr(global);
2339 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;1900 const seg = self.segments.items[self.text_segment_cmd_index.?];
2340 sym.n_sect = self.getSectionOrdinal(.{1901 sym.n_sect = 1;
2341 .seg = self.text_segment_cmd_index.?,1902 sym.n_value = seg.vmaddr;
2342 .sect = 0,
2343 });
2344 sym.n_value = seg.inner.vmaddr;
23451903
2346 log.debug("allocating {s} at the start of {s}", .{1904 log.debug("allocating {s} at the start of {s}", .{
2347 name,1905 name,
2348 seg.inner.segName(),1906 seg.segName(),
2349 });1907 });
2350 }1908 }
2351}1909}
...@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2353fn writeAtomsOneShot(self: *MachO) !void {1911fn writeAtomsOneShot(self: *MachO) !void {
2354 assert(self.mode == .one_shot);1912 assert(self.mode == .one_shot);
23551913
2356 var it = self.atoms.iterator();1914 const gpa = self.base.allocator;
2357 while (it.next()) |entry| {1915 const slice = self.sections.slice();
2358 const sect = self.getSection(entry.key_ptr.*);1916
2359 var atom: *Atom = entry.value_ptr.*;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);
2364 defer buffer.deinit();1925 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
2369 while (atom.prev) |prev| {1930 while (atom.prev) |prev| {
2370 atom = prev;1931 atom = prev;
...@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {...@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {
2399 if (atom.next) |next| {1960 if (atom.next) |next| {
2400 atom = next;1961 atom = next;
2401 } else {1962 } else {
2402 assert(buffer.items.len == sect.size);1963 assert(buffer.items.len == header.size);
2403 log.debug(" (writing at file offset 0x{x})", .{sect.offset});1964 log.debug(" (writing at file offset 0x{x})", .{header.offset});
2404 try self.base.file.?.pwriteAll(buffer.items, sect.offset);1965 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2405 break;1966 break;
2406 }1967 }
2407 }1968 }
2408 }1969 }
2409}1970}
24101971
2411fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anytype) !void {1972fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
2412 const is_code = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;1973 const header = self.sections.items(.header)[sect_id];
2413 const min_alignment: u3 = if (!is_code)1974 const min_alignment: u3 = if (!header.isCode())
2414 11975 1
2415 else switch (self.base.options.target.cpu.arch) {1976 else switch (self.base.options.target.cpu.arch) {
2416 .aarch64 => @sizeOf(u32),1977 .aarch64 => @sizeOf(u32),
...@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2421 const len = @divExact(size, min_alignment);1982 const len = @divExact(size, min_alignment);
2422 var i: usize = 0;1983 var i: usize = 0;
2423 while (i < len) : (i += 1) {1984 while (i < len) : (i += 1) {
2424 if (!is_code) {1985 if (!header.isCode()) {
2425 try writer.writeByte(0);1986 try writer.writeByte(0);
2426 } else switch (self.base.options.target.cpu.arch) {1987 } else switch (self.base.options.target.cpu.arch) {
2427 .aarch64 => {1988 .aarch64 => {
...@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2439fn writeAtomsIncremental(self: *MachO) !void {2000fn writeAtomsIncremental(self: *MachO) !void {
2440 assert(self.mode == .incremental);2001 assert(self.mode == .incremental);
24412002
2442 var it = self.atoms.iterator();2003 const slice = self.sections.slice();
2443 while (it.next()) |entry| {2004 for (slice.items(.last_atom)) |last, i| {
2444 const match = entry.key_ptr.*;2005 var atom: *Atom = last orelse continue;
2445 const sect = self.getSection(match);2006 const sect_i = @intCast(u8, i);
2446 var atom: *Atom = entry.value_ptr.*;2007 const header = slice.items(.header)[sect_i];
24472008
2448 // TODO handle zerofill in stage22009 if (header.isZerofill()) continue;
2449 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) 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
2453 while (true) {2013 while (true) {
2454 if (atom.dirty) {2014 if (atom.dirty) {
2455 try self.writeAtom(atom, match);2015 try self.writeAtom(atom, sect_i);
2456 atom.dirty = false;2016 atom.dirty = false;
2457 }2017 }
24582018
...@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2503 try self.managed_atoms.append(gpa, atom);2063 try self.managed_atoms.append(gpa, atom);
2504 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2064 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25052065
2506 try self.allocateAtomCommon(atom, .{2066 try self.allocateAtomCommon(atom, self.got_section_index.?);
2507 .seg = self.data_const_segment_cmd_index.?,
2508 .sect = self.got_section_index.?,
2509 });
25102067
2511 return atom;2068 return atom;
2512}2069}
...@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2535 try self.managed_atoms.append(gpa, atom);2092 try self.managed_atoms.append(gpa, atom);
2536 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2093 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25372094
2538 const match = (try self.getMatchingSection(.{2095 const match = (try self.getOutputSection(.{
2539 .segname = makeStaticString("__DATA"),2096 .segname = makeStaticString("__DATA"),
2540 .sectname = makeStaticString("__thread_ptrs"),2097 .sectname = makeStaticString("__thread_ptrs"),
2541 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,2098 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
...@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2561 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);2118 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2562 self.dyld_private_atom = atom;2119 self.dyld_private_atom = atom;
25632120
2564 try self.allocateAtomCommon(atom, .{2121 try self.allocateAtomCommon(atom, self.data_section_index.?);
2565 .seg = self.data_segment_cmd_index.?,
2566 .sect = self.data_section_index.?,
2567 });
25682122
2569 try self.managed_atoms.append(gpa, atom);2123 try self.managed_atoms.append(gpa, atom);
2570 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2124 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
...@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2692 }2246 }
2693 self.stub_helper_preamble_atom = atom;2247 self.stub_helper_preamble_atom = atom;
26942248
2695 try self.allocateAtomCommon(atom, .{2249 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2696 .seg = self.text_segment_cmd_index.?,
2697 .sect = self.stub_helper_section_index.?,
2698 });
26992250
2700 try self.managed_atoms.append(gpa, atom);2251 try self.managed_atoms.append(gpa, atom);
2701 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2252 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
...@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2771 try self.managed_atoms.append(gpa, atom);2322 try self.managed_atoms.append(gpa, atom);
2772 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2323 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
27732324
2774 try self.allocateAtomCommon(atom, .{2325 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2775 .seg = self.text_segment_cmd_index.?,
2776 .sect = self.stub_helper_section_index.?,
2777 });
27782326
2779 return atom;2327 return atom;
2780}2328}
...@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi...@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
2814 try self.managed_atoms.append(gpa, atom);2362 try self.managed_atoms.append(gpa, atom);
2815 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2363 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28162364
2817 try self.allocateAtomCommon(atom, .{2365 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
2818 .seg = self.data_segment_cmd_index.?,
2819 .sect = self.la_symbol_ptr_section_index.?,
2820 });
28212366
2822 return atom;2367 return atom;
2823}2368}
...@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2896 try self.managed_atoms.append(gpa, atom);2441 try self.managed_atoms.append(gpa, atom);
2897 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2442 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28982443
2899 try self.allocateAtomCommon(atom, .{2444 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
2900 .seg = self.text_segment_cmd_index.?,
2901 .sect = self.stubs_section_index.?,
2902 });
29032445
2904 return atom;2446 return atom;
2905}2447}
...@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29172459
2918 // Convert any tentative definition into a regular symbol and allocate2460 // Convert any tentative definition into a regular symbol and allocate
2919 // text blocks for each tentative definition.2461 // 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
2926 const size = sym.n_value;2462 const size = sym.n_value;
2927 const alignment = (sym.n_desc >> 8) & 0x0f;2463 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
2929 sym.* = .{2470 sym.* = .{
2930 .n_strx = sym.n_strx,2471 .n_strx = sym.n_strx,
2931 .n_type = macho.N_SECT | macho.N_EXT,2472 .n_type = macho.N_SECT | macho.N_EXT,
2932 .n_sect = 0,2473 .n_sect = n_sect,
2933 .n_desc = 0,2474 .n_desc = 0,
2934 .n_value = 0,2475 .n_value = 0,
2935 };2476 };
...@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2937 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);2478 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
2938 atom.file = global.file;2479 atom.file = global.file;
29392480
2940 try self.allocateAtomCommon(atom, match);2481 try self.allocateAtomCommon(atom, n_sect);
29412482
2942 if (global.file) |file| {2483 if (global.file) |file| {
2943 const object = &self.objects.items[file];2484 const object = &self.objects.items[file];
...@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
3060 gop.value_ptr.* = current;2601 gop.value_ptr.* = current;
3061}2602}
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];
3064 log.debug("resolving symbols in '{s}'", .{object.name});2606 log.debug("resolving symbols in '{s}'", .{object.name});
30652607
3066 for (object.symtab.items) |sym, index| {2608 for (object.symtab.items) |sym, index| {
...@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {...@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
3115fn resolveSymbolsInArchives(self: *MachO) !void {2657fn resolveSymbolsInArchives(self: *MachO) !void {
3116 if (self.archives.items.len == 0) return;2658 if (self.archives.items.len == 0) return;
31172659
2660 const gpa = self.base.allocator;
2661 const cpu_arch = self.base.options.target.cpu.arch;
3118 var next_sym: usize = 0;2662 var next_sym: usize = 0;
3119 loop: while (next_sym < self.unresolved.count()) {2663 loop: while (next_sym < self.unresolved.count()) {
3120 const global = self.globals.values()[self.unresolved.keys()[next_sym]];2664 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
...@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
3129 assert(offsets.items.len > 0);2673 assert(offsets.items.len > 0);
31302674
3131 const object_id = @intCast(u16, self.objects.items.len);2675 const object_id = @intCast(u16, self.objects.items.len);
3132 const object = try self.objects.addOne(self.base.allocator);2676 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
3133 object.* = try archive.parseObject(2677 try self.objects.append(gpa, object);
3134 self.base.allocator,2678 try self.resolveSymbolsInObject(object_id);
3135 self.base.options.target.cpu.arch,
3136 offsets.items[0],
3137 );
3138 try self.resolveSymbolsInObject(object, object_id);
31392679
3140 continue :loop;2680 continue :loop;
3141 }2681 }
...@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31592699
3160 const dylib_id = @intCast(u16, id);2700 const dylib_id = @intCast(u16, id);
3161 if (!self.referenced_dylibs.contains(dylib_id)) {2701 if (!self.referenced_dylibs.contains(dylib_id)) {
3162 try self.addLoadDylibLC(dylib_id);
3163 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});2702 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
3164 }2703 }
31652704
...@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32572796
3258 const dylib_id = @intCast(u16, id);2797 const dylib_id = @intCast(u16, id);
3259 if (!self.referenced_dylibs.contains(dylib_id)) {2798 if (!self.referenced_dylibs.contains(dylib_id)) {
3260 try self.addLoadDylibLC(dylib_id);
3261 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});2799 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
3262 }2800 }
32632801
...@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3280 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2818 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3281}2819}
32822820
3283fn addLoadDylibLC(self: *MachO, id: u16) !void {2821fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
3284 const dylib = self.dylibs.items[id];2822 const name_len = mem.sliceTo(default_dyld_path, 0).len;
3285 const dylib_id = dylib.id orelse unreachable;2823 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3286 var dylib_cmd = try macho.createLoadDylibCommand(2824 u64,
3287 self.base.allocator,2825 @sizeOf(macho.dylinker_command) + name_len,
3288 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,2826 @sizeOf(u64),
3289 dylib_id.name,2827 ));
3290 dylib_id.timestamp,2828 try lc_writer.writeStruct(macho.dylinker_command{
3291 dylib_id.current_version,2829 .cmd = .LOAD_DYLINKER,
3292 dylib_id.compatibility_version,2830 .cmdsize = cmdsize,
3293 );2831 .name = @sizeOf(macho.dylinker_command),
3294 errdefer dylib_cmd.deinit(self.base.allocator);2832 });
3295 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });2833 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
3296 self.load_commands_dirty = true;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;
3297}2839}
32982840
3299fn addCodeSignatureLC(self: *MachO) !void {2841fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3300 if (self.code_signature_cmd_index != null or self.code_signature == null) return;2842 if (self.base.options.output_mode != .Exe) return;
3301 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);2843 const seg = self.segments.items[self.text_segment_cmd_index.?];
3302 try self.load_commands.append(self.base.allocator, .{2844 const global = try self.getEntryPoint();
3303 .linkedit_data = .{2845 const sym = self.getSymbol(global);
3304 .cmd = .CODE_SIGNATURE,2846 try lc_writer.writeStruct(macho.entry_point_command{
3305 .cmdsize = @sizeOf(macho.linkedit_data_command),2847 .cmd = .MAIN,
3306 .dataoff = 0,2848 .cmdsize = @sizeOf(macho.entry_point_command),
3307 .datasize = 0,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,
3308 },2878 },
3309 });2879 });
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;
3311}2887}
33122888
3313fn setEntryPoint(self: *MachO) !void {2889fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3314 if (self.base.options.output_mode != .Exe) return;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;2910const RpathIterator = struct {
3317 const global = try self.getEntryPoint();2911 buffer: []const []const u8,
3318 const sym = self.getSymbol(global);2912 table: std.StringHashMap(void),
3319 const ec = &self.load_commands.items[self.main_cmd_index.?].main;2913 count: usize = 0,
3320 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);2914
3321 ec.stacksize = self.base.options.stack_size_override orelse 0;2915 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
3322 self.entry_addr = sym.n_value;2916 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
3323 self.load_commands_dirty = true;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 }
3324}3007}
33253008
3326pub fn deinit(self: *MachO) void {3009pub fn deinit(self: *MachO) void {
...@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {...@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {
3334 d_sym.deinit(gpa);3017 d_sym.deinit(gpa);
3335 }3018 }
33363019
3337 self.section_ordinals.deinit(gpa);
3338 self.tlv_ptr_entries.deinit(gpa);3020 self.tlv_ptr_entries.deinit(gpa);
3339 self.tlv_ptr_entries_free_list.deinit(gpa);3021 self.tlv_ptr_entries_free_list.deinit(gpa);
3340 self.tlv_ptr_entries_table.deinit(gpa);3022 self.tlv_ptr_entries_table.deinit(gpa);
...@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {...@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {
3371 self.dylibs_map.deinit(gpa);3053 self.dylibs_map.deinit(gpa);
3372 self.referenced_dylibs.deinit(gpa);3054 self.referenced_dylibs.deinit(gpa);
33733055
3374 for (self.load_commands.items) |*lc| {3056 self.segments.deinit(gpa);
3375 lc.deinit(gpa);3057
3058 for (self.sections.items(.free_list)) |*list| {
3059 list.deinit(gpa);
3376 }3060 }
3377 self.load_commands.deinit(gpa);3061 self.sections.deinit(gpa);
33783062
3379 for (self.managed_atoms.items) |atom| {3063 for (self.managed_atoms.items) |atom| {
3380 atom.deinit(gpa);3064 atom.deinit(gpa);
3381 gpa.destroy(atom);3065 gpa.destroy(atom);
3382 }3066 }
3383 self.managed_atoms.deinit(gpa);3067 self.managed_atoms.deinit(gpa);
3384 self.atoms.deinit(gpa);3068
3385 {
3386 var it = self.atom_free_lists.valueIterator();
3387 while (it.next()) |free_list| {
3388 free_list.deinit(gpa);
3389 }
3390 self.atom_free_lists.deinit(gpa);
3391 }
3392 if (self.base.options.module) |mod| {3069 if (self.base.options.module) |mod| {
3393 for (self.decls.keys()) |decl_index| {3070 for (self.decls.keys()) |decl_index| {
3394 const decl = mod.declPtr(decl_index);3071 const decl = mod.declPtr(decl_index);
...@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {...@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {
3408 }3085 }
34093086
3410 self.atom_by_index_table.deinit(gpa);3087 self.atom_by_index_table.deinit(gpa);
3411
3412 if (self.code_signature) |*csig| {
3413 csig.deinit(gpa);
3414 }
3415}3088}
34163089
3417pub fn closeFiles(self: MachO) void {3090pub fn closeFiles(self: MachO) void {
3418 for (self.objects.items) |object| {
3419 object.file.close();
3420 }
3421 for (self.archives.items) |archive| {3091 for (self.archives.items) |archive| {
3422 archive.file.close();3092 archive.file.close();
3423 }3093 }
3424 for (self.dylibs.items) |dylib| {
3425 dylib.file.close();
3426 }
3427 if (self.d_sym) |ds| {3094 if (self.d_sym) |ds| {
3428 ds.file.close();3095 ds.file.close();
3429 }3096 }
3430}3097}
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 {
3433 log.debug("freeAtom {*}", .{atom});3100 log.debug("freeAtom {*}", .{atom});
3434 if (!owns_atom) {3101 if (!owns_atom) {
3435 atom.deinit(self.base.allocator);3102 atom.deinit(self.base.allocator);
3436 }3103 }
34373104
3438 const free_list = self.atom_free_lists.getPtr(match).?;3105 const free_list = &self.sections.items(.free_list)[sect_id];
3439 var already_have_free_list_node = false;3106 var already_have_free_list_node = false;
3440 {3107 {
3441 var i: usize = 0;3108 var i: usize = 0;
...@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3452 }3119 }
3453 }3120 }
34543121
3455 if (self.atoms.getPtr(match)) |last_atom| {3122 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3456 if (last_atom.* == atom) {3123 if (maybe_last_atom.*) |last_atom| {
3124 if (last_atom == atom) {
3457 if (atom.prev) |prev| {3125 if (atom.prev) |prev| {
3458 // TODO shrink the section size here3126 // TODO shrink the section size here
3459 last_atom.* = prev;3127 maybe_last_atom.* = prev;
3460 } else {3128 } else {
3461 _ = self.atoms.fetchRemove(match);3129 maybe_last_atom.* = null;
3462 }3130 }
3463 }3131 }
3464 }3132 }
...@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3486 }3154 }
3487}3155}
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 {
3490 _ = self;3158 _ = self;
3491 _ = atom;3159 _ = atom;
3492 _ = new_block_size;3160 _ = new_block_size;
3493 _ = match;3161 _ = sect_id;
3494 // TODO check the new capacity, and if it crosses the size threshold into a big enough3162 // TODO check the new capacity, and if it crosses the size threshold into a big enough
3495 // capacity, insert a free list node for it.3163 // capacity, insert a free list node for it.
3496}3164}
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 {
3499 const sym = atom.getSymbol(self);3167 const sym = atom.getSymbol(self);
3500 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;3168 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3501 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);3169 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
3502 if (!need_realloc) return sym.n_value;3170 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);
3504}3172}
35053173
3506fn allocateSymbol(self: *MachO) !u32 {3174fn allocateSymbol(self: *MachO) !u32 {
...@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3671}3339}
36723340
3673pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {3341pub 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);
3675 defer code_buffer.deinit();3345 defer code_buffer.deinit();
36763346
3677 const gpa = self.base.allocator;
3678 const module = self.base.options.module.?;3347 const module = self.base.options.module.?;
3679 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);3348 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
3680 if (!gop.found_existing) {3349 if (!gop.found_existing) {
...@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3725 atom.code.clearRetainingCapacity();3394 atom.code.clearRetainingCapacity();
3726 try atom.code.appendSlice(gpa, code);3395 try atom.code.appendSlice(gpa, code);
37273396
3728 const match = try self.getMatchingSectionAtom(3397 const sect_id = try self.getOutputSectionAtom(
3729 atom,3398 atom,
3730 decl_name,3399 decl_name,
3731 typed_value.ty,3400 typed_value.ty,
3732 typed_value.val,3401 typed_value.val,
3733 required_alignment,3402 required_alignment,
3734 );3403 );
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
3737 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });3406 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
3738 log.debug(" (required alignment 0x{x})", .{required_alignment});3407 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
3742 const symbol = atom.getSymbolPtr(self);3411 const symbol = atom.getSymbolPtr(self);
3743 symbol.* = .{3412 symbol.* = .{
3744 .n_strx = name_str_index,3413 .n_strx = name_str_index,
3745 .n_type = macho.N_SECT,3414 .n_type = macho.N_SECT,
3746 .n_sect = self.getSectionOrdinal(match),3415 .n_sect = sect_id + 1,
3747 .n_desc = 0,3416 .n_desc = 0,
3748 .n_value = addr,3417 .n_value = addr,
3749 };3418 };
...@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {...@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
3894 }3563 }
3895}3564}
38963565
3897fn getMatchingSectionAtom(3566fn getOutputSectionAtom(
3898 self: *MachO,3567 self: *MachO,
3899 atom: *Atom,3568 atom: *Atom,
3900 name: []const u8,3569 name: []const u8,
3901 ty: Type,3570 ty: Type,
3902 val: Value,3571 val: Value,
3903 alignment: u32,3572 alignment: u32,
3904) !MatchingSection {3573) !u8 {
3905 const code = atom.code.items;3574 const code = atom.code.items;
3906 const mod = self.base.options.module.?;3575 const mod = self.base.options.module.?;
3907 const align_log_2 = math.log2(alignment);3576 const align_log_2 = math.log2(alignment);
3908 const zig_ty = ty.zigTypeTag();3577 const zig_ty = ty.zigTypeTag();
3909 const mode = self.base.options.optimize_mode;3578 const mode = self.base.options.optimize_mode;
3910 const match: MatchingSection = blk: {3579 const sect_id: u8 = blk: {
3911 // TODO finish and audit this function3580 // TODO finish and audit this function
3912 if (val.isUndefDeep()) {3581 if (val.isUndefDeep()) {
3913 if (mode == .ReleaseFast or mode == .ReleaseSmall) {3582 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3914 break :blk MatchingSection{3583 break :blk (try self.getOutputSection(.{
3915 .seg = self.data_segment_cmd_index.?,3584 .segname = makeStaticString("__DATA"),
3916 .sect = self.bss_section_index.?,3585 .sectname = makeStaticString("__bss"),
3917 };3586 .size = code.len,
3587 .@"align" = align_log_2,
3588 })).?;
3918 } else {3589 } else {
3919 break :blk MatchingSection{3590 break :blk self.data_section_index.?;
3920 .seg = self.data_segment_cmd_index.?,
3921 .sect = self.data_section_index.?,
3922 };
3923 }3591 }
3924 }3592 }
39253593
3926 if (val.castTag(.variable)) |_| {3594 if (val.castTag(.variable)) |_| {
3927 break :blk MatchingSection{3595 break :blk self.data_section_index.?;
3928 .seg = self.data_segment_cmd_index.?,
3929 .sect = self.data_section_index.?,
3930 };
3931 }3596 }
39323597
3933 if (needsPointerRebase(ty, val, mod)) {3598 if (needsPointerRebase(ty, val, mod)) {
3934 break :blk (try self.getMatchingSection(.{3599 break :blk (try self.getOutputSection(.{
3935 .segname = makeStaticString("__DATA_CONST"),3600 .segname = makeStaticString("__DATA_CONST"),
3936 .sectname = makeStaticString("__const"),3601 .sectname = makeStaticString("__const"),
3937 .size = code.len,3602 .size = code.len,
...@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(...@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(
39413606
3942 switch (zig_ty) {3607 switch (zig_ty) {
3943 .Fn => {3608 .Fn => {
3944 break :blk MatchingSection{3609 break :blk self.text_section_index.?;
3945 .seg = self.text_segment_cmd_index.?,
3946 .sect = self.text_section_index.?,
3947 };
3948 },3610 },
3949 .Array => {3611 .Array => {
3950 if (val.tag() == .bytes) {3612 if (val.tag() == .bytes) {
...@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(...@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(
3953 .const_slice_u8_sentinel_0,3615 .const_slice_u8_sentinel_0,
3954 .manyptr_const_u8_sentinel_0,3616 .manyptr_const_u8_sentinel_0,
3955 => {3617 => {
3956 break :blk (try self.getMatchingSection(.{3618 break :blk (try self.getOutputSection(.{
3957 .segname = makeStaticString("__TEXT"),3619 .segname = makeStaticString("__TEXT"),
3958 .sectname = makeStaticString("__cstring"),3620 .sectname = makeStaticString("__cstring"),
3959 .flags = macho.S_CSTRING_LITERALS,3621 .flags = macho.S_CSTRING_LITERALS,
...@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(...@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(
3967 },3629 },
3968 else => {},3630 else => {},
3969 }3631 }
3970 break :blk (try self.getMatchingSection(.{3632 break :blk (try self.getOutputSection(.{
3971 .segname = makeStaticString("__TEXT"),3633 .segname = makeStaticString("__TEXT"),
3972 .sectname = makeStaticString("__const"),3634 .sectname = makeStaticString("__const"),
3973 .size = code.len,3635 .size = code.len,
3974 .@"align" = align_log_2,3636 .@"align" = align_log_2,
3975 })).?;3637 })).?;
3976 };3638 };
3977 const sect = self.getSection(match);3639 const header = self.sections.items(.header)[sect_id];
3978 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{3640 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
3979 name,3641 name,
3980 sect.segName(),3642 header.segName(),
3981 sect.sectName(),3643 header.sectName(),
3982 match.seg,3644 sect_id,
3983 match.sect,
3984 });3645 });
3985 return match;3646 return sect_id;
3986}3647}
39873648
3988fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {3649fn 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...@@ -3996,7 +3657,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
39963657
3997 const decl_ptr = self.decls.getPtr(decl_index).?;3658 const decl_ptr = self.decls.getPtr(decl_index).?;
3998 if (decl_ptr.* == null) {3659 if (decl_ptr.* == null) {
3999 decl_ptr.* = try self.getMatchingSectionAtom(3660 decl_ptr.* = try self.getOutputSectionAtom(
4000 &decl.link.macho,3661 &decl.link.macho,
4001 sym_name,3662 sym_name,
4002 decl.ty,3663 decl.ty,
...@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64...@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
4045 symbol.* = .{3706 symbol.* = .{
4046 .n_strx = name_str_index,3707 .n_strx = name_str_index,
4047 .n_type = macho.N_SECT,3708 .n_type = macho.N_SECT,
4048 .n_sect = self.getSectionOrdinal(match),3709 .n_sect = match + 1,
4049 .n_desc = 0,3710 .n_desc = 0,
4050 .n_value = addr,3711 .n_value = addr,
4051 };3712 };
...@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(...@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(
4134 sym.* = .{3795 sym.* = .{
4135 .n_strx = try self.strtab.insert(gpa, exp_name),3796 .n_strx = try self.strtab.insert(gpa, exp_name),
4136 .n_type = macho.N_SECT | macho.N_EXT,3797 .n_type = macho.N_SECT | macho.N_EXT,
4137 .n_sect = self.getSectionOrdinal(.{3798 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?
4138 .seg = self.text_segment_cmd_index.?,
4139 .sect = self.text_section_index.?, // TODO what if we export a variable?
4140 }),
4141 .n_desc = 0,3799 .n_desc = 0,
4142 .n_value = decl_sym.n_value,3800 .n_value = decl_sym.n_value,
4143 };3801 };
...@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
4208fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {3866fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
4209 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;3867 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
4210 for (unnamed_consts.items) |atom| {3868 for (unnamed_consts.items) |atom| {
4211 self.freeAtom(atom, .{3869 // TODO
4212 .seg = self.text_segment_cmd_index.?,3870 // const sect_id = atom.getSymbol(self).n_sect;
4213 .sect = self.text_const_section_index.?,3871 const sect_id = self.getSectionByName("__TEXT", "__const").?;
4214 }, true);3872 self.freeAtom(atom, sect_id, true);
4215 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};3873 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
4216 self.locals.items[atom.sym_index].n_type = 0;3874 self.locals.items[atom.sym_index].n_type = 0;
4217 _ = self.atom_by_index_table.remove(atom.sym_index);3875 _ = 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...@@ -4294,6 +3952,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
4294}3952}
42953953
4296fn populateMissingMetadata(self: *MachO) !void {3954fn populateMissingMetadata(self: *MachO) !void {
3955 const gpa = self.base.allocator;
4297 const cpu_arch = self.base.options.target.cpu.arch;3956 const cpu_arch = self.base.options.target.cpu.arch;
4298 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;3957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
4299 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);3958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
...@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4305 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});3964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
4306 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});3965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
4307 }3966 }
4308 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);3967 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
4309 try self.load_commands.append(self.base.allocator, .{3968 try self.segments.append(gpa, .{
4310 .segment = .{3969 .segname = makeStaticString("__PAGEZERO"),
4311 .inner = .{3970 .vmsize = aligned_pagezero_vmsize,
4312 .segname = makeStaticString("__PAGEZERO"),3971 .cmdsize = @sizeOf(macho.segment_command_64),
4313 .vmsize = aligned_pagezero_vmsize,
4314 .cmdsize = @sizeOf(macho.segment_command_64),
4315 },
4316 },
4317 });3972 });
4318 self.load_commands_dirty = true;
4319 }3973 }
43203974
4321 if (self.text_segment_cmd_index == null) {3975 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);
4323 const needed_size = if (self.mode == .incremental) blk: {3977 const needed_size = if (self.mode == .incremental) blk: {
4324 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);3978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
4325 const program_code_size_hint = self.base.options.program_code_size_hint;3979 const program_code_size_hint = self.base.options.program_code_size_hint;
...@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {
4329 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });3983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
4330 break :blk needed_size;3984 break :blk needed_size;
4331 } else 0;3985 } else 0;
4332 try self.load_commands.append(self.base.allocator, .{3986 try self.segments.append(gpa, .{
4333 .segment = .{3987 .segname = makeStaticString("__TEXT"),
4334 .inner = .{3988 .vmaddr = aligned_pagezero_vmsize,
4335 .segname = makeStaticString("__TEXT"),3989 .vmsize = needed_size,
4336 .vmaddr = aligned_pagezero_vmsize,3990 .filesize = needed_size,
4337 .vmsize = needed_size,3991 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
4338 .filesize = needed_size,3992 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4339 .maxprot = macho.PROT.READ | macho.PROT.EXEC,3993 .cmdsize = @sizeOf(macho.segment_command_64),
4340 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4341 .cmdsize = @sizeOf(macho.segment_command_64),
4342 },
4343 },
4344 });3994 });
4345 self.load_commands_dirty = true;
4346 }3995 }
43473996
4348 if (self.text_section_index == null) {3997 if (self.text_section_index == null) {
...@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4353 };4002 };
4354 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;4003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
4355 self.text_section_index = try self.initSection(4004 self.text_section_index = try self.initSection(
4356 self.text_segment_cmd_index.?,4005 "__TEXT",
4357 "__text",4006 "__text",
4358 needed_size,4007 needed_size,
4359 alignment,4008 alignment,
...@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4376 };4025 };
4377 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;4026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
4378 self.stubs_section_index = try self.initSection(4027 self.stubs_section_index = try self.initSection(
4379 self.text_segment_cmd_index.?,4028 "__TEXT",
4380 "__stubs",4029 "__stubs",
4381 needed_size,4030 needed_size,
4382 alignment,4031 alignment,
...@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4408 else4057 else
4409 0;4058 0;
4410 self.stub_helper_section_index = try self.initSection(4059 self.stub_helper_section_index = try self.initSection(
4411 self.text_segment_cmd_index.?,4060 "__TEXT",
4412 "__stub_helper",4061 "__stub_helper",
4413 needed_size,4062 needed_size,
4414 alignment,4063 alignment,
...@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4419 }4068 }
44204069
4421 if (self.data_const_segment_cmd_index == null) {4070 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);
4423 var vmaddr: u64 = 0;4072 var vmaddr: u64 = 0;
4424 var fileoff: u64 = 0;4073 var fileoff: u64 = 0;
4425 var needed_size: u64 = 0;4074 var needed_size: u64 = 0;
...@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4434 fileoff + needed_size,4083 fileoff + needed_size,
4435 });4084 });
4436 }4085 }
4437 try self.load_commands.append(self.base.allocator, .{4086 try self.segments.append(gpa, .{
4438 .segment = .{4087 .segname = makeStaticString("__DATA_CONST"),
4439 .inner = .{4088 .vmaddr = vmaddr,
4440 .segname = makeStaticString("__DATA_CONST"),4089 .vmsize = needed_size,
4441 .vmaddr = vmaddr,4090 .fileoff = fileoff,
4442 .vmsize = needed_size,4091 .filesize = needed_size,
4443 .fileoff = fileoff,4092 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4444 .filesize = needed_size,4093 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4445 .maxprot = macho.PROT.READ | macho.PROT.WRITE,4094 .cmdsize = @sizeOf(macho.segment_command_64),
4446 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4447 .cmdsize = @sizeOf(macho.segment_command_64),
4448 },
4449 },
4450 });4095 });
4451 self.load_commands_dirty = true;
4452 }4096 }
44534097
4454 if (self.got_section_index == null) {4098 if (self.got_section_index == null) {
...@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4458 0;4102 0;
4459 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4460 self.got_section_index = try self.initSection(4104 self.got_section_index = try self.initSection(
4461 self.data_const_segment_cmd_index.?,4105 "__DATA_CONST",
4462 "__got",4106 "__got",
4463 needed_size,4107 needed_size,
4464 alignment,4108 alignment,
...@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4469 }4113 }
44704114
4471 if (self.data_segment_cmd_index == null) {4115 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);
4473 var vmaddr: u64 = 0;4117 var vmaddr: u64 = 0;
4474 var fileoff: u64 = 0;4118 var fileoff: u64 = 0;
4475 var needed_size: u64 = 0;4119 var needed_size: u64 = 0;
...@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4484 fileoff + needed_size,4128 fileoff + needed_size,
4485 });4129 });
4486 }4130 }
4487 try self.load_commands.append(self.base.allocator, .{4131 try self.segments.append(gpa, .{
4488 .segment = .{4132 .segname = makeStaticString("__DATA"),
4489 .inner = .{4133 .vmaddr = vmaddr,
4490 .segname = makeStaticString("__DATA"),4134 .vmsize = needed_size,
4491 .vmaddr = vmaddr,4135 .fileoff = fileoff,
4492 .vmsize = needed_size,4136 .filesize = needed_size,
4493 .fileoff = fileoff,4137 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4494 .filesize = needed_size,4138 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4495 .maxprot = macho.PROT.READ | macho.PROT.WRITE,4139 .cmdsize = @sizeOf(macho.segment_command_64),
4496 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4497 .cmdsize = @sizeOf(macho.segment_command_64),
4498 },
4499 },
4500 });4140 });
4501 self.load_commands_dirty = true;
4502 }4141 }
45034142
4504 if (self.la_symbol_ptr_section_index == null) {4143 if (self.la_symbol_ptr_section_index == null) {
...@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4508 0;4147 0;
4509 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4510 self.la_symbol_ptr_section_index = try self.initSection(4149 self.la_symbol_ptr_section_index = try self.initSection(
4511 self.data_segment_cmd_index.?,4150 "__DATA",
4512 "__la_symbol_ptr",4151 "__la_symbol_ptr",
4513 needed_size,4152 needed_size,
4514 alignment,4153 alignment,
...@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4525 0;4164 0;
4526 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4527 self.data_section_index = try self.initSection(4166 self.data_section_index = try self.initSection(
4528 self.data_segment_cmd_index.?,4167 "__DATA",
4529 "__data",4168 "__data",
4530 needed_size,4169 needed_size,
4531 alignment,4170 alignment,
...@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {
4533 );4172 );
4534 }4173 }
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
4604 if (self.linkedit_segment_cmd_index == null) {4175 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);
4606 var vmaddr: u64 = 0;4177 var vmaddr: u64 = 0;
4607 var fileoff: u64 = 0;4178 var fileoff: u64 = 0;
4608 if (self.mode == .incremental) {4179 if (self.mode == .incremental) {
...@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {
4611 fileoff = base.fileoff;4182 fileoff = base.fileoff;
4612 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});4183 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
4613 }4184 }
4614 try self.load_commands.append(self.base.allocator, .{4185 try self.segments.append(gpa, .{
4615 .segment = .{4186 .segname = makeStaticString("__LINKEDIT"),
4616 .inner = .{4187 .vmaddr = vmaddr,
4617 .segname = makeStaticString("__LINKEDIT"),4188 .fileoff = fileoff,
4618 .vmaddr = vmaddr,4189 .maxprot = macho.PROT.READ,
4619 .fileoff = fileoff,4190 .initprot = macho.PROT.READ,
4620 .maxprot = macho.PROT.READ,4191 .cmdsize = @sizeOf(macho.segment_command_64),
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 },
4646 });4192 });
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;
4690 }4193 }
4194}
46914195
4692 if (self.dylinker_cmd_index == null) {4196inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
4693 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);4197 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
4694 const cmdsize = @intCast(u32, mem.alignForwardGeneric(4198 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
4695 u64,4199}
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 }
47224200
4723 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {4201fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4724 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);4202 const gpa = self.base.allocator;
4725 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;4203 var sizeofcmds: u64 = 0;
4726 const current_version = self.base.options.version orelse4204 for (self.segments.items) |seg| {
4727 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4205 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
4728 const compat_version = self.base.options.compatibility_version orelse4206 }
4729 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4207
4730 var dylib_cmd = try macho.createLoadDylibCommand(4208 // LC_DYLD_INFO_ONLY
4731 self.base.allocator,4209 sizeofcmds += @sizeOf(macho.dyld_info_command);
4732 .ID_DYLIB,4210 // LC_FUNCTION_STARTS
4733 install_name,4211 if (self.text_section_index != null) {
4734 2,4212 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4735 current_version.major << 16 | current_version.minor << 8 | current_version.patch,4213 }
4736 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,4214 // LC_DATA_IN_CODE
4737 );4215 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4738 errdefer dylib_cmd.deinit(self.base.allocator);4216 // LC_SYMTAB
4739 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });4217 sizeofcmds += @sizeOf(macho.symtab_command);
4740 self.load_commands_dirty = true;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 };
4741 }4240 }
47424241 // LC_RPATH
4743 if (self.source_version_cmd_index == null) {4242 {
4744 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);4243 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
4745 try self.load_commands.append(self.base.allocator, .{4244 defer it.deinit();
4746 .source_version = .{4245 while (try it.next()) |rpath| {
4747 .cmdsize = @sizeOf(macho.source_version_command),4246 sizeofcmds += calcInstallNameLen(
4748 .version = 0x0,4247 @sizeOf(macho.rpath_command),
4749 },4248 rpath,
4750 });4249 assume_max_path_len,
4751 self.load_commands_dirty = true;4250 );
4251 }
4752 }4252 }
47534253 // LC_SOURCE_VERSION
4754 if (self.build_version_cmd_index == null) {4254 sizeofcmds += @sizeOf(macho.source_version_command);
4755 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);4255 // LC_BUILD_VERSION
4756 const cmdsize = @intCast(u32, mem.alignForwardGeneric(4256 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
4757 u64,4257 // LC_UUID
4758 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),4258 sizeofcmds += @sizeOf(macho.uuid_command);
4759 @sizeOf(u64),4259 // LC_LOAD_DYLIB
4760 ));4260 for (self.referenced_dylibs.keys()) |id| {
4761 const platform_version = blk: {4261 const dylib = self.dylibs.items[id];
4762 const ver = self.base.options.target.os.version_range.semver.min;4262 const dylib_id = dylib.id orelse unreachable;
4763 const platform_version = ver.major << 16 | ver.minor << 8;4263 sizeofcmds += calcInstallNameLen(
4764 break :blk platform_version;4264 @sizeOf(macho.dylib_command),
4765 };4265 dylib_id.name,
4766 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {4266 assume_max_path_len,
4767 const ver = sdk.version;4267 );
4768 const sdk_version = ver.major << 16 | ver.minor << 8;4268 }
4769 break :blk sdk_version;4269 // LC_CODE_SIGNATURE
4770 } else platform_version;4270 {
4771 const is_simulator_abi = self.base.options.target.abi == .simulator;4271 const target = self.base.options.target;
4772 var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{4272 const requires_codesig = blk: {
4773 .cmdsize = cmdsize,4273 if (self.base.options.entitlements) |_| break :blk true;
4774 .platform = switch (self.base.options.target.os.tag) {4274 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
4775 .macos => .MACOS,4275 break :blk true;
4776 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,4276 break :blk false;
4777 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
4778 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
4779 else => unreachable,
4780 },
4781 .minos = platform_version,
4782 .sdk = sdk_version,
4783 .ntools = 1,
4784 });
4785 const ld_ver = macho.build_tool_version{
4786 .tool = .LD,
4787 .version = 0x0,
4788 };
4789 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4790 mem.set(u8, cmd.data, 0);
4791 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4792 try self.load_commands.append(self.base.allocator, .{ .build_version = cmd });
4793 self.load_commands_dirty = true;
4794 }
4795
4796 if (self.uuid_cmd_index == null) {
4797 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
4798 var uuid_cmd: macho.uuid_command = .{
4799 .cmdsize = @sizeOf(macho.uuid_command),
4800 .uuid = undefined,
4801 };4277 };
4802 std.crypto.random.bytes(&uuid_cmd.uuid);4278 if (requires_codesig) {
4803 try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });4279 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4804 self.load_commands_dirty = true;4280 }
4805 }
4806
4807 if (self.function_starts_cmd_index == null) {
4808 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
4809 try self.load_commands.append(self.base.allocator, .{
4810 .linkedit_data = .{
4811 .cmd = .FUNCTION_STARTS,
4812 .cmdsize = @sizeOf(macho.linkedit_data_command),
4813 .dataoff = 0,
4814 .datasize = 0,
4815 },
4816 });
4817 self.load_commands_dirty = true;
4818 }4281 }
48194282
4820 if (self.data_in_code_cmd_index == null) {4283 return @intCast(u32, sizeofcmds);
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 }
4832}4284}
48334285
4834fn calcMinHeaderpad(self: *MachO) u64 {4286fn calcMinHeaderPad(self: *MachO) !u64 {
4835 var sizeofcmds: u32 = 0;4287 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 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);
4842 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});4288 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
48434289
4844 if (self.base.options.headerpad_max_install_names) {4290 if (self.base.options.headerpad_max_install_names) {
4845 var min_headerpad_size: u32 = 0;4291 var min_headerpad_size: u32 = try self.calcLCsSize(true);
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 };
4857 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{4292 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
4858 min_headerpad_size + @sizeOf(macho.mach_header_64),4293 min_headerpad_size + @sizeOf(macho.mach_header_64),
4859 });4294 });
...@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {...@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {
4868fn allocateSegments(self: *MachO) !void {4303fn allocateSegments(self: *MachO) !void {
4869 try self.allocateSegment(self.text_segment_cmd_index, &.{4304 try self.allocateSegment(self.text_segment_cmd_index, &.{
4870 self.pagezero_segment_cmd_index,4305 self.pagezero_segment_cmd_index,
4871 }, self.calcMinHeaderpad());4306 }, try self.calcMinHeaderPad());
48724307
4873 if (self.text_segment_cmd_index) |index| blk: {4308 if (self.text_segment_cmd_index) |index| blk: {
4874 const seg = &self.load_commands.items[index].segment;4309 const indexes = self.getSectionIndexes(index);
4875 if (seg.sections.items.len == 0) break :blk;4310 if (indexes.start == indexes.end) break :blk;
4311 const seg = self.segments.items[index];
48764312
4877 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.4313 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4878 var min_alignment: u32 = 0;4314 var min_alignment: u32 = 0;
4879 for (seg.sections.items) |sect| {4315 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4880 const alignment = try math.powi(u32, 2, sect.@"align");4316 const alignment = try math.powi(u32, 2, header.@"align");
4881 min_alignment = math.max(min_alignment, alignment);4317 min_alignment = math.max(min_alignment, alignment);
4882 }4318 }
48834319
4884 assert(min_alignment > 0);4320 assert(min_alignment > 0);
4885 const last_sect_idx = seg.sections.items.len - 1;4321 const last_header = self.sections.items(.header)[indexes.end - 1];
4886 const last_sect = seg.sections.items[last_sect_idx];
4887 const shift: u32 = shift: {4322 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;
4889 const factor = @divTrunc(diff, min_alignment);4324 const factor = @divTrunc(diff, min_alignment);
4890 break :shift @intCast(u32, factor * min_alignment);4325 break :shift @intCast(u32, factor * min_alignment);
4891 };4326 };
48924327
4893 if (shift > 0) {4328 if (shift > 0) {
4894 for (seg.sections.items) |*sect| {4329 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4895 sect.offset += shift;4330 header.offset += shift;
4896 sect.addr += shift;4331 header.addr += shift;
4897 }4332 }
4898 }4333 }
4899 }4334 }
...@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {...@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {
4917 }, 0);4352 }, 0);
4918}4353}
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 {
4921 const index = maybe_index orelse return;4356 const index = maybe_index orelse return;
4922 const seg = &self.load_commands.items[index].segment;4357 const seg = &self.segments.items[index];
49234358
4924 const base = self.getSegmentAllocBase(indices);4359 const base = self.getSegmentAllocBase(indices);
4925 seg.inner.vmaddr = base.vmaddr;4360 seg.vmaddr = base.vmaddr;
4926 seg.inner.fileoff = base.fileoff;4361 seg.fileoff = base.fileoff;
4927 seg.inner.filesize = init_size;4362 seg.filesize = init_size;
4928 seg.inner.vmsize = init_size;4363 seg.vmsize = init_size;
49294364
4930 // Allocate the sections according to their alignment at the beginning of the segment.4365 // Allocate the sections according to their alignment at the beginning of the segment.
4366 const indexes = self.getSectionIndexes(index);
4931 var start = init_size;4367 var start = init_size;
4932 for (seg.sections.items) |*sect| {4368 const slice = self.sections.slice();
4933 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;4369 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4934 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4370 const alignment = try math.powi(u32, 2, header.@"align");
4935 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4936 const alignment = try math.powi(u32, 2, sect.@"align");
4937 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);4371 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
49384372
4939 // TODO handle zerofill sections in stage24373 header.offset = if (header.isZerofill())
4940 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4941 04374 0
4942 else4375 else
4943 @intCast(u32, seg.inner.fileoff + start_aligned);4376 @intCast(u32, seg.fileoff + start_aligned);
4944 sect.addr = seg.inner.vmaddr + 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))) {4381 if (!header.isZerofill()) {
4949 seg.inner.filesize = start;4382 seg.filesize = start;
4950 }4383 }
4951 seg.inner.vmsize = start;4384 seg.vmsize = start;
4952 }4385 }
49534386
4954 seg.inner.filesize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);4387 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4955 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.vmsize, self.page_size);4388 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
4956}4389}
49574390
4958const InitSectionOpts = struct {4391const InitSectionOpts = struct {
...@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {...@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {
49634396
4964fn initSection(4397fn initSection(
4965 self: *MachO,4398 self: *MachO,
4966 segment_id: u16,4399 segname: []const u8,
4967 sectname: []const u8,4400 sectname: []const u8,
4968 size: u64,4401 size: u64,
4969 alignment: u32,4402 alignment: u32,
4970 opts: InitSectionOpts,4403 opts: InitSectionOpts,
4971) !u16 {4404) !u8 {
4972 const seg = &self.load_commands.items[segment_id].segment;4405 const segment_id = self.getSegmentByName(segname).?;
4973 var sect = macho.section_64{4406 const seg = &self.segments.items[segment_id];
4407 const index = try self.insertSection(segment_id, .{
4974 .sectname = makeStaticString(sectname),4408 .sectname = makeStaticString(sectname),
4975 .segname = seg.inner.segname,4409 .segname = seg.segname,
4976 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
4977 .@"align" = alignment,
4978 .flags = opts.flags,4410 .flags = opts.flags,
4979 .reserved1 = opts.reserved1,4411 .reserved1 = opts.reserved1,
4980 .reserved2 = opts.reserved2,4412 .reserved2 = opts.reserved2,
4981 };4413 });
4414 seg.cmdsize += @sizeOf(macho.section_64);
4415 seg.nsects += 1;
49824416
4983 if (self.mode == .incremental) {4417 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;
4984 const alignment_pow_2 = try math.powi(u32, 2, alignment);4429 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4985 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)4430 // TODO better prealloc for __text section
4986 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)4431 // const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4987 else4432 const padding: u64 = if (index == 0) 0x1000 else 0;
4988 null;4433 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
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 });
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;4440 // TODO Will this break if we are inserting section that is not the last section
5000 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4441 // in a segment?
5001 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;4442 const max_size = self.allocatedSize(segment_id, off);
50024443
5003 // TODO handle zerofill in stage24444 if (size > max_size) {
5004 if (!(is_zerofill and (use_stage1 or use_llvm))) {4445 try self.growSection(index, @intCast(u32, size));
5005 sect.offset = @intCast(u32, off);
5006 }4446 }
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,
5007 }4475 }
4476}
50084477
5009 const index = @intCast(u16, seg.sections.items.len);4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
5010 try seg.sections.append(self.base.allocator, sect);4479 const precedence = getSectionPrecedence(header);
5011 seg.inner.cmdsize += @sizeOf(macho.section_64);4480 const indexes = self.getSectionIndexes(segment_index);
5012 seg.inner.nsects += 1;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{4507fn updateSectionOrdinals(self: *MachO, start: u8) void {
5015 .seg = segment_id,4508 const tracy = trace(@src());
5016 .sect = index,4509 defer tracy.end();
5017 };
5018 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
5019 try self.atom_free_lists.putNoClobber(self.base.allocator, match, .{});
50204510
5021 self.load_commands_dirty = true;4511 const slice = self.sections.slice();
5022 self.sections_order_dirty = true;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 }
5025}4532}
50264533
5027fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 {4534fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
5028 const seg = self.load_commands.items[segment_id].segment;4535 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
5029 if (seg.sections.items.len == 0) {4536
5030 return if (start) |v| v else seg.inner.fileoff;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;
5031 }4552 }
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);
5035}4553}
50364554
5037fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {4555fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
5038 const seg = &self.load_commands.items[seg_id].segment;4556 const segment = &self.segments.items[segment_index];
5039 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);4557 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
5040 assert(new_seg_size > seg.inner.filesize);4558 assert(new_segment_size > segment.filesize);
5041 const offset_amt = new_seg_size - seg.inner.filesize;4559 const offset_amt = new_segment_size - segment.filesize;
5042 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{4560 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
5043 seg.inner.segname,4561 segment.segname,
5044 seg.inner.filesize,4562 segment.filesize,
5045 new_seg_size,4563 new_segment_size,
5046 });4564 });
5047 seg.inner.filesize = new_seg_size;4565 segment.filesize = new_segment_size;
5048 seg.inner.vmsize = new_seg_size;4566 segment.vmsize = new_segment_size;
50494567
5050 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4568 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5051 seg.inner.fileoff,4569 segment.fileoff,
5052 seg.inner.fileoff + seg.inner.filesize,4570 segment.fileoff + segment.filesize,
5053 seg.inner.vmaddr,4571 segment.vmaddr,
5054 seg.inner.vmaddr + seg.inner.vmsize,4572 segment.vmaddr + segment.vmsize,
5055 });4573 });
50564574
5057 var next: usize = seg_id + 1;4575 var next: u8 = segment_index + 1;
5058 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {4576 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
5061 try MachO.copyRangeAllOverlappingAlloc(4579 try MachO.copyRangeAllOverlappingAlloc(
5062 self.base.allocator,4580 self.base.allocator,
5063 self.base.file.?,4581 self.base.file.?,
5064 next_seg.inner.fileoff,4582 next_segment.fileoff,
5065 next_seg.inner.fileoff + offset_amt,4583 next_segment.fileoff + offset_amt,
5066 math.cast(usize, next_seg.inner.filesize) orelse return error.Overflow,4584 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
5067 );4585 );
50684586
5069 next_seg.inner.fileoff += offset_amt;4587 next_segment.fileoff += offset_amt;
5070 next_seg.inner.vmaddr += offset_amt;4588 next_segment.vmaddr += offset_amt;
50714589
5072 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4590 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,4591 next_segment.segname,
5074 next_seg.inner.fileoff,4592 next_segment.fileoff,
5075 next_seg.inner.fileoff + next_seg.inner.filesize,4593 next_segment.fileoff + next_segment.filesize,
5076 next_seg.inner.vmaddr,4594 next_segment.vmaddr,
5077 next_seg.inner.vmaddr + next_seg.inner.vmsize,4595 next_segment.vmaddr + next_segment.vmsize,
5078 });4596 });
50794597
5080 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {4598 const indexes = self.getSectionIndexes(next);
5081 moved_sect.offset += @intCast(u32, offset_amt);4599 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
5082 moved_sect.addr += offset_amt;4600 header.offset += @intCast(u32, offset_amt);
50834601 header.addr += offset_amt;
5084 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4602
5085 moved_sect.segName(),4603 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5086 moved_sect.sectName(),4604 header.segName(),
5087 moved_sect.offset,4605 header.sectName(),
5088 moved_sect.offset + moved_sect.size,4606 header.offset,
5089 moved_sect.addr,4607 header.offset + header.size,
5090 moved_sect.addr + moved_sect.size,4608 header.addr,
4609 header.addr + header.size,
5091 });4610 });
50924611
5093 try self.shiftLocalsByOffset(.{4612 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
5094 .seg = @intCast(u16, next),
5095 .sect = @intCast(u16, moved_sect_id),
5096 }, @intCast(i64, offset_amt));
5097 }4613 }
5098 }4614 }
5099}4615}
51004616
5101fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
5102 const tracy = trace(@src());4618 const tracy = trace(@src());
5103 defer tracy.end();4619 defer tracy.end();
51044620
5105 const seg = &self.load_commands.items[match.seg].segment;4621 const section = self.sections.get(sect_id);
5106 const sect = &seg.sections.items[match.sect];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");4626 const alignment = try math.powi(u32, 2, header.@"align");
5109 const max_size = self.allocatedSize(match.seg, sect.offset);4627 const max_size = self.allocatedSize(segment_index, header.offset);
5110 const ideal_size = padToIdeal(new_size);4628 const ideal_size = padToIdeal(new_size);
5111 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);4629 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
51124630
5113 if (needed_size > max_size) blk: {4631 if (needed_size > max_size) blk: {
5114 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });4632 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) {
5117 // Last section, just grow segments4636 // 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);
5119 break :blk;4638 break :blk;
5120 }4639 }
51214640
5122 // Need to move all sections below in file and address spaces.4641 // Need to move all sections below in file and address spaces.
5123 const offset_amt = offset: {4642 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);
5125 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);4644 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
5126 };4645 };
51274646
5128 // Before we commit to this, check if the segment needs to grow too.4647 // Before we commit to this, check if the segment needs to grow too.
5129 // We assume that each section header is growing linearly with the increasing4648 // We assume that each section header is growing linearly with the increasing
5130 // file offset / virtual memory address space.4649 // file offset / virtual memory address space.
5131 const last_sect = seg.sections.items[seg.sections.items.len - 1];4650 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
5132 const last_sect_off = last_sect.offset + last_sect.size;4651 const last_sect_off = last_sect_header.offset + last_sect_header.size;
5133 const seg_off = seg.inner.fileoff + seg.inner.filesize;4652 const seg_off = segment.fileoff + segment.filesize;
51344653
5135 if (last_sect_off + offset_amt > seg_off) {4654 if (last_sect_off + offset_amt > seg_off) {
5136 // Need to grow segment first.4655 // Need to grow segment first.
5137 const spill_size = (last_sect_off + offset_amt) - seg_off;4656 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);
5139 }4658 }
51404659
5141 // We have enough space to expand within the segment, so move all sections by4660 // We have enough space to expand within the segment, so move all sections by
5142 // the required amount and update their header offsets.4661 // 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];
5144 const total_size = last_sect_off - next_sect.offset;4663 const total_size = last_sect_off - next_sect.offset;
51454664
5146 try MachO.copyRangeAllOverlappingAlloc(4665 try MachO.copyRangeAllOverlappingAlloc(
...@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
5151 math.cast(usize, total_size) orelse return error.Overflow,4670 math.cast(usize, total_size) orelse return error.Overflow,
5152 );4671 );
51534672
5154 var next = match.sect + 1;4673 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
5155 while (next < seg.sections.items.len) : (next += 1) {
5156 const moved_sect = &seg.sections.items[next];
5157 moved_sect.offset += @intCast(u32, offset_amt);4674 moved_sect.offset += @intCast(u32, offset_amt);
5158 moved_sect.addr += offset_amt;4675 moved_sect.addr += offset_amt;
51594676
...@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
5166 moved_sect.addr + moved_sect.size,4683 moved_sect.addr + moved_sect.size,
5167 });4684 });
51684685
5169 try self.shiftLocalsByOffset(.{4686 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
5170 .seg = match.seg,
5171 .sect = next,
5172 }, @intCast(i64, offset_amt));
5173 }4687 }
5174 }4688 }
5175}4689}
51764690
5177fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {4691fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
5178 const seg = self.load_commands.items[segment_id].segment;4692 const segment = self.segments.items[segment_id];
5179 assert(start >= seg.inner.fileoff);4693 const indexes = self.getSectionIndexes(segment_id);
5180 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;4694 assert(start >= segment.fileoff);
4695 var min_pos: u64 = segment.fileoff + segment.filesize;
5181 if (start > min_pos) return 0;4696 if (start > min_pos) return 0;
5182 for (seg.sections.items) |section| {4697 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
5183 if (section.offset <= start) continue;4698 if (header.offset <= start) continue;
5184 if (section.offset < min_pos) min_pos = section.offset;4699 if (header.offset < min_pos) min_pos = header.offset;
5185 }4700 }
5186 return min_pos - start;4701 return min_pos - start;
5187}4702}
51884703
5189fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {4704fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
5190 const seg = self.load_commands.items[segment_id].segment;
5191 var max_alignment: u32 = 1;4705 var max_alignment: u32 = 1;
5192 var next = start_sect_id;4706 const slice = self.sections.slice();
5193 while (next < seg.sections.items.len) : (next += 1) {4707 for (slice.items(.header)[start..end]) |header| {
5194 const sect = seg.sections.items[next];4708 const alignment = try math.powi(u32, 2, header.@"align");
5195 const alignment = try math.powi(u32, 2, sect.@"align");
5196 max_alignment = math.max(max_alignment, alignment);4709 max_alignment = math.max(max_alignment, alignment);
5197 }4710 }
5198 return max_alignment;4711 return max_alignment;
5199}4712}
52004713
5201fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {4714fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
5202 const sym = atom.getSymbolPtr(self);4715 const sym = atom.getSymbolPtr(self);
5203 if (self.mode == .incremental) {4716 if (self.mode == .incremental) {
5204 const size = atom.size;4717 const size = atom.size;
5205 const alignment = try math.powi(u32, 2, atom.alignment);4718 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);
5207 const sym_name = atom.getName(self);4720 const sym_name = atom.getName(self);
5208 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });4721 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
5209 sym.n_value = vaddr;4722 sym.n_value = vaddr;
5210 } else try self.addAtomToSection(atom, match);4723 } else try self.addAtomToSection(atom, sect_id);
5211 sym.n_sect = self.getSectionOrdinal(match);4724 sym.n_sect = sect_id + 1;
5212}4725}
52134726
5214fn allocateAtom(4727fn allocateAtom(
...@@ -5216,15 +4729,15 @@ fn allocateAtom(...@@ -5216,15 +4729,15 @@ fn allocateAtom(
5216 atom: *Atom,4729 atom: *Atom,
5217 new_atom_size: u64,4730 new_atom_size: u64,
5218 alignment: u64,4731 alignment: u64,
5219 match: MatchingSection,4732 sect_id: u8,
5220) !u64 {4733) !u64 {
5221 const tracy = trace(@src());4734 const tracy = trace(@src());
5222 defer tracy.end();4735 defer tracy.end();
52234736
5224 const sect = self.getSectionPtr(match);4737 const header = &self.sections.items(.header)[sect_id];
5225 var free_list = self.atom_free_lists.get(match).?;4738 const free_list = &self.sections.items(.free_list)[sect_id];
5226 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;4739 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
5227 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;4740 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
52284741
5229 // We use these to indicate our intention to update metadata, placing the new atom,4742 // We use these to indicate our intention to update metadata, placing the new atom,
5230 // and possibly removing a free list node.4743 // and possibly removing a free list node.
...@@ -5244,7 +4757,7 @@ fn allocateAtom(...@@ -5244,7 +4757,7 @@ fn allocateAtom(
5244 // Is it enough that we could fit this new atom?4757 // Is it enough that we could fit this new atom?
5245 const sym = big_atom.getSymbol(self);4758 const sym = big_atom.getSymbol(self);
5246 const capacity = big_atom.capacity(self);4759 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;
5248 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;4761 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
5249 const capacity_end_vaddr = sym.n_value + capacity;4762 const capacity_end_vaddr = sym.n_value + capacity;
5250 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;4763 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
...@@ -5272,30 +4785,28 @@ fn allocateAtom(...@@ -5272,30 +4785,28 @@ fn allocateAtom(
5272 free_list_removal = i;4785 free_list_removal = i;
5273 }4786 }
5274 break :blk new_start_vaddr;4787 break :blk new_start_vaddr;
5275 } else if (self.atoms.get(match)) |last| {4788 } else if (maybe_last_atom.*) |last| {
5276 const last_symbol = last.getSymbol(self);4789 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;
5278 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;4791 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
5279 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);4792 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
5280 atom_placement = last;4793 atom_placement = last;
5281 break :blk new_start_vaddr;4794 break :blk new_start_vaddr;
5282 } else {4795 } else {
5283 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
5284 }4797 }
5285 };4798 };
52864799
5287 const expand_section = atom_placement == null or atom_placement.?.next == null;4800 const expand_section = atom_placement == null or atom_placement.?.next == null;
5288 if (expand_section) {4801 if (expand_section) {
5289 const needed_size = @intCast(u32, (vaddr + new_atom_size) - sect.addr);4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
5290 try self.growSection(match, needed_size);4803 try self.growSection(sect_id, needed_size);
5291 _ = try self.atoms.put(self.base.allocator, match, atom);4804 maybe_last_atom.* = atom;
5292 sect.size = needed_size;4805 header.size = needed_size;
5293 self.load_commands_dirty = true;
5294 }4806 }
5295 const align_pow = @intCast(u32, math.log2(alignment));4807 const align_pow = @intCast(u32, math.log2(alignment));
5296 if (sect.@"align" < align_pow) {4808 if (header.@"align" < align_pow) {
5297 sect.@"align" = align_pow;4809 header.@"align" = align_pow;
5298 self.load_commands_dirty = true;
5299 }4810 }
5300 atom.size = new_atom_size;4811 atom.size = new_atom_size;
5301 atom.alignment = align_pow;4812 atom.alignment = align_pow;
...@@ -5322,20 +4833,19 @@ fn allocateAtom(...@@ -5322,20 +4833,19 @@ fn allocateAtom(
5322 return vaddr;4833 return vaddr;
5323}4834}
53244835
5325pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {4836pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
5326 if (self.atoms.getPtr(match)) |last| {4837 var section = self.sections.get(sect_id);
5327 last.*.next = atom;4838 if (section.header.size > 0) {
5328 atom.prev = last.*;4839 section.last_atom.?.next = atom;
5329 last.* = atom;4840 atom.prev = section.last_atom.?;
5330 } else {
5331 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5332 }4841 }
5333 const sect = self.getSectionPtr(match);4842 section.last_atom = atom;
5334 const atom_alignment = try math.powi(u32, 2, atom.alignment);4843 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5335 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);4844 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
5336 const padding = aligned_end_addr - sect.size;4845 const padding = aligned_end_addr - section.header.size;
5337 sect.size += padding + atom.size;4846 section.header.size += padding + atom.size;
5338 sect.@"align" = @maximum(sect.@"align", atom.alignment);4847 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
4848 self.sections.set(sect_id, section);
5339}4849}
53404850
5341pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {4851pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
...@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5368 return sym_index;4878 return sym_index;
5369}4879}
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 } {
5372 for (indices) |maybe_prev_id| {4882 for (indices) |maybe_prev_id| {
5373 const prev_id = maybe_prev_id orelse continue;4883 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];
5375 return .{4885 return .{
5376 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,4886 .vmaddr = prev.vmaddr + prev.vmsize,
5377 .fileoff = prev.inner.fileoff + prev.inner.filesize,4887 .fileoff = prev.fileoff + prev.filesize,
5378 };4888 };
5379 }4889 }
5380 return .{ .vmaddr = 0, .fileoff = 0 };4890 return .{ .vmaddr = 0, .fileoff = 0 };
5381}4891}
53824892
5383fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {4893fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
5384 const seg_id = maybe_seg_id.* orelse return;4894 for (self.segments.items) |seg, i| {
53854895 const indexes = self.getSectionIndexes(@intCast(u8, i));
5386 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);4896 var out_seg = seg;
5387 defer mapping.deinit();4897 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
53884898 out_seg.nsects = 0;
5389 const seg = &self.load_commands.items[seg_id].segment;4899
5390 var sections = seg.sections.toOwnedSlice(self.base.allocator);4900 // Update section headers count; any section with size of 0 is excluded
5391 defer self.base.allocator.free(sections);4901 // since it doesn't have any data in the final binary file.
5392 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);4902 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
53934903 if (header.size == 0) continue;
5394 for (indices) |maybe_index| {4904 out_seg.cmdsize += @sizeOf(macho.section_64);
5395 const old_idx = maybe_index.* orelse continue;4905 out_seg.nsects += 1;
5396 const sect = &sections[old_idx];
5397 if (sect.size == 0) {
5398 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5399 maybe_index.* = null;
5400 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5401 seg.inner.nsects -= 1;
5402 } else {
5403 maybe_index.* = @intCast(u16, seg.sections.items.len);
5404 seg.sections.appendAssumeCapacity(sect.*);
5405 }4906 }
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 {4908 if (out_seg.nsects == 0 and
5438 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{4909 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
5439 &self.text_section_index,4910 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
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 });
54774911
5478 // Create new section ordinals.4912 try writer.writeStruct(out_seg);
5479 self.section_ordinals.clearRetainingCapacity();4913 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
5480 if (self.text_segment_cmd_index) |seg_id| {4914 if (header.size == 0) continue;
5481 const seg = self.load_commands.items[seg_id].segment;4915 try writer.writeStruct(header);
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);
5508 }4916 }
4917
4918 ncmds.* += 1;
5509 }4919 }
5510 self.sections_order_dirty = false;
5511}4920}
55124921
5513fn updateSectionOrdinals(self: *MachO) !void {4922fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5514 if (!self.sections_order_dirty) return;4923 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
55154924 seg.filesize = 0;
5516 const tracy = trace(@src());4925 seg.vmsize = 0;
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 }
55524926
5553 // FIXME Jakub4927 try self.writeDyldInfoData(ncmds, lc_writer);
5554 // TODO no need for duping work here; simply walk the atom graph4928 try self.writeFunctionStarts(ncmds, lc_writer);
5555 for (self.locals.items) |*sym| {4929 try self.writeDataInCode(ncmds, lc_writer);
5556 if (sym.undf()) continue;4930 try self.writeSymtabs(ncmds, lc_writer);
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 }
55674931
5568 self.section_ordinals.deinit(gpa);4932 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
5569 self.section_ordinals = ordinals;
5570}4933}
55714934
5572fn writeDyldInfoData(self: *MachO) !void {4935fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5573 const tracy = trace(@src());4936 const tracy = trace(@src());
5574 defer tracy.end();4937 defer tracy.end();
55754938
...@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {
5582 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);4945 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5583 defer lazy_bind_pointers.deinit();4946 defer lazy_bind_pointers.deinit();
55844947
5585 {4948 const slice = self.sections.slice();
5586 var it = self.atoms.iterator();4949 for (slice.items(.last_atom)) |last_atom, sect_id| {
5587 while (it.next()) |entry| {4950 var atom = last_atom orelse continue;
5588 const match = entry.key_ptr.*;4951 const segment_index = slice.items(.segment_index)[sect_id];
5589 var atom: *Atom = entry.value_ptr.*;4952 const header = slice.items(.header)[sect_id];
55904953
5591 if (self.text_segment_cmd_index) |seg| {4954 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
5592 if (match.seg == seg) continue; // __TEXT is non-writable
5593 }
55944955
5595 const seg = self.getSegment(match);4956 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
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 }
56114957
5612 for (atom.bindings.items) |binding| {4958 const seg = self.segments.items[segment_index];
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 }
56374959
5638 for (atom.lazy_bindings.items) |binding| {4960 while (true) {
5639 const bind_sym = self.getSymbol(binding.target);4961 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5640 const bind_sym_name = self.getSymbolName(binding.target);4962 const sym = atom.getSymbol(self);
5641 const dylib_ordinal = @divTrunc(4963 const base_offset = sym.n_value - seg.vmaddr;
5642 @bitCast(i16, bind_sym.n_desc),4964
5643 macho.N_SYMBOL_RESOLVER,4965 for (atom.rebases.items) |offset| {
5644 );4966 log.debug(" | rebase at {x}", .{base_offset + offset});
5645 var flags: u4 = 0;4967 try rebase_pointers.append(.{
5646 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{4968 .offset = base_offset + offset,
5647 binding.offset + base_offset,4969 .segment_id = segment_index,
5648 bind_sym_name,4970 });
5649 dylib_ordinal,4971 }
5650 });4972
5651 if (bind_sym.weakRef()) {4973 for (atom.bindings.items) |binding| {
5652 log.debug(" | marking as weak ref ", .{});4974 const bind_sym = self.getSymbol(binding.target);
5653 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);4975 const bind_sym_name = self.getSymbolName(binding.target);
5654 }4976 const dylib_ordinal = @divTrunc(
5655 try lazy_bind_pointers.append(.{4977 @bitCast(i16, bind_sym.n_desc),
5656 .offset = binding.offset + base_offset,4978 macho.N_SYMBOL_RESOLVER,
5657 .segment_id = match.seg,4979 );
5658 .dylib_ordinal = dylib_ordinal,4980 var flags: u4 = 0;
5659 .name = bind_sym_name,4981 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5660 .bind_flags = flags,4982 binding.offset + base_offset,
5661 });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);
5662 }4989 }
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| {4999 for (atom.lazy_bindings.items) |binding| {
5665 atom = prev;5000 const bind_sym = self.getSymbol(binding.target);
5666 } else break;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 });
5667 }5023 }
5024
5025 if (atom.prev) |prev| {
5026 atom = prev;
5027 } else break;
5668 }5028 }
5669 }5029 }
56705030
...@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {
5675 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5035 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5676 log.debug("generating export trie", .{});5036 log.debug("generating export trie", .{});
56775037
5678 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;5038 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5679 const base_address = text_segment.inner.vmaddr;5039 const base_address = text_segment.vmaddr;
56805040
5681 if (self.base.options.output_mode == .Exe) {5041 if (self.base.options.output_mode == .Exe) {
5682 for (&[_]SymbolWithLoc{5042 for (&[_]SymbolWithLoc{
...@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {
5714 try trie.finalize(gpa);5074 try trie.finalize(gpa);
5715 }5075 }
57165076
5717 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5077 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5718 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;5078 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
57195079 assert(rebase_off == link_seg.fileoff);
5720 const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64));
5721 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);5080 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5722 dyld_info.rebase_off = @intCast(u32, rebase_off);5081 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
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 });
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));
5730 const bind_size = try bind.bindInfoSize(bind_pointers.items);5084 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5731 dyld_info.bind_off = @intCast(u32, bind_off);5085 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
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 });
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));
5739 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);5088 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
5740 dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off);5089 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
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 });
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));
5748 const export_size = trie.size;5092 const export_size = trie.size;
5749 dyld_info.export_off = @intCast(u32, export_off);5093 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
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 });
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;5098 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
5759 var buffer = try gpa.alloc(u8, needed_size);
5760 defer gpa.free(buffer);5099 defer gpa.free(buffer);
5761 mem.set(u8, buffer, 0);5100 mem.set(u8, buffer, 0);
57625101
5763 var stream = std.io.fixedBufferStream(buffer);5102 var stream = std.io.fixedBufferStream(buffer);
5764 const writer = stream.writer();5103 const writer = stream.writer();
57655104
5766 const base_off = dyld_info.rebase_off;
5767 try bind.writeRebaseInfo(rebase_pointers.items, writer);5105 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
5770 try bind.writeBindInfo(bind_pointers.items, writer);5108 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
5773 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);5111 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
5776 _ = try trie.write(writer);5114 _ = try trie.write(writer);
57775115
5778 log.debug("writing dyld info from 0x{x} to 0x{x}", .{5116 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
5779 dyld_info.rebase_off,5117 rebase_off,
5780 dyld_info.rebase_off + needed_size,5118 rebase_off + needed_size,
5781 });5119 });
57825120
5783 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);5121 try self.base.file.?.pwriteAll(buffer, rebase_off);
5784 try self.populateLazyBindOffsetsInStubHelper(5122 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
5785 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],5123 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
5786 );5124 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
57875125
5788 self.load_commands_dirty = true;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;
5789}5141}
57905142
5791fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {5143fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5792 const gpa = self.base.allocator;5144 const gpa = self.base.allocator;
5793 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;5145
5794 const stub_helper_section_index = self.stub_helper_section_index orelse return;5146 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;
5799 if (self.stub_helper_preamble_atom == null) return;5147 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
5802 var table = std.AutoHashMap(i64, *Atom).init(gpa);5153 var table = std.AutoHashMap(i64, *Atom).init(gpa);
5803 defer table.deinit();5154 defer table.deinit();
58045155
5805 {5156 {
5806 var stub_atom = last_atom;5157 var stub_atom = last_atom;
5807 var laptr_atom = self.atoms.get(.{5158 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
5808 .seg = self.data_segment_cmd_index.?,
5809 .sect = self.la_symbol_ptr_section_index.?,
5810 }).?;
5811 const base_addr = blk: {5159 const base_addr = blk: {
5812 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;5160 const seg = self.segments.items[self.data_segment_cmd_index.?];
5813 break :blk seg.inner.vmaddr;5161 break :blk seg.vmaddr;
5814 };5162 };
58155163
5816 while (true) {5164 while (true) {
...@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5871 }5219 }
5872 }5220 }
58735221
5874 const sect = self.getSection(.{5222 const header = self.sections.items(.header)[stub_helper_section_index];
5875 .seg = text_segment_cmd_index,
5876 .sect = stub_helper_section_index,
5877 });
5878 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {5223 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
5879 .x86_64 => 1,5224 .x86_64 => 1,
5880 .aarch64 => 2 * @sizeOf(u32),5225 .aarch64 => 2 * @sizeOf(u32),
...@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5886 while (offsets.popOrNull()) |bind_offset| {5231 while (offsets.popOrNull()) |bind_offset| {
5887 const atom = table.get(bind_offset.sym_offset).?;5232 const atom = table.get(bind_offset.sym_offset).?;
5888 const sym = atom.getSymbol(self);5233 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;
5890 mem.writeIntLittle(u32, &buf, bind_offset.offset);5235 mem.writeIntLittle(u32, &buf, bind_offset.offset);
5891 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{5236 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
5892 bind_offset.offset,5237 bind_offset.offset,
...@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58995244
5900const asc_u64 = std.sort.asc(u64);5245const asc_u64 = std.sort.asc(u64);
59015246
5902fn writeFunctionStarts(self: *MachO) !void {5247fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !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
5907 const tracy = trace(@src());5248 const tracy = trace(@src());
5908 defer tracy.end();5249 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
5910 const gpa = self.base.allocator;5255 const gpa = self.base.allocator;
59115256
5912 // We need to sort by address first5257 // We need to sort by address first
...@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {
5918 const sym = self.getSymbol(global);5263 const sym = self.getSymbol(global);
5919 if (sym.undf()) continue;5264 if (sym.undf()) continue;
5920 if (sym.n_desc == N_DESC_GCED) continue;5265 if (sym.n_desc == N_DESC_GCED) continue;
5921 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);5266 const sect_id = sym.n_sect - 1;
5922 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;5267 if (sect_id != text_sect_index) continue;
59235268
5924 addresses.appendAssumeCapacity(sym.n_value);5269 addresses.appendAssumeCapacity(sym.n_value);
5925 }5270 }
...@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {
59325277
5933 var last_off: u32 = 0;5278 var last_off: u32 = 0;
5934 for (addresses.items) |addr| {5279 for (addresses.items) |addr| {
5935 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);5280 const offset = @intCast(u32, addr - text_seg.vmaddr);
5936 const diff = offset - last_off;5281 const diff = offset - last_off;
59375282
5938 if (diff == 0) continue;5283 if (diff == 0) continue;
...@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {
5951 try std.leb.writeULEB128(buffer.writer(), offset);5296 try std.leb.writeULEB128(buffer.writer(), offset);
5952 }5297 }
59535298
5954 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5299 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5955 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;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));5304 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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;
59625305
5963 log.debug("writing function starts info from 0x{x} to 0x{x}", .{5306 try self.base.file.?.pwriteAll(buffer.items, offset);
5964 fn_cmd.dataoff,
5965 fn_cmd.dataoff + fn_cmd.datasize,
5966 });
59675307
5968 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);5308 try lc_writer.writeStruct(macho.linkedit_data_command{
5969 self.load_commands_dirty = true;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;
5970}5315}
59715316
5972fn filterDataInCode(5317fn filterDataInCode(
...@@ -5988,17 +5333,15 @@ fn filterDataInCode(...@@ -5988,17 +5333,15 @@ fn filterDataInCode(
5988 return dices[start..end];5333 return dices[start..end];
5989}5334}
59905335
5991fn writeDataInCode(self: *MachO) !void {5336fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5992 const tracy = trace(@src());5337 const tracy = trace(@src());
5993 defer tracy.end();5338 defer tracy.end();
59945339
5995 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);5340 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
5996 defer out_dice.deinit();5341 defer out_dice.deinit();
59975342
5998 const text_sect = self.getSection(.{5343 const text_sect_id = self.text_section_index orelse return;
5999 .seg = self.text_segment_cmd_index orelse return,5344 const text_sect_header = self.sections.items(.header)[text_sect_id];
6000 .sect = self.text_section_index orelse return,
6001 });
60025345
6003 for (self.objects.items) |object| {5346 for (self.objects.items) |object| {
6004 const dice = object.parseDataInCode() orelse continue;5347 const dice = object.parseDataInCode() orelse continue;
...@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {...@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {
6008 const sym = atom.getSymbol(self);5351 const sym = atom.getSymbol(self);
6009 if (sym.n_desc == N_DESC_GCED) continue;5352 if (sym.n_desc == N_DESC_GCED) continue;
60105353
6011 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);5354 const sect_id = sym.n_sect - 1;
6012 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {5355 if (sect_id != self.text_section_index.?) {
6013 continue;5356 continue;
6014 }5357 }
60155358
6016 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;5359 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
6017 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;5360 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
6018 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);5361 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) orelse5362 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
6020 return error.Overflow;5363 return error.Overflow;
60215364
6022 for (filtered_dice) |single| {5365 for (filtered_dice) |single| {
...@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {...@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {
6030 }5373 }
6031 }5374 }
60325375
6033 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5376 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6034 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;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));5381 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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;
60415382
6042 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{5383 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
6043 dice_cmd.dataoff,5384 try lc_writer.writeStruct(macho.linkedit_data_command{
6044 dice_cmd.dataoff + dice_cmd.datasize,5385 .cmd = .DATA_IN_CODE,
5386 .cmdsize = @sizeOf(macho.linkedit_data_command),
5387 .dataoff = @intCast(u32, offset),
5388 .datasize = @intCast(u32, needed_size),
6045 });5389 });
60465390 ncmds.* += 1;
6047 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6048 self.load_commands_dirty = true;
6049}5391}
60505392
6051fn writeSymtab(self: *MachO) !void {5393fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
6052 const tracy = trace(@src());5394 var symtab_cmd = macho.symtab_command{
6053 defer tracy.end();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 {
6055 const gpa = self.base.allocator;5432 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
6061 var locals = std.ArrayList(macho.nlist_64).init(gpa);5434 var locals = std.ArrayList(macho.nlist_64).init(gpa);
6062 defer locals.deinit();5435 defer locals.deinit();
...@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {...@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {
61015474
6102 var imports = std.ArrayList(macho.nlist_64).init(gpa);5475 var imports = std.ArrayList(macho.nlist_64).init(gpa);
6103 defer imports.deinit();5476 defer imports.deinit();
5477
6104 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);5478 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6105 defer imports_table.deinit();
61065479
6107 for (self.globals.values()) |global| {5480 for (self.globals.values()) |global| {
6108 const sym = self.getSymbol(global);5481 const sym = self.getSymbol(global);
...@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {...@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {
6115 try imports_table.putNoClobber(global, new_index);5488 try imports_table.putNoClobber(global, new_index);
6116 }5489 }
61175490
6118 const nlocals = locals.items.len;5491 const nlocals = @intCast(u32, locals.items.len);
6119 const nexports = exports.items.len;5492 const nexports = @intCast(u32, exports.items.len);
6120 const nimports = imports.items.len;5493 const nimports = @intCast(u32, imports.items.len);
6121 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);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
6123 var buffer = std.ArrayList(u8).init(gpa);5505 var buffer = std.ArrayList(u8).init(gpa);
6124 defer buffer.deinit();5506 defer buffer.deinit();
6125 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));5507 try buffer.ensureTotalCapacityPrecise(needed_size);
6126 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));5508 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
6127 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));5509 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
6128 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));5510 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 });5512 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
6131 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);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.5534 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
6136 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;5535
6137 dysymtab.nlocalsym = @intCast(u32, nlocals);5536 lc.stroff = @intCast(u32, offset);
6138 dysymtab.iextdefsym = dysymtab.nlocalsym;5537 lc.strsize = @intCast(u32, needed_size);
6139 dysymtab.nextdefsym = @intCast(u32, nexports);5538}
6140 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;5539
6141 dysymtab.nundefsym = @intCast(u32, nimports);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;
6143 const nstubs = @intCast(u32, self.stubs_table.count());5549 const nstubs = @intCast(u32, self.stubs_table.count());
6144 const ngot_entries = @intCast(u32, self.got_entries_table.count());5550 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));5555 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6147 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);5556 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
6148 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;5557 const needed_size = nindirectsyms * @sizeOf(u32);
61495558 seg.filesize = offset + needed_size - seg.fileoff;
6150 seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff;
61515559
6152 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{5560 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
6153 dysymtab.indirectsymoff,
6154 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6155 });
61565561
6157 var buf = std.ArrayList(u8).init(gpa);5562 var buf = std.ArrayList(u8).init(gpa);
6158 defer buf.deinit();5563 defer buf.deinit();
6159 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));5564 try buf.ensureTotalCapacity(needed_size);
6160 const writer = buf.writer();5565 const writer = buf.writer();
61615566
6162 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {5567 if (self.stubs_section_index) |sect_id| {
6163 const stubs_section_index = self.stubs_section_index orelse break :blk;5568 const stubs = &self.sections.items(.header)[sect_id];
6164 const stubs = self.getSectionPtr(.{
6165 .seg = text_segment_cmd_index,
6166 .sect = stubs_section_index,
6167 });
6168 stubs.reserved1 = 0;5569 stubs.reserved1 = 0;
6169 for (self.stubs.items) |entry| {5570 for (self.stubs.items) |entry| {
6170 if (entry.sym_index == 0) continue;5571 if (entry.sym_index == 0) continue;
...@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {...@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {
6172 if (atom_sym.n_desc == N_DESC_GCED) continue;5573 if (atom_sym.n_desc == N_DESC_GCED) continue;
6173 const target_sym = self.getSymbol(entry.target);5574 const target_sym = self.getSymbol(entry.target);
6174 assert(target_sym.undf());5575 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).?);
6176 }5577 }
6177 }5578 }
61785579
6179 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {5580 if (self.got_section_index) |sect_id| {
6180 const got_section_index = self.got_section_index orelse break :blk;5581 const got = &self.sections.items(.header)[sect_id];
6181 const got = self.getSectionPtr(.{
6182 .seg = data_const_segment_cmd_index,
6183 .sect = got_section_index,
6184 });
6185 got.reserved1 = nstubs;5582 got.reserved1 = nstubs;
6186 for (self.got_entries.items) |entry| {5583 for (self.got_entries.items) |entry| {
6187 if (entry.sym_index == 0) continue;5584 if (entry.sym_index == 0) continue;
...@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {...@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {
6189 if (atom_sym.n_desc == N_DESC_GCED) continue;5586 if (atom_sym.n_desc == N_DESC_GCED) continue;
6190 const target_sym = self.getSymbol(entry.target);5587 const target_sym = self.getSymbol(entry.target);
6191 if (target_sym.undf()) {5588 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).?);
6193 } else {5590 } else {
6194 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);5591 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
6195 }5592 }
6196 }5593 }
6197 }5594 }
61985595
6199 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {5596 if (self.la_symbol_ptr_section_index) |sect_id| {
6200 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;5597 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
6201 const la_symbol_ptr = self.getSectionPtr(.{
6202 .seg = data_segment_cmd_index,
6203 .sect = la_symbol_ptr_section_index,
6204 });
6205 la_symbol_ptr.reserved1 = nstubs + ngot_entries;5598 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6206 for (self.stubs.items) |entry| {5599 for (self.stubs.items) |entry| {
6207 if (entry.sym_index == 0) continue;5600 if (entry.sym_index == 0) continue;
...@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {...@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {
6209 if (atom_sym.n_desc == N_DESC_GCED) continue;5602 if (atom_sym.n_desc == N_DESC_GCED) continue;
6210 const target_sym = self.getSymbol(entry.target);5603 const target_sym = self.getSymbol(entry.target);
6211 assert(target_sym.undf());5604 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).?);
6213 }5606 }
6214 }5607 }
62155608
6216 assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32));5609 assert(buf.items.len == needed_size);
62175610 try self.base.file.?.pwriteAll(buf.items, offset);
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();
62455611
6246 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5612 lc.nlocalsym = ctx.nlocalsym;
6247 seg.inner.filesize = 0;5613 lc.iextdefsym = iextdefsym;
62485614 lc.nextdefsym = ctx.nextdefsym;
6249 try self.writeDyldInfoData();5615 lc.iundefsym = iundefsym;
6250 try self.writeFunctionStarts();5616 lc.nundefsym = ctx.nundefsym;
6251 try self.writeDataInCode();5617 lc.indirectsymoff = @intCast(u32, offset);
6252 try self.writeSymtab();5618 lc.nindirectsyms = nindirectsyms;
6253 try self.writeStrtab();
6254
6255 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6256}5619}
62575620
6258fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {5621fn writeCodeSignaturePadding(
6259 const tracy = trace(@src());5622 self: *MachO,
6260 defer tracy.end();5623 code_sig: *CodeSignature,
62615624 ncmds: *u32,
6262 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5625 lc_writer: anytype,
6263 const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;5626) !u32 {
5627 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6264 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file5628 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
6265 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2715629 // 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);5630 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
6267 const datasize = code_sig.estimateSize(dataoff);5631 const needed_size = code_sig.estimateSize(offset);
6268 cs_cmd.dataoff = @intCast(u32, dataoff);5632 seg.filesize = offset + needed_size - seg.fileoff;
6269 cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff));5633 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
62705634 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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 });
6275 // Pad out the space. We need to do this to calculate valid hashes for everything in the file5635 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
6276 // except for code signature data.5636 // except for code signature data.
6277 try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1);5637 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
6278 self.load_commands_dirty = true;
6279}
62805638
6281fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {5639 try lc_writer.writeStruct(macho.linkedit_data_command{
6282 const tracy = trace(@src());5640 .cmd = .CODE_SIGNATURE,
6283 defer tracy.end();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;5650fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
6286 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5651 const seg = self.segments.items[self.text_segment_cmd_index.?];
62875652
6288 var buffer = std.ArrayList(u8).init(self.base.allocator);5653 var buffer = std.ArrayList(u8).init(self.base.allocator);
6289 defer buffer.deinit();5654 defer buffer.deinit();
6290 try buffer.ensureTotalCapacityPrecise(code_sig.size());5655 try buffer.ensureTotalCapacityPrecise(code_sig.size());
6291 try code_sig.writeAdhocSignature(self.base.allocator, .{5656 try code_sig.writeAdhocSignature(self.base.allocator, .{
6292 .file = self.base.file.?,5657 .file = self.base.file.?,
6293 .exec_seg_base = seg.inner.fileoff,5658 .exec_seg_base = seg.fileoff,
6294 .exec_seg_limit = seg.inner.filesize,5659 .exec_seg_limit = seg.filesize,
6295 .code_sig_cmd = code_sig_cmd,5660 .file_size = offset,
6296 .output_mode = self.base.options.output_mode,5661 .output_mode = self.base.options.output_mode,
6297 }, buffer.writer());5662 }, buffer.writer());
6298 assert(buffer.items.len == code_sig.size());5663 assert(buffer.items.len == code_sig.size());
62995664
6300 log.debug("writing code signature from 0x{x} to 0x{x}", .{5665 log.debug("writing code signature from 0x{x} to 0x{x}", .{
6301 code_sig_cmd.dataoff,5666 offset,
6302 code_sig_cmd.dataoff + buffer.items.len,5667 offset + buffer.items.len,
6303 });5668 });
63045669
6305 try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff);5670 try self.base.file.?.pwriteAll(buffer.items, offset);
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;
6333}5671}
63345672
6335/// Writes Mach-O file header.5673/// Writes Mach-O file header.
6336fn writeHeader(self: *MachO) !void {5674fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
6337 var header: macho.mach_header_64 = .{};5675 var header: macho.mach_header_64 = .{};
6338 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;5676 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
63395677
...@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {...@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {
6361 else => unreachable,5699 else => unreachable,
6362 }5700 }
63635701
6364 if (self.tlv_section_index) |_| {5702 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {
6365 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;5703 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
6366 }5704 }
63675705
6368 header.ncmds = 0;5706 header.ncmds = ncmds;
6369 header.sizeofcmds = 0;5707 header.sizeofcmds = sizeofcmds;
6370
6371 for (self.load_commands.items) |cmd| {
6372 if (cmd.cmd() == .NONE) continue;
6373 header.sizeofcmds += cmd.cmdsize();
6374 header.ncmds += 1;
6375 }
63765708
6377 log.debug("writing Mach-O header {}", .{header});5709 log.debug("writing Mach-O header {}", .{header});
63785710
...@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {...@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
6392 return buf;5724 return buf;
6393}5725}
63945726
6395pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {5727fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
6396 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;5728 for (self.segments.items) |seg, i| {
6397}5729 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
63985730 } else return null;
6399pub fn getMatchingSectionFromOrdinal(self: *MachO, ord: u8) MatchingSection {
6400 const index = ord - 1;
6401 assert(index < self.section_ordinals.count());
6402 return self.section_ordinals.keys()[index];
6403}
6404
6405pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
6406 assert(match.seg < self.load_commands.items.len);
6407 return &self.load_commands.items[match.seg].segment;
6408}
6409
6410pub fn getSegment(self: *MachO, match: MatchingSection) macho.SegmentCommand {
6411 return self.getSegmentPtr(match).*;
6412}5731}
64135732
6414pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {5733pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
6415 const seg = self.getSegmentPtr(match);5734 // TODO investigate caching with a hashmap
6416 assert(match.sect < seg.sections.items.len);5735 for (self.sections.items(.header)) |header, i| {
6417 return &seg.sections.items[match.sect];5736 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
5737 return @intCast(u8, i);
5738 } else return null;
6418}5739}
64195740
6420pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {5741pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
6421 return self.getSectionPtr(match).*;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 };
6422}5748}
64235749
6424pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {5750pub 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:...@@ -6512,72 +5838,6 @@ pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate:
6512 return i;5838 return i;
6513}5839}
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
6581pub fn generateSymbolStabs(5841pub fn generateSymbolStabs(
6582 self: *MachO,5842 self: *MachO,
6583 object: Object,5843 object: Object,
...@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(...@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(
6585) !void {5845) !void {
6586 assert(!self.base.options.strip);5846 assert(!self.base.options.strip);
65875847
6588 const gpa = self.base.allocator;
6589
6590 log.debug("parsing debug info in '{s}'", .{object.name});5848 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
6594 // We assume there is only one CU.5855 // 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) {
6596 error.MissingDebugInfo => {5857 error.MissingDebugInfo => {
6597 // TODO audit cases with missing debug info and audit our dwarf.zig module.5858 // TODO audit cases with missing debug info and audit our dwarf.zig module.
6598 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});5859 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
...@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(...@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(
6600 },5861 },
6601 else => |e| return e,5862 else => |e| return e,
6602 };5863 };
6603 const tu_name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);5864 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name);
6604 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);5865 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir);
66055866
6606 // Open scope5867 // Open scope
6607 try locals.ensureUnusedCapacity(3);5868 try locals.ensureUnusedCapacity(3);
...@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(...@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(
6664fn generateSymbolStabsForSymbol(5925fn generateSymbolStabsForSymbol(
6665 self: *MachO,5926 self: *MachO,
6666 sym_loc: SymbolWithLoc,5927 sym_loc: SymbolWithLoc,
6667 debug_info: DebugInfo,5928 debug_info: dwarf.DwarfInfo,
6668 buf: *[4]macho.nlist_64,5929 buf: *[4]macho.nlist_64,
6669) ![]const macho.nlist_64 {5930) ![]const macho.nlist_64 {
6670 const gpa = self.base.allocator;5931 const gpa = self.base.allocator;
...@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(...@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(
6679 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];5940 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
6680 const size: ?u64 = size: {5941 const size: ?u64 = size: {
6681 if (source_sym.tentative()) break :size null;5942 if (source_sym.tentative()) break :size null;
6682 for (debug_info.inner.func_list.items) |func| {5943 for (debug_info.func_list.items) |func| {
6683 if (func.pc_range) |range| {5944 if (func.pc_range) |range| {
6684 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {5945 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
6685 break :size range.end - range.start;5946 break :size range.end - range.start;
...@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(...@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(
6731 }5992 }
6732}5993}
67335994
6734fn snapshotState(self: *MachO) !void {5995// fn snapshotState(self: *MachO) !void {
6735 const emit = self.base.options.emit orelse {5996// const emit = self.base.options.emit orelse {
6736 log.debug("no emit directory found; skipping snapshot...", .{});5997// log.debug("no emit directory found; skipping snapshot...", .{});
6737 return;5998// return;
6738 };5999// };
67396000
6740 const Snapshot = struct {6001// const Snapshot = struct {
6741 const Node = struct {6002// const Node = struct {
6742 const Tag = enum {6003// const Tag = enum {
6743 section_start,6004// section_start,
6744 section_end,6005// section_end,
6745 atom_start,6006// atom_start,
6746 atom_end,6007// atom_end,
6747 relocation,6008// relocation,
67486009
6749 pub fn jsonStringify(6010// pub fn jsonStringify(
6750 tag: Tag,6011// tag: Tag,
6751 options: std.json.StringifyOptions,6012// options: std.json.StringifyOptions,
6752 out_stream: anytype,6013// out_stream: anytype,
6753 ) !void {6014// ) !void {
6754 _ = options;6015// _ = options;
6755 switch (tag) {6016// switch (tag) {
6756 .section_start => try out_stream.writeAll("\"section_start\""),6017// .section_start => try out_stream.writeAll("\"section_start\""),
6757 .section_end => try out_stream.writeAll("\"section_end\""),6018// .section_end => try out_stream.writeAll("\"section_end\""),
6758 .atom_start => try out_stream.writeAll("\"atom_start\""),6019// .atom_start => try out_stream.writeAll("\"atom_start\""),
6759 .atom_end => try out_stream.writeAll("\"atom_end\""),6020// .atom_end => try out_stream.writeAll("\"atom_end\""),
6760 .relocation => try out_stream.writeAll("\"relocation\""),6021// .relocation => try out_stream.writeAll("\"relocation\""),
6761 }6022// }
6762 }6023// }
6763 };6024// };
6764 const Payload = struct {6025// const Payload = struct {
6765 name: []const u8 = "",6026// name: []const u8 = "",
6766 aliases: [][]const u8 = &[0][]const u8{},6027// aliases: [][]const u8 = &[0][]const u8{},
6767 is_global: bool = false,6028// is_global: bool = false,
6768 target: u64 = 0,6029// target: u64 = 0,
6769 };6030// };
6770 address: u64,6031// address: u64,
6771 tag: Tag,6032// tag: Tag,
6772 payload: Payload,6033// payload: Payload,
6773 };6034// };
6774 timestamp: i128,6035// timestamp: i128,
6775 nodes: []Node,6036// nodes: []Node,
6776 };6037// };
67776038
6778 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);6039// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6779 defer arena_allocator.deinit();6040// defer arena_allocator.deinit();
6780 const arena = arena_allocator.allocator();6041// const arena = arena_allocator.allocator();
67816042
6782 const out_file = try emit.directory.handle.createFile("snapshots.json", .{6043// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6783 .truncate = false,6044// .truncate = false,
6784 .read = true,6045// .read = true,
6785 });6046// });
6786 defer out_file.close();6047// defer out_file.close();
67876048
6788 if (out_file.seekFromEnd(-1)) {6049// if (out_file.seekFromEnd(-1)) {
6789 try out_file.writer().writeByte(',');6050// try out_file.writer().writeByte(',');
6790 } else |err| switch (err) {6051// } else |err| switch (err) {
6791 error.Unseekable => try out_file.writer().writeByte('['),6052// error.Unseekable => try out_file.writer().writeByte('['),
6792 else => |e| return e,6053// else => |e| return e,
6793 }6054// }
6794 const writer = out_file.writer();6055// const writer = out_file.writer();
67956056
6796 var snapshot = Snapshot{6057// var snapshot = Snapshot{
6797 .timestamp = std.time.nanoTimestamp(),6058// .timestamp = std.time.nanoTimestamp(),
6798 .nodes = undefined,6059// .nodes = undefined,
6799 };6060// };
6800 var nodes = std.ArrayList(Snapshot.Node).init(arena);6061// var nodes = std.ArrayList(Snapshot.Node).init(arena);
68016062
6802 for (self.section_ordinals.keys()) |key| {6063// for (self.section_ordinals.keys()) |key| {
6803 const sect = self.getSection(key);6064// const sect = self.getSection(key);
6804 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });6065// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6805 try nodes.append(.{6066// try nodes.append(.{
6806 .address = sect.addr,6067// .address = sect.addr,
6807 .tag = .section_start,6068// .tag = .section_start,
6808 .payload = .{ .name = sect_name },6069// .payload = .{ .name = sect_name },
6809 });6070// });
68106071
6811 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;6072// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
68126073
6813 var atom: *Atom = self.atoms.get(key) orelse {6074// var atom: *Atom = self.atoms.get(key) orelse {
6814 try nodes.append(.{6075// try nodes.append(.{
6815 .address = sect.addr + sect.size,6076// .address = sect.addr + sect.size,
6816 .tag = .section_end,6077// .tag = .section_end,
6817 .payload = .{},6078// .payload = .{},
6818 });6079// });
6819 continue;6080// continue;
6820 };6081// };
68216082
6822 while (atom.prev) |prev| {6083// while (atom.prev) |prev| {
6823 atom = prev;6084// atom = prev;
6824 }6085// }
68256086
6826 while (true) {6087// while (true) {
6827 const atom_sym = atom.getSymbol(self);6088// const atom_sym = atom.getSymbol(self);
6828 var node = Snapshot.Node{6089// var node = Snapshot.Node{
6829 .address = atom_sym.n_value,6090// .address = atom_sym.n_value,
6830 .tag = .atom_start,6091// .tag = .atom_start,
6831 .payload = .{6092// .payload = .{
6832 .name = atom.getName(self),6093// .name = atom.getName(self),
6833 .is_global = self.globals.contains(atom.getName(self)),6094// .is_global = self.globals.contains(atom.getName(self)),
6834 },6095// },
6835 };6096// };
68366097
6837 var aliases = std.ArrayList([]const u8).init(arena);6098// var aliases = std.ArrayList([]const u8).init(arena);
6838 for (atom.contained.items) |sym_off| {6099// for (atom.contained.items) |sym_off| {
6839 if (sym_off.offset == 0) {6100// if (sym_off.offset == 0) {
6840 try aliases.append(self.getSymbolName(.{6101// try aliases.append(self.getSymbolName(.{
6841 .sym_index = sym_off.sym_index,6102// .sym_index = sym_off.sym_index,
6842 .file = atom.file,6103// .file = atom.file,
6843 }));6104// }));
6844 }6105// }
6845 }6106// }
6846 node.payload.aliases = aliases.toOwnedSlice();6107// node.payload.aliases = aliases.toOwnedSlice();
6847 try nodes.append(node);6108// try nodes.append(node);
68486109
6849 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);6110// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6850 for (atom.relocs.items) |rel| {6111// for (atom.relocs.items) |rel| {
6851 const source_addr = blk: {6112// const source_addr = blk: {
6852 const source_sym = atom.getSymbol(self);6113// const source_sym = atom.getSymbol(self);
6853 break :blk source_sym.n_value + rel.offset;6114// break :blk source_sym.n_value + rel.offset;
6854 };6115// };
6855 const target_addr = blk: {6116// const target_addr = blk: {
6856 const target_atom = rel.getTargetAtom(self) orelse {6117// const target_atom = rel.getTargetAtom(self) orelse {
6857 // If there is no atom for target, we still need to check for special, atom-less6118// // If there is no atom for target, we still need to check for special, atom-less
6858 // symbols such as `___dso_handle`.6119// // symbols such as `___dso_handle`.
6859 const target_name = self.getSymbolName(rel.target);6120// const target_name = self.getSymbolName(rel.target);
6860 if (self.globals.contains(target_name)) {6121// if (self.globals.contains(target_name)) {
6861 const atomless_sym = self.getSymbol(rel.target);6122// const atomless_sym = self.getSymbol(rel.target);
6862 break :blk atomless_sym.n_value;6123// break :blk atomless_sym.n_value;
6863 }6124// }
6864 break :blk 0;6125// break :blk 0;
6865 };6126// };
6866 const target_sym = if (target_atom.isSymbolContained(rel.target, self))6127// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6867 self.getSymbol(rel.target)6128// self.getSymbol(rel.target)
6868 else6129// else
6869 target_atom.getSymbol(self);6130// target_atom.getSymbol(self);
6870 const base_address: u64 = if (is_tlv) base_address: {6131// const base_address: u64 = if (is_tlv) base_address: {
6871 const sect_id: u16 = sect_id: {6132// const sect_id: u16 = sect_id: {
6872 if (self.tlv_data_section_index) |i| {6133// if (self.tlv_data_section_index) |i| {
6873 break :sect_id i;6134// break :sect_id i;
6874 } else if (self.tlv_bss_section_index) |i| {6135// } else if (self.tlv_bss_section_index) |i| {
6875 break :sect_id i;6136// break :sect_id i;
6876 } else unreachable;6137// } else unreachable;
6877 };6138// };
6878 break :base_address self.getSection(.{6139// break :base_address self.getSection(.{
6879 .seg = self.data_segment_cmd_index.?,6140// .seg = self.data_segment_cmd_index.?,
6880 .sect = sect_id,6141// .sect = sect_id,
6881 }).addr;6142// }).addr;
6882 } else 0;6143// } else 0;
6883 break :blk target_sym.n_value - base_address;6144// break :blk target_sym.n_value - base_address;
6884 };6145// };
68856146
6886 relocs.appendAssumeCapacity(.{6147// relocs.appendAssumeCapacity(.{
6887 .address = source_addr,6148// .address = source_addr,
6888 .tag = .relocation,6149// .tag = .relocation,
6889 .payload = .{ .target = target_addr },6150// .payload = .{ .target = target_addr },
6890 });6151// });
6891 }6152// }
68926153
6893 if (atom.contained.items.len == 0) {6154// if (atom.contained.items.len == 0) {
6894 try nodes.appendSlice(relocs.items);6155// try nodes.appendSlice(relocs.items);
6895 } else {6156// } else {
6896 // Need to reverse iteration order of relocs since by default for relocatable sources6157// // 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, for6158// // they come in reverse. For linking, this doesn't matter in any way, however, for
6898 // arranging the memoryline for displaying it does.6159// // arranging the memoryline for displaying it does.
6899 std.mem.reverse(Snapshot.Node, relocs.items);6160// std.mem.reverse(Snapshot.Node, relocs.items);
69006161
6901 var next_i: usize = 0;6162// var next_i: usize = 0;
6902 var last_rel: usize = 0;6163// var last_rel: usize = 0;
6903 while (next_i < atom.contained.items.len) : (next_i += 1) {6164// while (next_i < atom.contained.items.len) : (next_i += 1) {
6904 const loc = SymbolWithLoc{6165// const loc = SymbolWithLoc{
6905 .sym_index = atom.contained.items[next_i].sym_index,6166// .sym_index = atom.contained.items[next_i].sym_index,
6906 .file = atom.file,6167// .file = atom.file,
6907 };6168// };
6908 const cont_sym = self.getSymbol(loc);6169// const cont_sym = self.getSymbol(loc);
6909 const cont_sym_name = self.getSymbolName(loc);6170// const cont_sym_name = self.getSymbolName(loc);
6910 var contained_node = Snapshot.Node{6171// var contained_node = Snapshot.Node{
6911 .address = cont_sym.n_value,6172// .address = cont_sym.n_value,
6912 .tag = .atom_start,6173// .tag = .atom_start,
6913 .payload = .{6174// .payload = .{
6914 .name = cont_sym_name,6175// .name = cont_sym_name,
6915 .is_global = self.globals.contains(cont_sym_name),6176// .is_global = self.globals.contains(cont_sym_name),
6916 },6177// },
6917 };6178// };
69186179
6919 // Accumulate aliases6180// // Accumulate aliases
6920 var inner_aliases = std.ArrayList([]const u8).init(arena);6181// var inner_aliases = std.ArrayList([]const u8).init(arena);
6921 while (true) {6182// while (true) {
6922 if (next_i + 1 >= atom.contained.items.len) break;6183// if (next_i + 1 >= atom.contained.items.len) break;
6923 const next_sym_loc = SymbolWithLoc{6184// const next_sym_loc = SymbolWithLoc{
6924 .sym_index = atom.contained.items[next_i + 1].sym_index,6185// .sym_index = atom.contained.items[next_i + 1].sym_index,
6925 .file = atom.file,6186// .file = atom.file,
6926 };6187// };
6927 const next_sym = self.getSymbol(next_sym_loc);6188// const next_sym = self.getSymbol(next_sym_loc);
6928 if (next_sym.n_value != cont_sym.n_value) break;6189// if (next_sym.n_value != cont_sym.n_value) break;
6929 const next_sym_name = self.getSymbolName(next_sym_loc);6190// const next_sym_name = self.getSymbolName(next_sym_loc);
6930 if (self.globals.contains(next_sym_name)) {6191// if (self.globals.contains(next_sym_name)) {
6931 try inner_aliases.append(contained_node.payload.name);6192// try inner_aliases.append(contained_node.payload.name);
6932 contained_node.payload.name = next_sym_name;6193// contained_node.payload.name = next_sym_name;
6933 contained_node.payload.is_global = true;6194// contained_node.payload.is_global = true;
6934 } else try inner_aliases.append(next_sym_name);6195// } else try inner_aliases.append(next_sym_name);
6935 next_i += 1;6196// next_i += 1;
6936 }6197// }
69376198
6938 const cont_size = if (next_i + 1 < atom.contained.items.len)6199// const cont_size = if (next_i + 1 < atom.contained.items.len)
6939 self.getSymbol(.{6200// self.getSymbol(.{
6940 .sym_index = atom.contained.items[next_i + 1].sym_index,6201// .sym_index = atom.contained.items[next_i + 1].sym_index,
6941 .file = atom.file,6202// .file = atom.file,
6942 }).n_value - cont_sym.n_value6203// }).n_value - cont_sym.n_value
6943 else6204// else
6944 atom_sym.n_value + atom.size - cont_sym.n_value;6205// atom_sym.n_value + atom.size - cont_sym.n_value;
69456206
6946 contained_node.payload.aliases = inner_aliases.toOwnedSlice();6207// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6947 try nodes.append(contained_node);6208// try nodes.append(contained_node);
69486209
6949 for (relocs.items[last_rel..]) |rel| {6210// for (relocs.items[last_rel..]) |rel| {
6950 if (rel.address >= cont_sym.n_value + cont_size) {6211// if (rel.address >= cont_sym.n_value + cont_size) {
6951 break;6212// break;
6952 }6213// }
6953 try nodes.append(rel);6214// try nodes.append(rel);
6954 last_rel += 1;6215// last_rel += 1;
6955 }6216// }
69566217
6957 try nodes.append(.{6218// try nodes.append(.{
6958 .address = cont_sym.n_value + cont_size,6219// .address = cont_sym.n_value + cont_size,
6959 .tag = .atom_end,6220// .tag = .atom_end,
6960 .payload = .{},6221// .payload = .{},
6961 });6222// });
6962 }6223// }
6963 }6224// }
69646225
6965 try nodes.append(.{6226// try nodes.append(.{
6966 .address = atom_sym.n_value + atom.size,6227// .address = atom_sym.n_value + atom.size,
6967 .tag = .atom_end,6228// .tag = .atom_end,
6968 .payload = .{},6229// .payload = .{},
6969 });6230// });
69706231
6971 if (atom.next) |next| {6232// if (atom.next) |next| {
6972 atom = next;6233// atom = next;
6973 } else break;6234// } else break;
6974 }6235// }
69756236
6976 try nodes.append(.{6237// try nodes.append(.{
6977 .address = sect.addr + sect.size,6238// .address = sect.addr + sect.size,
6978 .tag = .section_end,6239// .tag = .section_end,
6979 .payload = .{},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,
6980 });6259 });
6981 }6260 }
6982
6983 snapshot.nodes = nodes.toOwnedSlice();
6984
6985 try std.json.stringify(snapshot, .{}, writer);
6986 try writer.writeByte(']');
6987}6261}
69886262
6989fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {6263fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
...@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {...@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {
7104 }6378 }
7105}6379}
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
7114fn logAtoms(self: *MachO) void {6381fn logAtoms(self: *MachO) void {
7115 log.debug("atoms:", .{});6382 log.debug("atoms:", .{});
7116 var it = self.atoms.iterator();6383
7117 while (it.next()) |entry| {6384 const slice = self.sections.slice();
7118 const match = entry.key_ptr.*;6385 for (slice.items(.last_atom)) |last, i| {
7119 var atom = entry.value_ptr.*;6386 var atom = last orelse continue;
6387 const header = slice.items(.header)[i];
71206388
7121 while (atom.prev) |prev| {6389 while (atom.prev) |prev| {
7122 atom = prev;6390 atom = prev;
7123 }6391 }
71246392
7125 const sect = self.getSection(match);6393 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
7126 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
71276394
7128 while (true) {6395 while (true) {
7129 self.logAtom(atom);6396 self.logAtom(atom);
src/link/MachO/Archive.zig+33-25
...@@ -6,19 +6,14 @@ const fs = std.fs;...@@ -6,19 +6,14 @@ const fs = std.fs;
6const log = std.log.scoped(.link);6const log = std.log.scoped(.link);
7const macho = std.macho;7const macho = std.macho;
8const mem = std.mem;8const mem = std.mem;
9const fat = @import("fat.zig");
109
11const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
12const Object = @import("Object.zig");11const Object = @import("Object.zig");
1312
14file: fs.File,13file: fs.File,
14fat_offset: u64,
15name: []const u8,15name: []const u8,
1616header: ar_hdr = undefined,
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,
2217
23/// Parsed table of contents.18/// Parsed table of contents.
24/// Each symbol name points to a list of all definition19/// Each symbol name points to a list of all definition
...@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {...@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
103 allocator.free(self.name);98 allocator.free(self.name);
104}99}
105100
106pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {101pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !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
111 const magic = try reader.readBytesNoEof(SARMAG);102 const magic = try reader.readBytesNoEof(SARMAG);
112 if (!mem.eql(u8, &magic, ARMAG)) {103 if (!mem.eql(u8, &magic, ARMAG)) {
113 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });104 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...@@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
115 }106 }
116107
117 self.header = try reader.readStruct(ar_hdr);108 self.header = try reader.readStruct(ar_hdr);
118 if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {109 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
119 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });110 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
111 ARFMAG,
112 self.header.ar_fmag,
113 });
120 return error.NotArchive;114 return error.NotArchive;
121 }115 }
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);
124 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });119 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
125 defer allocator.free(embedded_name);120 defer allocator.free(embedded_name);
126121
127 try self.parseTableOfContents(allocator, reader);122 try self.parseTableOfContents(allocator, reader);
128 try reader.context.seekTo(0);
129}123}
130124
131fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {125fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
132 const name_or_length = try header.nameOrLength();
133 var name: []u8 = undefined;126 var name: []u8 = undefined;
134 switch (name_or_length) {127 switch (name_or_length) {
135 .Name => |n| {128 .Name => |n| {
...@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !...@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
187 }180 }
188}181}
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 {
191 const reader = self.file.reader();189 const reader = self.file.reader();
192 try reader.context.seekTo(offset + self.library_offset);190 try reader.context.seekTo(self.fat_offset + offset);
193191
194 const object_header = try reader.readStruct(ar_hdr);192 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...@@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
198 return error.MalformedArchive;196 return error.MalformedArchive;
199 }197 }
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);
202 defer allocator.free(object_name);201 defer allocator.free(object_name);
203202
204 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });203 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...@@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
209 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });208 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
210 };209 };
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
212 var object = Object{222 var object = Object{
213 .file = try fs.cwd().openFile(self.name, .{}),
214 .name = name,223 .name = name,
215 .file_offset = @intCast(u32, try reader.context.getPos()),224 .mtime = try self.header.date(),
216 .mtime = try self.header.?.date(),225 .contents = contents,
217 };226 };
218227
219 try object.parse(allocator, cpu_arch);228 try object.parse(allocator, cpu_arch);
220 try reader.context.seekTo(0);
221229
222 return object;230 return object;
223}231}
src/link/MachO/Atom.zig+21-19
...@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
246 else => {246 else => {
247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
249 log.err(" found {}", .{next});249 log.err(" found {s}", .{@tagName(next)});
250 return error.UnexpectedRelocationType;250 return error.UnexpectedRelocationType;
251 },251 },
252 }252 }
...@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
285 else => {285 else => {
286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
287 log.err(" expected ARM64_RELOC_UNSIGNED", .{});287 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 });
289 return error.UnexpectedRelocationType;291 return error.UnexpectedRelocationType;
290 },292 },
291 },293 },
...@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
294 else => {296 else => {
295 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});297 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
296 log.err(" expected X86_64_RELOC_UNSIGNED", .{});298 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 });
298 return error.UnexpectedRelocationType;302 return error.UnexpectedRelocationType;
299 },303 },
300 },304 },
...@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
309 const sect_id = @intCast(u16, rel.r_symbolnum - 1);313 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
310 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {314 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
311 const sect = object.getSourceSection(sect_id);315 const sect = object.getSourceSection(sect_id);
312 const match = (try context.macho_file.getMatchingSection(sect)) orelse316 const match = (try context.macho_file.getOutputSection(sect)) orelse
313 unreachable;317 unreachable;
314 const sym_index = @intCast(u32, object.symtab.items.len);318 const sym_index = @intCast(u32, object.symtab.items.len);
315 try object.symtab.append(gpa, .{319 try object.symtab.append(gpa, .{
316 .n_strx = 0,320 .n_strx = 0,
317 .n_type = macho.N_SECT,321 .n_type = macho.N_SECT,
318 .n_sect = context.macho_file.getSectionOrdinal(match),322 .n_sect = match + 1,
319 .n_desc = 0,323 .n_desc = 0,
320 .n_value = sect.addr,324 .n_value = sect.addr,
321 });325 });
...@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(...@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
459 });463 });
460 } else {464 } else {
461 const source_sym = self.getSymbol(context.macho_file);465 const source_sym = self.getSymbol(context.macho_file);
462 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
463 const sect = context.macho_file.getSection(match);467 const header = section.header;
464 const sect_type = sect.type_();468 const segment_index = section.segment_index;
469 const sect_type = header.@"type"();
465470
466 const should_rebase = rebase: {471 const should_rebase = rebase: {
467 if (rel.r_length != 3) break :rebase false;472 if (rel.r_length != 3) break :rebase false;
...@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(...@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
470 // that the segment is writable should be enough here.475 // that the segment is writable should be enough here.
471 const is_right_segment = blk: {476 const is_right_segment = blk: {
472 if (context.macho_file.data_segment_cmd_index) |idx| {477 if (context.macho_file.data_segment_cmd_index) |idx| {
473 if (match.seg == idx) {478 if (segment_index == idx) {
474 break :blk true;479 break :blk true;
475 }480 }
476 }481 }
477 if (context.macho_file.data_const_segment_cmd_index) |idx| {482 if (context.macho_file.data_const_segment_cmd_index) |idx| {
478 if (match.seg == idx) {483 if (segment_index == idx) {
479 break :blk true;484 break :blk true;
480 }485 }
481 }486 }
...@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {...@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
524 if (context.macho_file.stubs_table.contains(target)) return;529 if (context.macho_file.stubs_table.contains(target)) return;
525530
526 const stub_index = try context.macho_file.allocateStubEntry(target);531 const stub_index = try context.macho_file.allocateStubEntry(target);
532
527 const stub_helper_atom = try context.macho_file.createStubHelperAtom();533 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
528 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);534 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
529 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);535 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 {...@@ -565,9 +571,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
565 };571 };
566 const is_tlv = is_tlv: {572 const is_tlv = is_tlv: {
567 const source_sym = self.getSymbol(macho_file);573 const source_sym = self.getSymbol(macho_file);
568 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);574 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
569 const sect = macho_file.getSection(match);575 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
571 };576 };
572 const target_addr = blk: {577 const target_addr = blk: {
573 const target_atom = rel.getTargetAtom(macho_file) orelse {578 const target_atom = rel.getTargetAtom(macho_file) orelse {
...@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
597 // * wrt to __thread_data if defined, then602 // * wrt to __thread_data if defined, then
598 // * wrt to __thread_bss603 // * wrt to __thread_bss
599 const sect_id: u16 = sect_id: {604 const sect_id: u16 = sect_id: {
600 if (macho_file.tlv_data_section_index) |i| {605 if (macho_file.getSectionByName("__DATA", "__thread_data")) |i| {
601 break :sect_id i;606 break :sect_id i;
602 } else if (macho_file.tlv_bss_section_index) |i| {607 } else if (macho_file.getSectionByName("__DATA", "__thread_bss")) |i| {
603 break :sect_id i;608 break :sect_id i;
604 } else {609 } else {
605 log.err("threadlocal variables present but no initializer sections found", .{});610 log.err("threadlocal variables present but no initializer sections found", .{});
...@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
608 return error.FailedToResolveRelocationTarget;613 return error.FailedToResolveRelocationTarget;
609 }614 }
610 };615 };
611 break :base_address macho_file.getSection(.{616 break :base_address macho_file.sections.items(.header)[sect_id].addr;
612 .seg = macho_file.data_segment_cmd_index.?,
613 .sect = sect_id,
614 }).addr;
615 } else 0;617 } else 0;
616 break :blk target_sym.n_value - base_address;618 break :blk target_sym.n_value - base_address;
617 };619 };
src/link/MachO/CodeSignature.zig+7-5
...@@ -252,7 +252,7 @@ pub const WriteOpts = struct {...@@ -252,7 +252,7 @@ pub const WriteOpts = struct {
252 file: fs.File,252 file: fs.File,
253 exec_seg_base: u64,253 exec_seg_base: u64,
254 exec_seg_limit: u64,254 exec_seg_limit: u64,
255 code_sig_cmd: macho.linkedit_data_command,255 file_size: u32,
256 output_mode: std.builtin.OutputMode,256 output_mode: std.builtin.OutputMode,
257};257};
258258
...@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(...@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
274 self.code_directory.inner.execSegBase = opts.exec_seg_base;274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;276 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;277 self.code_directory.inner.codeLimit = opts.file_size;
278 self.code_directory.inner.codeLimit = 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
282 var buffer = try allocator.alloc(u8, self.page_size);281 var buffer = try allocator.alloc(u8, self.page_size);
283 defer allocator.free(buffer);282 defer allocator.free(buffer);
...@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(...@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
289 var i: usize = 0;288 var i: usize = 0;
290 while (i < total_pages) : (i += 1) {289 while (i < total_pages) : (i += 1) {
291 const fstart = i * self.page_size;290 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;
293 const len = try opts.file.preadAll(buffer, fstart);295 const len = try opts.file.preadAll(buffer, fstart);
294 assert(fsize <= len);296 assert(fsize <= len);
295297
src/link/MachO/DebugSymbols.zig+199-331
...@@ -25,35 +25,18 @@ base: *MachO,...@@ -25,35 +25,18 @@ base: *MachO,
25dwarf: Dwarf,25dwarf: Dwarf,
26file: fs.File,26file: fs.File,
2727
28/// Table of all load commands28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
29load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},29sections: std.ArrayListUnmanaged(macho.section_64) = .{},
30/// __PAGEZERO segment30
31pagezero_segment_cmd_index: ?u16 = null,31linkedit_segment_cmd_index: ?u8 = null,
32/// __TEXT segment32dwarf_segment_cmd_index: ?u8 = null,
33text_segment_cmd_index: ?u16 = null,33
34/// __DATA_CONST segment34debug_info_section_index: ?u8 = null,
35data_const_segment_cmd_index: ?u16 = null,35debug_abbrev_section_index: ?u8 = null,
36/// __DATA segment36debug_str_section_index: ?u8 = null,
37data_segment_cmd_index: ?u16 = null,37debug_aranges_section_index: ?u8 = null,
38/// __LINKEDIT segment38debug_line_section_index: ?u8 = null,
39linkedit_segment_cmd_index: ?u16 = null,39
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,
57debug_string_table_dirty: bool = false,40debug_string_table_dirty: bool = false,
58debug_abbrev_section_dirty: bool = false,41debug_abbrev_section_dirty: bool = false,
59debug_aranges_section_dirty: bool = false,42debug_aranges_section_dirty: bool = false,
...@@ -78,98 +61,44 @@ pub const Reloc = struct {...@@ -78,98 +61,44 @@ pub const Reloc = struct {
78/// You must call this function *after* `MachO.populateMissingMetadata()`61/// You must call this function *after* `MachO.populateMissingMetadata()`
79/// has been called to get a viable debug symbols output.62/// has been called to get a viable debug symbols output.
80pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {63pub 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
137 if (self.linkedit_segment_cmd_index == null) {64 if (self.linkedit_segment_cmd_index == null) {
138 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);65 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
139 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;66 log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{
140 var cmd = try self.copySegmentCommand(allocator, base_cmd);67 self.base.page_size,
68 self.base.page_size * 2,
69 });
141 // TODO this needs reworking70 // TODO this needs reworking
142 cmd.inner.vmsize = self.base.page_size;71 try self.segments.append(allocator, .{
143 cmd.inner.fileoff = self.base.page_size;72 .segname = makeStaticString("__LINKEDIT"),
144 cmd.inner.filesize = self.base.page_size;73 .vmaddr = self.base.page_size,
145 try self.load_commands.append(allocator, .{ .segment = cmd });74 .vmsize = self.base.page_size,
146 self.load_commands_dirty = true;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 });
147 }81 }
14882
149 if (self.dwarf_segment_cmd_index == null) {83 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.?];
153 const ideal_size: u16 = 200 + 128 + 160 + 250;87 const ideal_size: u16 = 200 + 128 + 160 + 250;
154 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);88 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
155 const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;89 const fileoff = linkedit.fileoff + linkedit.filesize;
156 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;90 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
15791
158 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });92 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
15993
160 try self.load_commands.append(allocator, .{94 try self.segments.append(allocator, .{
161 .segment = .{95 .segname = makeStaticString("__DWARF"),
162 .inner = .{96 .vmaddr = vmaddr,
163 .segname = makeStaticString("__DWARF"),97 .vmsize = needed_size,
164 .vmaddr = vmaddr,98 .fileoff = fileoff,
165 .vmsize = needed_size,99 .filesize = needed_size,
166 .fileoff = fileoff,100 .cmdsize = @sizeOf(macho.segment_command_64),
167 .filesize = needed_size,
168 .cmdsize = @sizeOf(macho.segment_command_64),
169 },
170 },
171 });101 });
172 self.load_commands_dirty = true;
173 }102 }
174103
175 if (self.debug_str_section_index == null) {104 if (self.debug_str_section_index == null) {
...@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
203 }132 }
204}133}
205134
206fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {135fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
207 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;136 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
208 var sect = macho.section_64{137 var sect = macho.section_64{
209 .sectname = makeStaticString(sectname),138 .sectname = makeStaticString(sectname),
210 .segname = seg.inner.segname,139 .segname = segment.segname,
211 .size = @intCast(u32, size),140 .size = @intCast(u32, size),
212 .@"align" = alignment,141 .@"align" = alignment,
213 };142 };
214 const alignment_pow_2 = try math.powi(u32, 2, alignment);143 const alignment_pow_2 = try math.powi(u32, 2, alignment);
215 const off = self.findFreeSpace(size, alignment_pow_2);144 const off = self.findFreeSpace(size, alignment_pow_2);
216145
217 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand146 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
218147
219 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{148 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
220 sect.segName(),149 sect.segName(),
...@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
223 off + size,152 off + size,
224 });153 });
225154
226 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;155 sect.addr = segment.vmaddr + off - segment.fileoff;
227 sect.offset = @intCast(u32, off);156 sect.offset = @intCast(u32, off);
228157
229 const index = @intCast(u16, seg.sections.items.len);158 const index = @intCast(u8, self.sections.items.len);
230 try seg.sections.append(self.base.base.allocator, sect);159 try self.sections.append(self.base.base.allocator, sect);
231 seg.inner.cmdsize += @sizeOf(macho.section_64);160 segment.cmdsize += @sizeOf(macho.section_64);
232 seg.inner.nsects += 1;161 segment.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;
243162
244 return index;163 return index;
245}164}
246165
247fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {166fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
248 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
249 const end = start + padToIdeal(size);167 const end = start + padToIdeal(size);
250 for (seg.sections.items) |section| {168 for (self.sections.items) |section| {
251 const increased_size = padToIdeal(section.size);169 const increased_size = padToIdeal(section.size);
252 const test_end = section.offset + increased_size;170 const test_end = section.offset + increased_size;
253 if (end > section.offset and start < test_end) {171 if (end > section.offset and start < test_end) {
...@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {...@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
258}176}
259177
260pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {178pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
261 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;179 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
262 var offset: u64 = seg.inner.fileoff;180 var offset: u64 = segment.fileoff;
263 while (self.detectAllocCollision(offset, object_size)) |item_end| {181 while (self.detectAllocCollision(offset, object_size)) |item_end| {
264 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);182 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
265 }183 }
...@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
296 break :blk got_entry.getName(self.base);214 break :blk got_entry.getName(self.base);
297 },215 },
298 };216 };
299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;217 const sect = &self.sections.items[self.debug_info_section_index.?];
300 const sect = &seg.sections.items[self.debug_info_section_index.?];
301 const file_offset = sect.offset + reloc.offset;218 const file_offset = sect.offset + reloc.offset;
302 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{219 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
303 reloc.target,220 reloc.target,
...@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
311228
312 if (self.debug_abbrev_section_dirty) {229 if (self.debug_abbrev_section_dirty) {
313 try self.dwarf.writeDbgAbbrev(&self.base.base);230 try self.dwarf.writeDbgAbbrev(&self.base.base);
314 self.load_commands_dirty = true;
315 self.debug_abbrev_section_dirty = false;231 self.debug_abbrev_section_dirty = false;
316 }232 }
317233
318 if (self.debug_info_header_dirty) {234 if (self.debug_info_header_dirty) {
319 // Currently only one compilation unit is supported, so the address range is simply235 // Currently only one compilation unit is supported, so the address range is simply
320 // identical to the main program header virtual address and memory size.236 // 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;237 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
322 const text_section = text_segment.sections.items[self.text_section_index.?];
323 const low_pc = text_section.addr;238 const low_pc = text_section.addr;
324 const high_pc = text_section.addr + text_section.size;239 const high_pc = text_section.addr + text_section.size;
325 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);240 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...@@ -329,10 +244,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
329 if (self.debug_aranges_section_dirty) {244 if (self.debug_aranges_section_dirty) {
330 // Currently only one compilation unit is supported, so the address range is simply245 // Currently only one compilation unit is supported, so the address range is simply
331 // identical to the main program header virtual address and memory size.246 // 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;247 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
333 const text_section = text_segment.sections.items[self.text_section_index.?];
334 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);248 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
335 self.load_commands_dirty = true;
336 self.debug_aranges_section_dirty = false;249 self.debug_aranges_section_dirty = false;
337 }250 }
338251
...@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
342 }255 }
343256
344 {257 {
345 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;258 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
346 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];259 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
347 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {260 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
348 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);261 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
349 const needed_size = self.dwarf.strtab.items.len;262 const needed_size = self.dwarf.strtab.items.len;
...@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
351 if (needed_size > allocated_size) {264 if (needed_size > allocated_size) {
352 debug_strtab_sect.size = 0; // free the space265 debug_strtab_sect.size = 0; // free the space
353 const new_offset = self.findFreeSpace(needed_size, 1);266 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;
355 debug_strtab_sect.offset = @intCast(u32, new_offset);268 debug_strtab_sect.offset = @intCast(u32, new_offset);
356 }269 }
357 debug_strtab_sect.size = @intCast(u32, needed_size);270 debug_strtab_sect.size = @intCast(u32, needed_size);
...@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
362 });275 });
363276
364 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);277 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
365 self.load_commands_dirty = true;
366 self.debug_string_table_dirty = false;278 self.debug_string_table_dirty = false;
367 }279 }
368 }280 }
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);
370 self.updateDwarfSegment();288 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
377 assert(!self.debug_abbrev_section_dirty);304 assert(!self.debug_abbrev_section_dirty);
378 assert(!self.debug_aranges_section_dirty);305 assert(!self.debug_aranges_section_dirty);
379 assert(!self.debug_string_table_dirty);306 assert(!self.debug_string_table_dirty);
380}307}
381308
382pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {309pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
383 for (self.load_commands.items) |*lc| {310 self.segments.deinit(allocator);
384 lc.deinit(allocator);311 self.sections.deinit(allocator);
385 }
386 self.load_commands.deinit(allocator);
387 self.dwarf.deinit();312 self.dwarf.deinit();
388 self.strtab.deinit(allocator);313 self.strtab.deinit(allocator);
389 self.relocs.deinit(allocator);314 self.relocs.deinit(allocator);
...@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {...@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
402 }327 }
403}328}
404329
405fn copySegmentCommand(330fn updateDwarfSegment(self: *DebugSymbols) void {
406 self: *DebugSymbols,331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
407 allocator: Allocator,332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
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 }
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;
448 }339 }
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
456 var max_offset: u64 = 0;341 var max_offset: u64 = 0;
457 for (dwarf_segment.sections.items) |sect| {342 for (self.sections.items) |*sect| {
343 sect.addr += diff;
458 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{344 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
459 sect.segName(),345 sect.segName(),
460 sect.sectName(),346 sect.sectName(),
...@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {...@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
468 }354 }
469 }355 }
470356
471 const file_size = max_offset - dwarf_segment.inner.fileoff;357 const file_size = max_offset - dwarf_segment.fileoff;
472 log.debug("__DWARF size 0x{x}", .{file_size});358 log.debug("__DWARF size 0x{x}", .{file_size});
473359
474 if (file_size != dwarf_segment.inner.filesize) {360 if (file_size != dwarf_segment.filesize) {
475 dwarf_segment.inner.filesize = file_size;361 dwarf_segment.filesize = file_size;
476 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
477 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
478 }
479 self.load_commands_dirty = true;
480 }363 }
481}364}
482365
483/// Writes all load commands and section headers.366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {
484fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {367 // Write segment/section headers from the binary file first.
485 if (!self.load_commands_dirty) return;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;385 if (out_seg.nsects == 0 and
488 for (self.load_commands.items) |lc| {386 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
489 sizeofcmds += lc.cmdsize();387 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
490 }
491388
492 var buffer = try allocator.alloc(u8, sizeofcmds);389 try writer.writeStruct(out_seg);
493 defer allocator.free(buffer);390 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
494 var fib = std.io.fixedBufferStream(buffer);391 if (header.size == 0) continue;
495 const writer = fib.writer();392 var out_header = header;
496 for (self.load_commands.items) |lc| {393 out_header.offset = 0;
497 try lc.write(writer);394 try writer.writeStruct(out_header);
498 }395 }
499396
500 const off = @sizeOf(macho.mach_header_64);397 ncmds.* += 1;
501 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });398 }
502 try self.file.pwriteAll(buffer, off);399 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
503 self.load_commands_dirty = false;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 }
504}407}
505408
506fn writeHeader(self: *DebugSymbols) !void {409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
507 var header: macho.mach_header_64 = .{};410 var header: macho.mach_header_64 = .{};
508 header.filetype = macho.MH_DSYM;411 header.filetype = macho.MH_DSYM;
509412
...@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {...@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {
519 else => return error.UnsupportedCpuArchitecture,422 else => return error.UnsupportedCpuArchitecture,
520 }423 }
521424
522 header.ncmds = @intCast(u32, self.load_commands.items.len);425 header.ncmds = ncmds;
523 header.sizeofcmds = 0;426 header.sizeofcmds = sizeofcmds;
524
525 for (self.load_commands.items) |cmd| {
526 header.sizeofcmds += cmd.cmdsize();
527 }
528427
529 log.debug("writing Mach-O header {}", .{header});428 log.debug("writing Mach-O header {}", .{header});
530429
...@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {...@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {
532}431}
533432
534pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {433pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
535 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
536 assert(start >= seg.inner.fileoff);435 assert(start >= seg.fileoff);
537 var min_pos: u64 = std.math.maxInt(u64);436 var min_pos: u64 = std.math.maxInt(u64);
538 for (seg.sections.items) |section| {437 for (self.sections.items) |section| {
539 if (section.offset <= start) continue;438 if (section.offset <= start) continue;
540 if (section.offset < min_pos) min_pos = section.offset;439 if (section.offset < min_pos) min_pos = section.offset;
541 }440 }
542 return min_pos - start;441 return min_pos - start;
543}442}
544443
545fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {444fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !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 {
588 const tracy = trace(@src());445 const tracy = trace(@src());
589 defer tracy.end();446 defer tracy.end();
590447
591 try self.writeSymbolTable();448 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
592 try self.writeStringTable();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;452 var symtab_cmd = macho.symtab_command{
595 const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);453 .cmdsize = @sizeOf(macho.symtab_command),
596 seg.inner.filesize = aligned_size;454 .symoff = 0,
597 seg.inner.vmsize = aligned_size;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;
598}467}
599468
600fn writeSymbolTable(self: *DebugSymbols) !void {469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601 const tracy = trace(@src());470 const tracy = trace(@src());
602 defer tracy.end();471 defer tracy.end();
603472
604 const gpa = self.base.base.allocator;473 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
609 var locals = std.ArrayList(macho.nlist_64).init(gpa);475 var locals = std.ArrayList(macho.nlist_64).init(gpa);
610 defer locals.deinit();476 defer locals.deinit();
...@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
634500
635 const nlocals = locals.items.len;501 const nlocals = locals.items.len;
636 const nexports = exports.items.len;502 const nexports = exports.items.len;
637 const locals_off = symtab.symoff;503 const nsyms = nlocals + nexports;
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);
641504
642 symtab.nsyms = @intCast(u32, nlocals + nexports);505 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
643 const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);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) {
646 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);510 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
647 const diff = @intCast(u32, aligned_size - seg.inner.filesize);511 const diff = @intCast(u32, aligned_size - seg.filesize);
648 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
649 seg.inner.filesize = aligned_size;513 seg.filesize = aligned_size;
650514
651 try MachO.copyRangeAllOverlappingAlloc(515 try MachO.copyRangeAllOverlappingAlloc(
652 self.base.base.allocator,516 self.base.base.allocator,
653 self.file,517 self.file,
654 dwarf_seg.inner.fileoff,518 dwarf_seg.fileoff,
655 dwarf_seg.inner.fileoff + diff,519 dwarf_seg.fileoff + diff,
656 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,520 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
657 );521 );
658522
659 const old_seg_fileoff = dwarf_seg.inner.fileoff;523 const old_seg_fileoff = dwarf_seg.fileoff;
660 dwarf_seg.inner.fileoff += diff;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| {
665 const old_offset = sect.offset;529 const old_offset = sect.offset;
666 sect.offset += diff;530 sect.offset += diff;
667531
...@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
674 }538 }
675 }539 }
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
677 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });549 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
678 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);550 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
679551
680 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });552 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
681 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);553 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
682
683 self.load_commands_dirty = true;
684}554}
685555
686fn writeStringTable(self: *DebugSymbols) !void {556fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
687 const tracy = trace(@src());557 const tracy = trace(@src());
688 defer tracy.end();558 defer tracy.end();
689559
690 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;560 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
691 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;561 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
692 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));562 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
693 symtab.stroff = symtab.symoff + symtab_size;563 lc.stroff = @intCast(u32, offset);
694564
695 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));565 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) {568 if (symtab_size + needed_size > seg.filesize) {
699 const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);569 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
700 const diff = @intCast(u32, aligned_size - seg.inner.filesize);570 const diff = @intCast(u32, aligned_size - seg.filesize);
701 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
702 seg.inner.filesize = aligned_size;572 seg.filesize = aligned_size;
703573
704 try MachO.copyRangeAllOverlappingAlloc(574 try MachO.copyRangeAllOverlappingAlloc(
705 self.base.base.allocator,575 self.base.base.allocator,
706 self.file,576 self.file,
707 dwarf_seg.inner.fileoff,577 dwarf_seg.fileoff,
708 dwarf_seg.inner.fileoff + diff,578 dwarf_seg.fileoff + diff,
709 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,579 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
710 );580 );
711581
712 const old_seg_fileoff = dwarf_seg.inner.fileoff;582 const old_seg_fileoff = dwarf_seg.fileoff;
713 dwarf_seg.inner.fileoff += diff;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| {
718 const old_offset = sect.offset;588 const old_offset = sect.offset;
719 sect.offset += diff;589 sect.offset += diff;
720590
...@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
727 }597 }
728 }598 }
729599
730 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });600 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
731
732 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
733601
734 self.load_commands_dirty = true;602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
735}603}
src/link/MachO/Dylib.zig+53-106
...@@ -13,23 +13,9 @@ const fat = @import("fat.zig");...@@ -13,23 +13,9 @@ const fat = @import("fat.zig");
13const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
14const CrossTarget = std.zig.CrossTarget;14const CrossTarget = std.zig.CrossTarget;
15const LibStub = @import("../tapi.zig").LibStub;15const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommandIterator = macho.LoadCommandIterator;
16const MachO = @import("../MachO.zig");17const 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
33id: ?Id = null,19id: ?Id = null,
34weak: bool = false,20weak: bool = false,
3521
...@@ -53,16 +39,12 @@ pub const Id = struct {...@@ -53,16 +39,12 @@ pub const Id = struct {
53 };39 };
54 }40 }
5541
56 pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {42 pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !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
61 return Id{43 return Id{
62 .name = name,44 .name = try allocator.dupe(u8, name),
63 .timestamp = dylib.timestamp,45 .timestamp = lc.dylib.timestamp,
64 .current_version = dylib.current_version,46 .current_version = lc.dylib.current_version,
65 .compatibility_version = dylib.compatibility_version,47 .compatibility_version = lc.dylib.compatibility_version,
66 };48 };
67 }49 }
6850
...@@ -126,125 +108,89 @@ pub const Id = struct {...@@ -126,125 +108,89 @@ pub const Id = struct {
126};108};
127109
128pub fn deinit(self: *Dylib, allocator: Allocator) void {110pub 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
134 for (self.symbols.keys()) |key| {111 for (self.symbols.keys()) |key| {
135 allocator.free(key);112 allocator.free(key);
136 }113 }
137 self.symbols.deinit(allocator);114 self.symbols.deinit(allocator);
138
139 allocator.free(self.name);
140
141 if (self.id) |*id| {115 if (self.id) |*id| {
142 id.deinit(allocator);116 id.deinit(allocator);
143 }117 }
144}118}
145119
146pub fn parse(120pub fn parseFromBinary(
147 self: *Dylib,121 self: *Dylib,
148 allocator: Allocator,122 allocator: Allocator,
149 cpu_arch: std.Target.Cpu.Arch,123 cpu_arch: std.Target.Cpu.Arch,
150 dylib_id: u16,124 dylib_id: u16,
151 dependent_libs: anytype,125 dependent_libs: anytype,
126 name: []const u8,
127 data: []align(@alignOf(u64)) const u8,
152) !void {128) !void {
153 log.debug("parsing shared library '{s}'", .{self.name});129 var stream = std.io.fixedBufferStream(data);
154130 const reader = stream.reader();
155 self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
156131
157 try self.file.seekTo(self.library_offset);132 log.debug("parsing shared library '{s}'", .{name});
158133
159 var reader = self.file.reader();134 const header = try reader.readStruct(macho.mach_header_64);
160 self.header = try reader.readStruct(macho.mach_header_64);
161135
162 if (self.header.?.filetype != macho.MH_DYLIB) {136 if (header.filetype != macho.MH_DYLIB) {
163 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });137 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
164 return error.NotDylib;138 return error.NotDylib;
165 }139 }
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
169 if (this_arch != cpu_arch) {143 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 });
171 return error.MismatchedCpuArchitecture;148 return error.MismatchedCpuArchitecture;
172 }149 }
173150
174 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);151 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
175 try self.parseId(allocator);152 var it = LoadCommandIterator{
176 try self.parseSymbols(allocator);153 .ncmds = header.ncmds,
177}154 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
178155 };
179fn readLoadCommands(156 while (it.next()) |cmd| {
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);
193 switch (cmd.cmd()) {157 switch (cmd.cmd()) {
194 .SYMTAB => {158 .SYMTAB => {
195 self.symtab_cmd_index = i;159 const symtab_cmd = cmd.cast(macho.symtab_command).?;
196 },160 const symtab = @ptrCast(
197 .DYSYMTAB => {161 [*]const macho.nlist_64,
198 self.dysymtab_cmd_index = i;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 }
199 },173 },
200 .ID_DYLIB => {174 .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 );
202 },180 },
203 .REEXPORT_DYLIB => {181 .REEXPORT_DYLIB => {
204 if (should_lookup_reexports) {182 if (should_lookup_reexports) {
205 // Parse install_name to dependent dylib.183 // 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 );
207 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });189 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
208 }190 }
209 },191 },
210 else => {192 else => {},
211 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
212 },
213 }193 }
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, {});
248 }194 }
249}195}
250196
...@@ -356,10 +302,11 @@ pub fn parseFromStub(...@@ -356,10 +302,11 @@ pub fn parseFromStub(
356 lib_stub: LibStub,302 lib_stub: LibStub,
357 dylib_id: u16,303 dylib_id: u16,
358 dependent_libs: anytype,304 dependent_libs: anytype,
305 name: []const u8,
359) !void {306) !void {
360 if (lib_stub.inner.len == 0) return error.EmptyStubFile;307 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
364 const umbrella_lib = lib_stub.inner[0];311 const umbrella_lib = lib_stub.inner[0];
365312
src/link/MachO/Object.zig+151-176
...@@ -3,6 +3,7 @@ const Object = @This();...@@ -3,6 +3,7 @@ const Object = @This();
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const dwarf = std.dwarf;
6const fs = std.fs;7const fs = std.fs;
7const io = std.io;8const io = std.io;
8const log = std.log.scoped(.link);9const log = std.log.scoped(.link);
...@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;...@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
1415
15const Allocator = mem.Allocator;16const Allocator = mem.Allocator;
16const Atom = @import("Atom.zig");17const Atom = @import("Atom.zig");
18const LoadCommandIterator = macho.LoadCommandIterator;
17const MachO = @import("../MachO.zig");19const MachO = @import("../MachO.zig");
18const MatchingSection = MachO.MatchingSection;
19const SymbolWithLoc = MachO.SymbolWithLoc;20const SymbolWithLoc = MachO.SymbolWithLoc;
2021
21file: fs.File,
22name: []const u8,22name: []const u8,
23mtime: u64,23mtime: u64,
2424contents: []align(@alignOf(u64)) const u8,
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,
3125
32header: macho.mach_header_64 = undefined,26header: macho.mach_header_64 = undefined,
3327in_symtab: []const macho.nlist_64 = undefined,
34load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},28in_strtab: []const u8 = undefined,
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,
5029
51symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},30symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
52strtab: []const u8 = &.{},31sections: std.ArrayListUnmanaged(macho.section_64) = .{},
53data_in_code_entries: []const macho.data_in_code_entry = &.{},
5432
55sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},33sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5634
...@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},...@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
61atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},39atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
6240
63pub fn deinit(self: *Object, gpa: Allocator) void {41pub 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);
69 self.symtab.deinit(gpa);42 self.symtab.deinit(gpa);
43 self.sections.deinit(gpa);
70 self.sections_as_symbols.deinit(gpa);44 self.sections_as_symbols.deinit(gpa);
71 self.atom_by_index_table.deinit(gpa);45 self.atom_by_index_table.deinit(gpa);
7246
...@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
77 self.managed_atoms.deinit(gpa);51 self.managed_atoms.deinit(gpa);
7852
79 gpa.free(self.name);53 gpa.free(self.name);
54 gpa.free(self.contents);
80}55}
8156
82pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {57pub 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
87 var stream = std.io.fixedBufferStream(self.contents);58 var stream = std.io.fixedBufferStream(self.contents);
88 const reader = stream.reader();59 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
95 self.header = try reader.readStruct(macho.mach_header_64);61 self.header = try reader.readStruct(macho.mach_header_64);
62
96 if (self.header.filetype != macho.MH_OBJECT) {63 if (self.header.filetype != macho.MH_OBJECT) {
97 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{64 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
98 macho.MH_OBJECT,65 macho.MH_OBJECT,
...@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
110 },77 },
111 };78 };
112 if (this_arch != cpu_arch) {79 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 });
114 return error.MismatchedCpuArchitecture;84 return error.MismatchedCpuArchitecture;
115 }85 }
11686
117 try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);87 var it = LoadCommandIterator{
11888 .ncmds = self.header.ncmds,
119 var i: u16 = 0;89 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
120 while (i < self.header.ncmds) : (i += 1) {90 };
121 var cmd = try macho.LoadCommand.read(allocator, reader);91 while (it.next()) |cmd| {
122 switch (cmd.cmd()) {92 switch (cmd.cmd()) {
123 .SEGMENT_64 => {93 .SEGMENT_64 => {
124 self.segment_cmd_index = i;94 const segment = cmd.cast(macho.segment_command_64).?;
125 var seg = cmd.segment;95 try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
126 for (seg.sections.items) |*sect, j| {96 for (cmd.getSections()) |sect| {
127 const index = @intCast(u16, j);97 self.sections.appendAssumeCapacity(sect);
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 }
154 }98 }
155
156 seg.inner.fileoff += file_offset;
157 },99 },
158 .SYMTAB => {100 .SYMTAB => {
159 self.symtab_cmd_index = i;101 const symtab = cmd.cast(macho.symtab_command).?;
160 cmd.symtab.symoff += file_offset;102 self.in_symtab = @ptrCast(
161 cmd.symtab.stroff += file_offset;103 [*]const macho.nlist_64,
162 },104 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
163 .DYSYMTAB => {105 )[0..symtab.nsyms];
164 self.dysymtab_cmd_index = i;106 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
165 },107 try self.symtab.appendSlice(allocator, self.in_symtab);
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())});
175 },108 },
109 else => {},
176 }110 }
177 self.load_commands.appendAssumeCapacity(cmd);
178 }111 }
179
180 try self.parseSymtab(allocator);
181}112}
182113
183const Context = struct {114const Context = struct {
184 symtab: []const macho.nlist_64,115 object: *const Object,
185 strtab: []const u8,
186};116};
187117
188const SymbolAtIndex = struct {118const SymbolAtIndex = struct {
189 index: u32,119 index: u32,
190120
191 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {121 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
192 return ctx.symtab[self.index];122 return ctx.object.getSourceSymbol(self.index).?;
193 }123 }
194124
195 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {125 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
196 const sym = self.getSymbol(ctx);126 const sym = self.getSymbol(ctx);
197 assert(sym.n_strx < ctx.strtab.len);127 return ctx.object.getString(sym.n_strx);
198 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
199 }128 }
200129
201 /// Returns whether lhs is less than rhs by allocated address in object file.130 /// Returns whether lhs is less than rhs by allocated address in object file.
...@@ -285,6 +214,23 @@ fn filterRelocs(...@@ -285,6 +214,23 @@ fn filterRelocs(
285 return relocs[start..end];214 return relocs[start..end];
286}215}
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
288/// Splits object into atoms assuming one-shot linking mode.234/// Splits object into atoms assuming one-shot linking mode.
289pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {235pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
290 assert(macho_file.mode == .one_shot);236 assert(macho_file.mode == .one_shot);
...@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
293 defer tracy.end();239 defer tracy.end();
294240
295 const gpa = macho_file.base.allocator;241 const gpa = macho_file.base.allocator;
296 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
297242
298 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });243 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)...@@ -302,13 +247,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
302 // the GO compiler does not necessarily respect that therefore we sort immediately by type247 // the GO compiler does not necessarily respect that therefore we sort immediately by type
303 // and address within.248 // and address within.
304 const context = Context{249 const context = Context{
305 .symtab = self.getSourceSymtab(),250 .object = self,
306 .strtab = self.strtab,
307 };251 };
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);
309 defer sorted_all_syms.deinit();253 defer sorted_all_syms.deinit();
310254
311 for (context.symtab) |_, index| {255 for (self.in_symtab) |_, index| {
312 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });256 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
313 }257 }
314258
...@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
320264
321 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we265 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
322 // have to infer the start of undef section in the symtab ourselves.266 // have to infer the start of undef section in the symtab ourselves.
323 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {267 const iundefsym = blk: {
324 const dysymtab = self.load_commands.items[cmd_index].dysymtab;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 };
325 break :blk dysymtab.iundefsym;276 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;
333 };277 };
334278
335 // We only care about defined symbols, so filter every other out.279 // We only care about defined symbols, so filter every other out.
336 const sorted_syms = sorted_all_syms.items[0..iundefsym];280 const sorted_syms = sorted_all_syms.items[0..iundefsym];
337 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;281 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| {
340 const sect_id = @intCast(u8, id);284 const sect_id = @intCast(u8, id);
341 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });285 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
342286
343 // Get matching segment/section in the final artifact.287 // 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 {
345 log.debug(" unhandled section", .{});289 log.debug(" unhandled section", .{});
346 continue;290 continue;
347 };291 };
348292
349 log.debug(" output sect({d}, '{s},{s}')", .{293 log.debug(" output sect({d}, '{s},{s}')", .{
350 macho_file.getSectionOrdinal(match),294 match + 1,
351 macho_file.getSection(match).segName(),295 macho_file.sections.items(.header)[match].segName(),
352 macho_file.getSection(match).sectName(),296 macho_file.sections.items(.header)[match].sectName(),
353 });297 });
354298
355 const cpu_arch = macho_file.base.options.target.cpu.arch;299 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
361 // Read section's code301 // 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
364 // Read section's list of relocations304 // Read section's list of relocations
365 const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];305 const relocs = @ptrCast(
366 const relocs = mem.bytesAsSlice(306 [*]const macho.relocation_info,
367 macho.relocation_info,307 @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
368 @alignCast(@alignOf(macho.relocation_info), raw_relocs),308 )[0..sect.nreloc];
369 );
370309
371 // Symbols within this section only.310 // Symbols within this section only.
372 const filtered_syms = filterSymbolsByAddress(311 const filtered_syms = filterSymbolsByAddress(
...@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
387 try self.symtab.append(gpa, .{326 try self.symtab.append(gpa, .{
388 .n_strx = 0,327 .n_strx = 0,
389 .n_type = macho.N_SECT,328 .n_type = macho.N_SECT,
390 .n_sect = macho_file.getSectionOrdinal(match),329 .n_sect = match + 1,
391 .n_desc = 0,330 .n_desc = 0,
392 .n_value = sect.addr,331 .n_value = sect.addr,
393 });332 });
...@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
476 try self.symtab.append(gpa, .{415 try self.symtab.append(gpa, .{
477 .n_strx = 0,416 .n_strx = 0,
478 .n_type = macho.N_SECT,417 .n_type = macho.N_SECT,
479 .n_sect = macho_file.getSectionOrdinal(match),418 .n_sect = match + 1,
480 .n_desc = 0,419 .n_desc = 0,
481 .n_value = addr,420 .n_value = addr,
482 });421 });
...@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
501 try self.symtab.append(gpa, .{440 try self.symtab.append(gpa, .{
502 .n_strx = 0,441 .n_strx = 0,
503 .n_type = macho.N_SECT,442 .n_type = macho.N_SECT,
504 .n_sect = macho_file.getSectionOrdinal(match),443 .n_sect = match + 1,
505 .n_desc = 0,444 .n_desc = 0,
506 .n_value = sect.addr,445 .n_value = sect.addr,
507 });446 });
...@@ -535,21 +474,21 @@ fn createAtomFromSubsection(...@@ -535,21 +474,21 @@ fn createAtomFromSubsection(
535 code: ?[]const u8,474 code: ?[]const u8,
536 relocs: []const macho.relocation_info,475 relocs: []const macho.relocation_info,
537 indexes: []const SymbolAtIndex,476 indexes: []const SymbolAtIndex,
538 match: MatchingSection,477 match: u8,
539 sect: macho.section_64,478 sect: macho.section_64,
540) !*Atom {479) !*Atom {
541 const gpa = macho_file.base.allocator;480 const gpa = macho_file.base.allocator;
542 const sym = self.symtab.items[sym_index];481 const sym = self.symtab.items[sym_index];
543 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);482 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
544 atom.file = object_id;483 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
547 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{486 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
548 sym_index,487 sym_index,
549 self.getString(sym.n_strx),488 self.getString(sym.n_strx),
550 macho_file.getSectionOrdinal(match),489 match + 1,
551 macho_file.getSection(match).segName(),490 macho_file.sections.items(.header)[match].segName(),
552 macho_file.getSection(match).sectName(),491 macho_file.sections.items(.header)[match].sectName(),
553 object_id,492 object_id,
554 });493 });
555494
...@@ -577,7 +516,7 @@ fn createAtomFromSubsection(...@@ -577,7 +516,7 @@ fn createAtomFromSubsection(
577 try atom.contained.ensureTotalCapacity(gpa, indexes.len);516 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
578 for (indexes) |inner_sym_index| {517 for (indexes) |inner_sym_index| {
579 const inner_sym = &self.symtab.items[inner_sym_index.index];518 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;
581 atom.contained.appendAssumeCapacity(.{520 atom.contained.appendAssumeCapacity(.{
582 .sym_index = inner_sym_index.index,521 .sym_index = inner_sym_index.index,
583 .offset = inner_sym.n_value - sym.n_value,522 .offset = inner_sym.n_value - sym.n_value,
...@@ -589,48 +528,84 @@ fn createAtomFromSubsection(...@@ -589,48 +528,84 @@ fn createAtomFromSubsection(
589 return atom;528 return atom;
590}529}
591530
592fn parseSymtab(self: *Object, allocator: Allocator) !void {531pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
593 const index = self.symtab_cmd_index orelse return;532 if (index >= self.in_symtab.len) return null;
594 const symtab = self.load_commands.items[index].symtab;533 return self.in_symtab[index];
595 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
596 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
597}534}
598535
599pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {536pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
600 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};537 assert(index < self.sections.items.len);
601 const symtab = self.load_commands.items[index].symtab;538 return self.sections.items[index];
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 );
608}539}
609540
610pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {541pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
611 const symtab = self.getSourceSymtab();542 var it = LoadCommandIterator{
612 if (index >= symtab.len) return null;543 .ncmds = self.header.ncmds,
613 return symtab[index];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;
614}559}
615560
616pub fn getSourceSection(self: Object, index: u16) macho.section_64 {561fn parseDysymtab(self: Object) ?macho.dysymtab_command {
617 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;562 var it = LoadCommandIterator{
618 assert(index < seg.sections.items.len);563 .ncmds = self.header.ncmds,
619 return seg.sections.items[index];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;
620}574}
621575
622pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {576pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
623 const index = self.data_in_code_cmd_index orelse return null;577 var di = dwarf.DwarfInfo{
624 const data_in_code = self.load_commands.items[index].linkedit_data;578 .endian = .Little,
625 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];579 .debug_info = &[0]u8{},
626 return mem.bytesAsSlice(580 .debug_abbrev = &[0]u8{},
627 macho.data_in_code_entry,581 .debug_str = &[0]u8{},
628 @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),582 .debug_line = &[0]u8{},
629 );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;
630}606}
631607
632pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {608pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
633 const sect = self.getSourceSection(index);
634 const size = math.cast(usize, sect.size) orelse return error.Overflow;609 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{610 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636 sect.segName(),611 sect.segName(),
...@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {...@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
642}617}
643618
644pub fn getString(self: Object, off: u32) []const u8 {619pub fn getString(self: Object, off: u32) []const u8 {
645 assert(off < self.strtab.len);620 assert(off < self.in_strtab.len);
646 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);621 return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
647}622}
648623
649pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {624pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
src/link/MachO/dead_strip.zig+26-24
...@@ -8,7 +8,6 @@ const mem = std.mem;...@@ -8,7 +8,6 @@ const mem = std.mem;
8const Allocator = mem.Allocator;8const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");10const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
1211
13pub fn gcAtoms(macho_file: *MachO) !void {12pub fn gcAtoms(macho_file: *MachO) !void {
14 const gpa = macho_file.base.allocator;13 const gpa = macho_file.base.allocator;
...@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {...@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
25 try prune(arena, alive, macho_file);24 try prune(arena, alive, macho_file);
26}25}
2726
28fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
29 const sect = macho_file.getSectionPtr(match);28 var section = macho_file.sections.get(match);
3029
31 // If we want to enable GC for incremental codepath, we need to take into30 // If we want to enable GC for incremental codepath, we need to take into
32 // account any padding that might have been left here.31 // account any padding that might have been left here.
33 sect.size -= atom.size;32 section.header.size -= atom.size;
3433
35 if (atom.prev) |prev| {34 if (atom.prev) |prev| {
36 prev.next = atom.next;35 prev.next = atom.next;
...@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO...@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
38 if (atom.next) |next| {37 if (atom.next) |next| {
39 next.prev = atom.prev;38 next.prev = atom.prev;
40 } else {39 } else {
41 const last = macho_file.atoms.getPtr(match).?;
42 if (atom.prev) |prev| {40 if (atom.prev) |prev| {
43 last.* = prev;41 section.last_atom = prev;
44 } else {42 } else {
45 // The section will be GCed in the next step.43 // The section will be GCed in the next step.
46 last.* = undefined;44 section.last_atom = null;
47 sect.size = 0;45 section.header.size = 0;
48 }46 }
49 }47 }
48
49 macho_file.sections.set(match, section);
50}50}
5151
52fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {52fn 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...@@ -93,7 +93,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
93 const is_gc_root = blk: {93 const is_gc_root = blk: {
94 if (source_sect.isDontDeadStrip()) break :blk true;94 if (source_sect.isDontDeadStrip()) break :blk true;
95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {96 switch (source_sect.@"type"()) {
97 macho.S_MOD_INIT_FUNC_POINTERS,97 macho.S_MOD_INIT_FUNC_POINTERS,
98 macho.S_MOD_TERM_FUNC_POINTERS,98 macho.S_MOD_TERM_FUNC_POINTERS,
99 => break :blk true,99 => break :blk true,
...@@ -173,19 +173,19 @@ fn mark(...@@ -173,19 +173,19 @@ fn mark(
173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174 // Any section that ends up here will be updated, that is,174 // Any section that ends up here will be updated, that is,
175 // its size and alignment recalculated.175 // 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);
177 var loop: bool = true;177 var loop: bool = true;
178 while (loop) {178 while (loop) {
179 loop = false;179 loop = false;
180180
181 for (macho_file.objects.items) |object| {181 for (macho_file.objects.items) |object| {
182 for (object.getSourceSymtab()) |_, source_index| {182 for (object.in_symtab) |_, source_index| {
183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
184 if (alive.contains(atom)) continue;184 if (alive.contains(atom)) continue;
185185
186 const global = atom.getSymbolWithLoc();186 const global = atom.getSymbolWithLoc();
187 const sym = atom.getSymbolPtr(macho_file);187 const sym = atom.getSymbolPtr(macho_file);
188 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);188 const match = sym.n_sect - 1;
189189
190 if (sym.n_desc == MachO.N_DESC_GCED) continue;190 if (sym.n_desc == MachO.N_DESC_GCED) continue;
191 if (!sym.ext() and !refersDead(atom, macho_file)) continue;191 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...@@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
232232
233 // TODO tombstone233 // TODO tombstone
234 const atom = entry.getAtom(macho_file);234 const atom = entry.getAtom(macho_file);
235 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);235 const match = sym.n_sect - 1;
236 removeAtomFromSection(atom, match, macho_file);236 removeAtomFromSection(atom, match, macho_file);
237 _ = try gc_sections.put(match, {});237 _ = try gc_sections.put(match, {});
238 _ = macho_file.got_entries_table.remove(entry.target);238 _ = macho_file.got_entries_table.remove(entry.target);
...@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
244244
245 // TODO tombstone245 // TODO tombstone
246 const atom = entry.getAtom(macho_file);246 const atom = entry.getAtom(macho_file);
247 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);247 const match = sym.n_sect - 1;
248 removeAtomFromSection(atom, match, macho_file);248 removeAtomFromSection(atom, match, macho_file);
249 _ = try gc_sections.put(match, {});249 _ = try gc_sections.put(match, {});
250 _ = macho_file.stubs_table.remove(entry.target);250 _ = macho_file.stubs_table.remove(entry.target);
...@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
256256
257 // TODO tombstone257 // TODO tombstone
258 const atom = entry.getAtom(macho_file);258 const atom = entry.getAtom(macho_file);
259 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);259 const match = sym.n_sect - 1;
260 removeAtomFromSection(atom, match, macho_file);260 removeAtomFromSection(atom, match, macho_file);
261 _ = try gc_sections.put(match, {});261 _ = try gc_sections.put(match, {});
262 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);262 _ = 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...@@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
265 var gc_sections_it = gc_sections.iterator();265 var gc_sections_it = gc_sections.iterator();
266 while (gc_sections_it.next()) |entry| {266 while (gc_sections_it.next()) |entry| {
267 const match = entry.key_ptr.*;267 const match = entry.key_ptr.*;
268 const sect = macho_file.getSectionPtr(match);268 var section = macho_file.sections.get(match);
269 if (sect.size == 0) continue; // Pruning happens automatically in next step.269 if (section.header.size == 0) continue; // Pruning happens automatically in next step.
270270
271 sect.@"align" = 0;271 section.header.@"align" = 0;
272 sect.size = 0;272 section.header.size = 0;
273273
274 var atom = macho_file.atoms.get(match).?;274 var atom = section.last_atom.?;
275275
276 while (atom.prev) |prev| {276 while (atom.prev) |prev| {
277 atom = prev;277 atom = prev;
...@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
279279
280 while (true) {280 while (true) {
281 const atom_alignment = try math.powi(u32, 2, atom.alignment);281 const atom_alignment = try math.powi(u32, 2, atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);282 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
283 const padding = aligned_end_addr - sect.size;283 const padding = aligned_end_addr - section.header.size;
284 sect.size += padding + atom.size;284 section.header.size += padding + atom.size;
285 sect.@"align" = @maximum(sect.@"align", atom.alignment);285 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
286286
287 if (atom.next) |next| {287 if (atom.next) |next| {
288 atom = next;288 atom = next;
289 } else break;289 } else break;
290 }290 }
291
292 macho_file.sections.set(match, section);
291 }293 }
292}294}
src/link/MachO/fat.zig+3-1
...@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {...@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
46 return fat_arch.offset;46 return fat_arch.offset;
47 }47 }
48 } else {48 } 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 });
50 return error.MismatchedCpuArchitecture;52 return error.MismatchedCpuArchitecture;
51 }53 }
52}54}