authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-01 23:39:07+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-01 23:39:07+01:00
logb58a2a4de6f9ce82d969d076dea798daf22d4b69
treea95be647a0ac9566c6e6c6a7a7a823a67c55a71a
parented180465182fb95424f1c08793b529d5ec577018

lld+macho: move parsing logic into MachO


2 files changed, 142 insertions(+), 195 deletions(-)

src/link/MachO.zig+142-113
......@@ -23,7 +23,6 @@ const target_util = @import("../target.zig");
2323
2424const Trie = @import("MachO/Trie.zig");
2525const CodeSignature = @import("MachO/CodeSignature.zig");
26const Parser = @import("MachO/Parser.zig");
2726
2827usingnamespace @import("MachO/commands.zig");
2928
......@@ -35,6 +34,9 @@ base: File,
3534/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
3635page_size: u16,
3736
37/// Mach-O header
38header: ?macho.mach_header_64 = null,
39
3840/// Table of all load commands
3941load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
4042/// __PAGEZERO segment
......@@ -105,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
105107error_flags: File.ErrorFlags = File.ErrorFlags{},
106108
107109cmd_table_dirty: bool = false,
108dylinker_cmd_dirty: bool = false,
109libsystem_cmd_dirty: bool = false,
110110
111111/// A list of text blocks that have surplus capacity. This list can have false
112112/// positives, as functions grow and shrink over time, only sometimes being added
......@@ -325,7 +325,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
325325
326326 if (self.cmd_table_dirty) {
327327 try self.writeLoadCommands();
328 try self.writeMachOHeader();
328 try self.writeHeader();
329329 self.cmd_table_dirty = false;
330330 }
331331
......@@ -725,66 +725,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
725725
726726 // At this stage, LLD has done its job. It is time to patch the resultant
727727 // binaries up!
728 var parser = Parser.init(self.base.allocator);
729 defer parser.deinit();
730728 const out_file = try directory.handle.openFile(full_out_path, .{ .write = true });
731 defer out_file.close();
732 try parser.parseFile(out_file);
733 // Pad out space for code signature
734 const text_cmd = parser.load_commands.items[parser.text_cmd_index.?].Segment.inner;
735 const dataoff = @intCast(u32, mem.alignForward(parser.end_pos.?, @sizeOf(u64)));
736 const emit = self.base.options.emit.?;
737 const datasize = CodeSignature.calcCodeSignaturePadding(emit.sub_path, dataoff);
738 const code_sig = macho.linkedit_data_command{
739 .cmd = macho.LC_CODE_SIGNATURE,
740 .cmdsize = @sizeOf(macho.linkedit_data_command),
741 .dataoff = dataoff,
742 .datasize = datasize,
743 };
744 const linkedit_seg = parser.load_commands.items[parser.linkedit_cmd_index.?].Segment.inner;
745 const linkedit = macho.segment_command_64{
746 .cmd = linkedit_seg.cmd,
747 .cmdsize = linkedit_seg.cmdsize,
748 .segname = linkedit_seg.segname,
749 .vmaddr = linkedit_seg.vmaddr,
750 .vmsize = mem.alignForwardGeneric(u64, linkedit_seg.vmsize + datasize, self.page_size),
751 .fileoff = linkedit_seg.fileoff,
752 .filesize = linkedit_seg.filesize + (dataoff - parser.end_pos.?) + datasize,
753 .maxprot = linkedit_seg.maxprot,
754 .initprot = linkedit_seg.initprot,
755 .nsects = linkedit_seg.nsects,
756 .flags = linkedit_seg.flags,
757 };
758 const header_cmd = parser.header.?;
759 const header = macho.mach_header_64{
760 .magic = header_cmd.magic,
761 .cputype = header_cmd.cputype,
762 .cpusubtype = header_cmd.cpusubtype,
763 .filetype = header_cmd.filetype,
764 .ncmds = header_cmd.ncmds + 1,
765 .sizeofcmds = header_cmd.sizeofcmds + @sizeOf(macho.linkedit_data_command),
766 .flags = header_cmd.flags,
767 .reserved = header_cmd.reserved,
768 };
769 try out_file.pwriteAll(&[_]u8{0}, code_sig.dataoff + code_sig.datasize);
770 try out_file.pwriteAll(mem.sliceAsBytes(&[_]macho.linkedit_data_command{code_sig}), parser.code_sig_cmd_offset.?);
771 try out_file.pwriteAll(mem.sliceAsBytes(&[_]macho.segment_command_64{linkedit}), parser.linkedit_cmd_offset.?);
772 try out_file.pwriteAll(mem.sliceAsBytes(&[_]macho.mach_header_64{header}), 0);
773 // Generate adhoc code signature
774 var signature = CodeSignature.init(self.base.allocator);
775 defer signature.deinit();
776 try signature.calcAdhocSignature(
777 out_file,
778 emit.sub_path,
779 text_cmd,
780 code_sig,
781 self.base.options.output_mode,
782 );
783 var buffer = try self.base.allocator.alloc(u8, signature.size());
784 defer self.base.allocator.free(buffer);
785 signature.write(buffer);
786 try out_file.pwriteAll(buffer, code_sig.dataoff);
787 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
729 try self.parseFromFile(out_file);
730 if (self.code_signature_cmd_index == null) {
731 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
732 const text_section = text_segment.sections.items[self.text_section_index.?];
733 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
734 const needed_size = @sizeOf(macho.linkedit_data_command);
735 if (needed_size + after_last_cmd_offset > text_section.offset) {
736 // TODO We are in the position to be able to increase the padding by moving all sections
737 // by the required offset, but this requires a little bit more thinking and bookkeeping.
738 // For now, return an error informing the user of the problem.
739 std.debug.print("Not enough padding between load commands and start of __text section:\n", .{});
740 std.debug.print("Offset after last load command: 0x{x}\n", .{after_last_cmd_offset});
741 std.debug.print("Beginning of __text section: 0x{x}\n", .{text_section.offset});
742 std.debug.print("Needed size: 0x{x}\n", .{needed_size});
743 return error.NotEnoughPadding;
744 }
745 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
746 // TODO This is clunky.
747 self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64)));
748 // Add code signature load command
749 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
750 try self.load_commands.append(self.base.allocator, .{
751 .LinkeditData = .{
752 .cmd = macho.LC_CODE_SIGNATURE,
753 .cmdsize = @sizeOf(macho.linkedit_data_command),
754 .dataoff = 0,
755 .datasize = 0,
756 },
757 });
758 // Pad out space for code signature
759 try self.writeCodeSignaturePadding();
760 // Write updated load commands and the header
761 try self.writeLoadCommands();
762 try self.writeHeader();
763 // Generate adhoc code signature
764 try self.writeCodeSignature();
765 // Move file in-place to please the kernel
766 const emit = self.base.options.emit.?;
767 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
768 }
788769 }
789770 }
790771
......@@ -1132,6 +1113,53 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11321113 .Lib => return error.TODOImplementWritingLibFiles,
11331114 }
11341115
1116 if (self.header == null) {
1117 var header: macho.mach_header_64 = undefined;
1118 header.magic = macho.MH_MAGIC_64;
1119
1120 const CpuInfo = struct {
1121 cpu_type: macho.cpu_type_t,
1122 cpu_subtype: macho.cpu_subtype_t,
1123 };
1124
1125 const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
1126 .aarch64 => .{
1127 .cpu_type = macho.CPU_TYPE_ARM64,
1128 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
1129 },
1130 .x86_64 => .{
1131 .cpu_type = macho.CPU_TYPE_X86_64,
1132 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
1133 },
1134 else => return error.UnsupportedMachOArchitecture,
1135 };
1136 header.cputype = cpu_info.cpu_type;
1137 header.cpusubtype = cpu_info.cpu_subtype;
1138
1139 const filetype: u32 = switch (self.base.options.output_mode) {
1140 .Exe => macho.MH_EXECUTE,
1141 .Obj => macho.MH_OBJECT,
1142 .Lib => switch (self.base.options.link_mode) {
1143 .Static => return error.TODOStaticLibMachOType,
1144 .Dynamic => macho.MH_DYLIB,
1145 },
1146 };
1147 header.filetype = filetype;
1148 // These will get populated at the end of flushing the results to file.
1149 header.ncmds = 0;
1150 header.sizeofcmds = 0;
1151
1152 switch (self.base.options.output_mode) {
1153 .Exe => {
1154 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE;
1155 },
1156 else => {
1157 header.flags = 0;
1158 },
1159 }
1160 header.reserved = 0;
1161 self.header = header;
1162 }
11351163 if (self.pagezero_segment_cmd_index == null) {
11361164 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
11371165 try self.load_commands.append(self.base.allocator, .{
......@@ -1852,60 +1880,16 @@ fn writeLoadCommands(self: *MachO) !void {
18521880}
18531881
18541882/// Writes Mach-O file header.
1855fn writeMachOHeader(self: *MachO) !void {
1856 var hdr: macho.mach_header_64 = undefined;
1857 hdr.magic = macho.MH_MAGIC_64;
1858
1859 const CpuInfo = struct {
1860 cpu_type: macho.cpu_type_t,
1861 cpu_subtype: macho.cpu_subtype_t,
1862 };
1863
1864 const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
1865 .aarch64 => .{
1866 .cpu_type = macho.CPU_TYPE_ARM64,
1867 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
1868 },
1869 .x86_64 => .{
1870 .cpu_type = macho.CPU_TYPE_X86_64,
1871 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
1872 },
1873 else => return error.UnsupportedMachOArchitecture,
1874 };
1875 hdr.cputype = cpu_info.cpu_type;
1876 hdr.cpusubtype = cpu_info.cpu_subtype;
1877
1878 const filetype: u32 = switch (self.base.options.output_mode) {
1879 .Exe => macho.MH_EXECUTE,
1880 .Obj => macho.MH_OBJECT,
1881 .Lib => switch (self.base.options.link_mode) {
1882 .Static => return error.TODOStaticLibMachOType,
1883 .Dynamic => macho.MH_DYLIB,
1884 },
1885 };
1886 hdr.filetype = filetype;
1887 hdr.ncmds = @intCast(u32, self.load_commands.items.len);
1888
1883fn writeHeader(self: *MachO) !void {
1884 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
18891885 var sizeofcmds: u32 = 0;
18901886 for (self.load_commands.items) |cmd| {
18911887 sizeofcmds += cmd.cmdsize();
18921888 }
1893
1894 hdr.sizeofcmds = sizeofcmds;
1895
1896 switch (self.base.options.output_mode) {
1897 .Exe => {
1898 hdr.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE;
1899 },
1900 else => {
1901 hdr.flags = 0;
1902 },
1903 }
1904 hdr.reserved = 0;
1905
1906 log.debug("writing Mach-O header {}\n", .{hdr});
1907
1908 try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0);
1889 self.header.?.sizeofcmds = sizeofcmds;
1890 log.debug("writing Mach-O header {}\n", .{self.header.?});
1891 const slice = [1]macho.mach_header_64{self.header.?};
1892 try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0);
19091893}
19101894
19111895/// Saturating multiplication
......@@ -1913,3 +1897,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
19131897 const T = @TypeOf(a, b);
19141898 return std.math.mul(T, a, b) catch std.math.maxInt(T);
19151899}
1900
1901/// Parse MachO contents from existing binary file.
1902/// TODO This method is incomplete and currently parses only the header
1903/// plus the load commands.
1904fn parseFromFile(self: *MachO, file: fs.File) !void {
1905 self.base.file = file;
1906 var reader = file.reader();
1907 const header = try reader.readStruct(macho.mach_header_64);
1908 try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds);
1909 var i: u16 = 0;
1910 while (i < header.ncmds) : (i += 1) {
1911 const cmd = try LoadCommand.read(self.base.allocator, reader);
1912 switch (cmd.cmd()) {
1913 macho.LC_SEGMENT_64 => {
1914 const x = cmd.Segment;
1915 if (isSegmentOrSection(&x.inner.segname, "__LINKEDIT")) {
1916 self.linkedit_segment_cmd_index = i;
1917 } else if (isSegmentOrSection(&x.inner.segname, "__TEXT")) {
1918 self.text_segment_cmd_index = i;
1919 for (x.sections.items) |sect, j| {
1920 if (isSegmentOrSection(&sect.sectname, "__text")) {
1921 self.text_section_index = @intCast(u16, j);
1922 }
1923 }
1924 }
1925 },
1926 macho.LC_SYMTAB => {
1927 self.symtab_cmd_index = i;
1928 },
1929 macho.LC_CODE_SIGNATURE => {
1930 self.code_signature_cmd_index = i;
1931 },
1932 // TODO populate more MachO fields
1933 else => {},
1934 }
1935 self.load_commands.appendAssumeCapacity(cmd);
1936 }
1937 self.header = header;
1938
1939 // TODO parse memory mapped segments
1940}
1941
1942fn isSegmentOrSection(name: *const [16]u8, needle: []const u8) bool {
1943 return mem.eql(u8, mem.trimRight(u8, name.*[0..], &[_]u8{0}), needle);
1944}
src/link/MachO/Parser.zig deleted-82
......@@ -1,82 +0,0 @@
1const Parser = @This();
2
3const std = @import("std");
4const fs = std.fs;
5const io = std.io;
6const mem = std.mem;
7const macho = std.macho;
8
9const Allocator = std.mem.Allocator;
10
11const LoadCommand = @import("commands.zig").LoadCommand;
12
13allocator: *Allocator,
14
15/// Mach-O header
16header: ?macho.mach_header_64 = null,
17
18/// Load commands
19load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
20
21text_cmd_index: ?u16 = null,
22
23linkedit_cmd_index: ?u16 = null,
24linkedit_cmd_offset: ?u64 = null,
25
26code_sig_cmd_offset: ?u64 = null,
27
28end_pos: ?u64 = null,
29
30pub fn init(allocator: *Allocator) Parser {
31 return .{ .allocator = allocator };
32}
33
34pub fn parse(self: *Parser, reader: anytype) !void {
35 self.header = try reader.readStruct(macho.mach_header_64);
36
37 const ncmds = self.header.?.ncmds;
38 try self.load_commands.ensureCapacity(self.allocator, ncmds);
39
40 var off: u64 = @sizeOf(macho.mach_header_64);
41 var i: u16 = 0;
42 while (i < ncmds) : (i += 1) {
43 const cmd = try LoadCommand.read(self.allocator, reader);
44 switch (cmd.cmd()) {
45 macho.LC_SEGMENT_64 => {
46 const x = cmd.Segment;
47 if (mem.eql(u8, parseName(&x.inner.segname), "__LINKEDIT")) {
48 self.linkedit_cmd_index = i;
49 self.linkedit_cmd_offset = off;
50 } else if (mem.eql(u8, parseName(&x.inner.segname), "__TEXT")) {
51 self.text_cmd_index = i;
52 }
53 },
54 macho.LC_SYMTAB => {
55 const x = cmd.Symtab;
56 self.end_pos = x.stroff + x.strsize;
57 },
58 else => {},
59 }
60 off += cmd.cmdsize();
61 self.load_commands.appendAssumeCapacity(cmd);
62 }
63
64 self.code_sig_cmd_offset = off;
65
66 // TODO parse memory mapped segments
67}
68
69pub fn parseFile(self: *Parser, file: fs.File) !void {
70 return self.parse(file.reader());
71}
72
73pub fn deinit(self: *Parser) void {
74 for (self.load_commands.items) |*cmd| {
75 cmd.deinit(self.allocator);
76 }
77 self.load_commands.deinit(self.allocator);
78}
79
80fn parseName(name: *const [16]u8) []const u8 {
81 return mem.trimRight(u8, name.*[0..], &[_]u8{0});
82}