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");...@@ -23,7 +23,6 @@ const target_util = @import("../target.zig");
2323
24const Trie = @import("MachO/Trie.zig");24const Trie = @import("MachO/Trie.zig");
25const CodeSignature = @import("MachO/CodeSignature.zig");25const CodeSignature = @import("MachO/CodeSignature.zig");
26const Parser = @import("MachO/Parser.zig");
2726
28usingnamespace @import("MachO/commands.zig");27usingnamespace @import("MachO/commands.zig");
2928
...@@ -35,6 +34,9 @@ base: File,...@@ -35,6 +34,9 @@ base: File,
35/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.34/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
36page_size: u16,35page_size: u16,
3736
37/// Mach-O header
38header: ?macho.mach_header_64 = null,
39
38/// Table of all load commands40/// Table of all load commands
39load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},41load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
40/// __PAGEZERO segment42/// __PAGEZERO segment
...@@ -105,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},...@@ -105,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
105error_flags: File.ErrorFlags = File.ErrorFlags{},107error_flags: File.ErrorFlags = File.ErrorFlags{},
106108
107cmd_table_dirty: bool = false,109cmd_table_dirty: bool = false,
108dylinker_cmd_dirty: bool = false,
109libsystem_cmd_dirty: bool = false,
110110
111/// A list of text blocks that have surplus capacity. This list can have false111/// A list of text blocks that have surplus capacity. This list can have false
112/// positives, as functions grow and shrink over time, only sometimes being added112/// positives, as functions grow and shrink over time, only sometimes being added
...@@ -325,7 +325,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -325,7 +325,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
325325
326 if (self.cmd_table_dirty) {326 if (self.cmd_table_dirty) {
327 try self.writeLoadCommands();327 try self.writeLoadCommands();
328 try self.writeMachOHeader();328 try self.writeHeader();
329 self.cmd_table_dirty = false;329 self.cmd_table_dirty = false;
330 }330 }
331331
...@@ -725,66 +725,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -725,66 +725,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
725725
726 // At this stage, LLD has done its job. It is time to patch the resultant726 // At this stage, LLD has done its job. It is time to patch the resultant
727 // binaries up!727 // binaries up!
728 var parser = Parser.init(self.base.allocator);
729 defer parser.deinit();
730 const out_file = try directory.handle.openFile(full_out_path, .{ .write = true });728 const out_file = try directory.handle.openFile(full_out_path, .{ .write = true });
731 defer out_file.close();729 try self.parseFromFile(out_file);
732 try parser.parseFile(out_file);730 if (self.code_signature_cmd_index == null) {
733 // Pad out space for code signature731 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
734 const text_cmd = parser.load_commands.items[parser.text_cmd_index.?].Segment.inner;732 const text_section = text_segment.sections.items[self.text_section_index.?];
735 const dataoff = @intCast(u32, mem.alignForward(parser.end_pos.?, @sizeOf(u64)));733 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
736 const emit = self.base.options.emit.?;734 const needed_size = @sizeOf(macho.linkedit_data_command);
737 const datasize = CodeSignature.calcCodeSignaturePadding(emit.sub_path, dataoff);735 if (needed_size + after_last_cmd_offset > text_section.offset) {
738 const code_sig = macho.linkedit_data_command{736 // TODO We are in the position to be able to increase the padding by moving all sections
739 .cmd = macho.LC_CODE_SIGNATURE,737 // by the required offset, but this requires a little bit more thinking and bookkeeping.
740 .cmdsize = @sizeOf(macho.linkedit_data_command),738 // For now, return an error informing the user of the problem.
741 .dataoff = dataoff,739 std.debug.print("Not enough padding between load commands and start of __text section:\n", .{});
742 .datasize = datasize,740 std.debug.print("Offset after last load command: 0x{x}\n", .{after_last_cmd_offset});
743 };741 std.debug.print("Beginning of __text section: 0x{x}\n", .{text_section.offset});
744 const linkedit_seg = parser.load_commands.items[parser.linkedit_cmd_index.?].Segment.inner;742 std.debug.print("Needed size: 0x{x}\n", .{needed_size});
745 const linkedit = macho.segment_command_64{743 return error.NotEnoughPadding;
746 .cmd = linkedit_seg.cmd,744 }
747 .cmdsize = linkedit_seg.cmdsize,745 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
748 .segname = linkedit_seg.segname,746 // TODO This is clunky.
749 .vmaddr = linkedit_seg.vmaddr,747 self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64)));
750 .vmsize = mem.alignForwardGeneric(u64, linkedit_seg.vmsize + datasize, self.page_size),748 // Add code signature load command
751 .fileoff = linkedit_seg.fileoff,749 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
752 .filesize = linkedit_seg.filesize + (dataoff - parser.end_pos.?) + datasize,750 try self.load_commands.append(self.base.allocator, .{
753 .maxprot = linkedit_seg.maxprot,751 .LinkeditData = .{
754 .initprot = linkedit_seg.initprot,752 .cmd = macho.LC_CODE_SIGNATURE,
755 .nsects = linkedit_seg.nsects,753 .cmdsize = @sizeOf(macho.linkedit_data_command),
756 .flags = linkedit_seg.flags,754 .dataoff = 0,
757 };755 .datasize = 0,
758 const header_cmd = parser.header.?;756 },
759 const header = macho.mach_header_64{757 });
760 .magic = header_cmd.magic,758 // Pad out space for code signature
761 .cputype = header_cmd.cputype,759 try self.writeCodeSignaturePadding();
762 .cpusubtype = header_cmd.cpusubtype,760 // Write updated load commands and the header
763 .filetype = header_cmd.filetype,761 try self.writeLoadCommands();
764 .ncmds = header_cmd.ncmds + 1,762 try self.writeHeader();
765 .sizeofcmds = header_cmd.sizeofcmds + @sizeOf(macho.linkedit_data_command),763 // Generate adhoc code signature
766 .flags = header_cmd.flags,764 try self.writeCodeSignature();
767 .reserved = header_cmd.reserved,765 // Move file in-place to please the kernel
768 };766 const emit = self.base.options.emit.?;
769 try out_file.pwriteAll(&[_]u8{0}, code_sig.dataoff + code_sig.datasize);767 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
770 try out_file.pwriteAll(mem.sliceAsBytes(&[_]macho.linkedit_data_command{code_sig}), parser.code_sig_cmd_offset.?);768 }
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, .{});
788 }769 }
789 }770 }
790771
...@@ -1132,6 +1113,53 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1132,6 +1113,53 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1132 .Lib => return error.TODOImplementWritingLibFiles,1113 .Lib => return error.TODOImplementWritingLibFiles,
1133 }1114 }
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 }
1135 if (self.pagezero_segment_cmd_index == null) {1163 if (self.pagezero_segment_cmd_index == null) {
1136 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);1164 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1137 try self.load_commands.append(self.base.allocator, .{1165 try self.load_commands.append(self.base.allocator, .{
...@@ -1852,60 +1880,16 @@ fn writeLoadCommands(self: *MachO) !void {...@@ -1852,60 +1880,16 @@ fn writeLoadCommands(self: *MachO) !void {
1852}1880}
18531881
1854/// Writes Mach-O file header.1882/// Writes Mach-O file header.
1855fn writeMachOHeader(self: *MachO) !void {1883fn writeHeader(self: *MachO) !void {
1856 var hdr: macho.mach_header_64 = undefined;1884 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
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
1889 var sizeofcmds: u32 = 0;1885 var sizeofcmds: u32 = 0;
1890 for (self.load_commands.items) |cmd| {1886 for (self.load_commands.items) |cmd| {
1891 sizeofcmds += cmd.cmdsize();1887 sizeofcmds += cmd.cmdsize();
1892 }1888 }
18931889 self.header.?.sizeofcmds = sizeofcmds;
1894 hdr.sizeofcmds = sizeofcmds;1890 log.debug("writing Mach-O header {}\n", .{self.header.?});
18951891 const slice = [1]macho.mach_header_64{self.header.?};
1896 switch (self.base.options.output_mode) {1892 try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0);
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);
1909}1893}
19101894
1911/// Saturating multiplication1895/// Saturating multiplication
...@@ -1913,3 +1897,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {...@@ -1913,3 +1897,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
1913 const T = @TypeOf(a, b);1897 const T = @TypeOf(a, b);
1914 return std.math.mul(T, a, b) catch std.math.maxInt(T);1898 return std.math.mul(T, a, b) catch std.math.maxInt(T);
1915}1899}
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}