| ... | ... | @@ -23,7 +23,6 @@ const target_util = @import("../target.zig"); |
| 23 | 23 | |
| 24 | 24 | const Trie = @import("MachO/Trie.zig"); |
| 25 | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | | const Parser = @import("MachO/Parser.zig"); |
| 27 | 26 | |
| 28 | 27 | usingnamespace @import("MachO/commands.zig"); |
| 29 | 28 | |
| ... | ... | @@ -35,6 +34,9 @@ base: File, |
| 35 | 34 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 36 | 35 | page_size: u16, |
| 37 | 36 | |
| 37 | /// Mach-O header |
| 38 | header: ?macho.mach_header_64 = null, |
| 39 | |
| 38 | 40 | /// Table of all load commands |
| 39 | 41 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 40 | 42 | /// __PAGEZERO segment |
| ... | ... | @@ -105,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 105 | 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 106 | 108 | |
| 107 | 109 | cmd_table_dirty: bool = false, |
| 108 | | dylinker_cmd_dirty: bool = false, |
| 109 | | libsystem_cmd_dirty: bool = false, |
| 110 | 110 | |
| 111 | 111 | /// A list of text blocks that have surplus capacity. This list can have false |
| 112 | 112 | /// 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 | 325 | |
| 326 | 326 | if (self.cmd_table_dirty) { |
| 327 | 327 | try self.writeLoadCommands(); |
| 328 | | try self.writeMachOHeader(); |
| 328 | try self.writeHeader(); |
| 329 | 329 | self.cmd_table_dirty = false; |
| 330 | 330 | } |
| 331 | 331 | |
| ... | ... | @@ -725,66 +725,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 725 | 725 | |
| 726 | 726 | // At this stage, LLD has done its job. It is time to patch the resultant |
| 727 | 727 | // binaries up! |
| 728 | | var parser = Parser.init(self.base.allocator); |
| 729 | | defer parser.deinit(); |
| 730 | 728 | 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 | } |
| 788 | 769 | } |
| 789 | 770 | } |
| 790 | 771 | |
| ... | ... | @@ -1132,6 +1113,53 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1132 | 1113 | .Lib => return error.TODOImplementWritingLibFiles, |
| 1133 | 1114 | } |
| 1134 | 1115 | |
| 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 | 1163 | if (self.pagezero_segment_cmd_index == null) { |
| 1136 | 1164 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1137 | 1165 | try self.load_commands.append(self.base.allocator, .{ |
| ... | ... | @@ -1852,60 +1880,16 @@ fn writeLoadCommands(self: *MachO) !void { |
| 1852 | 1880 | } |
| 1853 | 1881 | |
| 1854 | 1882 | /// Writes Mach-O file header. |
| 1855 | | fn 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 | | |
| 1883 | fn writeHeader(self: *MachO) !void { |
| 1884 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); |
| 1889 | 1885 | var sizeofcmds: u32 = 0; |
| 1890 | 1886 | for (self.load_commands.items) |cmd| { |
| 1891 | 1887 | sizeofcmds += cmd.cmdsize(); |
| 1892 | 1888 | } |
| 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); |
| 1909 | 1893 | } |
| 1910 | 1894 | |
| 1911 | 1895 | /// Saturating multiplication |
| ... | ... | @@ -1913,3 +1897,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1913 | 1897 | const T = @TypeOf(a, b); |
| 1914 | 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. |
| 1904 | fn 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 | |
| 1942 | fn isSegmentOrSection(name: *const [16]u8, needle: []const u8) bool { |
| 1943 | return mem.eql(u8, mem.trimRight(u8, name.*[0..], &[_]u8{0}), needle); |
| 1944 | } |