| ... | ... | @@ -24,55 +24,9 @@ const target_util = @import("../target.zig"); |
| 24 | 24 | const Trie = @import("MachO/Trie.zig"); |
| 25 | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | 26 | |
| 27 | | pub const base_tag: File.Tag = File.Tag.macho; |
| 28 | | |
| 29 | | const LoadCommand = union(enum) { |
| 30 | | Segment: macho.segment_command_64, |
| 31 | | LinkeditData: macho.linkedit_data_command, |
| 32 | | Symtab: macho.symtab_command, |
| 33 | | Dysymtab: macho.dysymtab_command, |
| 34 | | DyldInfo: macho.dyld_info_command, |
| 35 | | Dylinker: macho.dylinker_command, |
| 36 | | Dylib: macho.dylib_command, |
| 37 | | EntryPoint: macho.entry_point_command, |
| 38 | | MinVersion: macho.version_min_command, |
| 39 | | SourceVersion: macho.source_version_command, |
| 40 | | |
| 41 | | pub fn cmdsize(self: LoadCommand) u32 { |
| 42 | | return switch (self) { |
| 43 | | .Segment => |x| x.cmdsize, |
| 44 | | .LinkeditData => |x| x.cmdsize, |
| 45 | | .Symtab => |x| x.cmdsize, |
| 46 | | .Dysymtab => |x| x.cmdsize, |
| 47 | | .DyldInfo => |x| x.cmdsize, |
| 48 | | .Dylinker => |x| x.cmdsize, |
| 49 | | .Dylib => |x| x.cmdsize, |
| 50 | | .EntryPoint => |x| x.cmdsize, |
| 51 | | .MinVersion => |x| x.cmdsize, |
| 52 | | .SourceVersion => |x| x.cmdsize, |
| 53 | | }; |
| 54 | | } |
| 55 | | |
| 56 | | pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void { |
| 57 | | return switch (self) { |
| 58 | | .Segment => |cmd| writeGeneric(cmd, file, offset), |
| 59 | | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), |
| 60 | | .Symtab => |cmd| writeGeneric(cmd, file, offset), |
| 61 | | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), |
| 62 | | .DyldInfo => |cmd| writeGeneric(cmd, file, offset), |
| 63 | | .Dylinker => |cmd| writeGeneric(cmd, file, offset), |
| 64 | | .Dylib => |cmd| writeGeneric(cmd, file, offset), |
| 65 | | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), |
| 66 | | .MinVersion => |cmd| writeGeneric(cmd, file, offset), |
| 67 | | .SourceVersion => |cmd| writeGeneric(cmd, file, offset), |
| 68 | | }; |
| 69 | | } |
| 27 | usingnamespace @import("MachO/commands.zig"); |
| 70 | 28 | |
| 71 | | fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void { |
| 72 | | const slice = [1]@TypeOf(cmd){cmd}; |
| 73 | | return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset); |
| 74 | | } |
| 75 | | }; |
| 29 | pub const base_tag: File.Tag = File.Tag.macho; |
| 76 | 30 | |
| 77 | 31 | base: File, |
| 78 | 32 | |
| ... | ... | @@ -80,6 +34,9 @@ base: File, |
| 80 | 34 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 81 | 35 | page_size: u16, |
| 82 | 36 | |
| 37 | /// Mach-O header |
| 38 | header: ?macho.mach_header_64 = null, |
| 39 | |
| 83 | 40 | /// Table of all load commands |
| 84 | 41 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 85 | 42 | /// __PAGEZERO segment |
| ... | ... | @@ -115,18 +72,14 @@ source_version_cmd_index: ?u16 = null, |
| 115 | 72 | /// Code signature |
| 116 | 73 | code_signature_cmd_index: ?u16 = null, |
| 117 | 74 | |
| 118 | | /// Table of all sections |
| 119 | | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 120 | | |
| 121 | | /// __TEXT,__text section |
| 75 | /// Index into __TEXT,__text section. |
| 122 | 76 | text_section_index: ?u16 = null, |
| 123 | | |
| 124 | | /// __DATA,__got section |
| 77 | /// Index into __TEXT,__got section. |
| 125 | 78 | got_section_index: ?u16 = null, |
| 126 | | |
| 79 | /// The absolute address of the entry point. |
| 127 | 80 | entry_addr: ?u64 = null, |
| 128 | 81 | |
| 129 | | // TODO move this into each Segment aggregator |
| 82 | /// TODO move this into each Segment aggregator |
| 130 | 83 | linkedit_segment_next_offset: ?u32 = null, |
| 131 | 84 | |
| 132 | 85 | /// Table of all local symbols |
| ... | ... | @@ -154,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 154 | 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 155 | 108 | |
| 156 | 109 | cmd_table_dirty: bool = false, |
| 157 | | dylinker_cmd_dirty: bool = false, |
| 158 | | libsystem_cmd_dirty: bool = false, |
| 159 | 110 | |
| 160 | 111 | /// A list of text blocks that have surplus capacity. This list can have false |
| 161 | 112 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | ... | @@ -355,40 +306,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 355 | 306 | if (self.entry_addr) |addr| { |
| 356 | 307 | // Update LC_MAIN with entry offset. |
| 357 | 308 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 358 | | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 359 | | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 360 | | } |
| 361 | | if (self.dylinker_cmd_dirty) { |
| 362 | | // Write path to dyld loader. |
| 363 | | var off: usize = @sizeOf(macho.mach_header_64); |
| 364 | | for (self.load_commands.items) |cmd| { |
| 365 | | if (cmd == .Dylinker) break; |
| 366 | | off += cmd.cmdsize(); |
| 367 | | } |
| 368 | | const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker; |
| 369 | | off += cmd.name; |
| 370 | | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); |
| 371 | | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); |
| 372 | | self.dylinker_cmd_dirty = false; |
| 373 | | } |
| 374 | | if (self.libsystem_cmd_dirty) { |
| 375 | | // Write path to libSystem. |
| 376 | | var off: usize = @sizeOf(macho.mach_header_64); |
| 377 | | for (self.load_commands.items) |cmd| { |
| 378 | | if (cmd == .Dylib) break; |
| 379 | | off += cmd.cmdsize(); |
| 380 | | } |
| 381 | | const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib; |
| 382 | | off += cmd.dylib.name; |
| 383 | | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); |
| 384 | | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 385 | | self.libsystem_cmd_dirty = false; |
| 309 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; |
| 310 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; |
| 386 | 311 | } |
| 387 | | |
| 388 | 312 | try self.writeExportTrie(); |
| 389 | 313 | try self.writeSymbolTable(); |
| 390 | 314 | try self.writeStringTable(); |
| 391 | | |
| 392 | 315 | // Preallocate space for the code signature. |
| 393 | 316 | // We need to do this at this stage so that we have the load commands with proper values |
| 394 | 317 | // written out to the file. |
| ... | ... | @@ -401,8 +324,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 401 | 324 | } |
| 402 | 325 | |
| 403 | 326 | if (self.cmd_table_dirty) { |
| 404 | | try self.writeCmdHeaders(); |
| 405 | | try self.writeMachOHeader(); |
| 327 | try self.writeLoadCommands(); |
| 328 | try self.writeHeader(); |
| 406 | 329 | self.cmd_table_dirty = false; |
| 407 | 330 | } |
| 408 | 331 | |
| ... | ... | @@ -415,8 +338,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 415 | 338 | } |
| 416 | 339 | |
| 417 | 340 | assert(!self.cmd_table_dirty); |
| 418 | | assert(!self.dylinker_cmd_dirty); |
| 419 | | assert(!self.libsystem_cmd_dirty); |
| 420 | 341 | |
| 421 | 342 | switch (self.base.options.output_mode) { |
| 422 | 343 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last |
| ... | ... | @@ -760,7 +681,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 760 | 681 | if (result.term != .Exited or result.term.Exited != 0) { |
| 761 | 682 | // TODO parse this output and surface with the Compilation API rather than |
| 762 | 683 | // directly outputting to stderr here. |
| 763 | | std.debug.print("{}", .{result.stderr}); |
| 684 | std.log.err("{}", .{result.stderr}); |
| 764 | 685 | return error.LDReportedFailure; |
| 765 | 686 | } |
| 766 | 687 | } else { |
| ... | ... | @@ -795,12 +716,53 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 795 | 716 | if (!ok) { |
| 796 | 717 | // TODO parse this output and surface with the Compilation API rather than |
| 797 | 718 | // directly outputting to stderr here. |
| 798 | | std.debug.print("{}", .{stderr_context.data.items}); |
| 719 | std.log.err("{}", .{stderr_context.data.items}); |
| 799 | 720 | return error.LLDReportedFailure; |
| 800 | 721 | } |
| 801 | 722 | if (stderr_context.data.items.len != 0) { |
| 802 | 723 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); |
| 803 | 724 | } |
| 725 | |
| 726 | // At this stage, LLD has done its job. It is time to patch the resultant |
| 727 | // binaries up! |
| 728 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); |
| 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.log.err("Not enough padding between load commands and start of __text section:\n", .{}); |
| 740 | std.log.err("Offset after last load command: 0x{x}\n", .{after_last_cmd_offset}); |
| 741 | std.log.err("Beginning of __text section: 0x{x}\n", .{text_section.offset}); |
| 742 | std.log.err("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 | } |
| 804 | 766 | } |
| 805 | 767 | } |
| 806 | 768 | |
| ... | ... | @@ -857,7 +819,9 @@ pub fn deinit(self: *MachO) void { |
| 857 | 819 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 858 | 820 | self.local_symbols.deinit(self.base.allocator); |
| 859 | 821 | self.local_symbol_free_list.deinit(self.base.allocator); |
| 860 | | self.sections.deinit(self.base.allocator); |
| 822 | for (self.load_commands.items) |*lc| { |
| 823 | lc.deinit(self.base.allocator); |
| 824 | } |
| 861 | 825 | self.load_commands.deinit(self.base.allocator); |
| 862 | 826 | } |
| 863 | 827 | |
| ... | ... | @@ -1011,7 +975,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1011 | 975 | } |
| 1012 | 976 | |
| 1013 | 977 | // Perform PIE fixups (if any) |
| 1014 | | const got_section = self.sections.items[self.got_section_index.?]; |
| 978 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 979 | const got_section = text_segment.sections.items[self.got_section_index.?]; |
| 1015 | 980 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1016 | 981 | const target_addr = fixup.address; |
| 1017 | 982 | const this_addr = symbol.n_value + fixup.start; |
| ... | ... | @@ -1030,7 +995,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1030 | 995 | } |
| 1031 | 996 | } |
| 1032 | 997 | |
| 1033 | | const text_section = self.sections.items[self.text_section_index.?]; |
| 998 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1034 | 999 | const section_offset = symbol.n_value - text_section.addr; |
| 1035 | 1000 | const file_offset = text_section.offset + section_offset; |
| 1036 | 1001 | try self.base.file.?.pwriteAll(code, file_offset); |
| ... | ... | @@ -1145,10 +1110,57 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1145 | 1110 | .Lib => return error.TODOImplementWritingLibFiles, |
| 1146 | 1111 | } |
| 1147 | 1112 | |
| 1113 | if (self.header == null) { |
| 1114 | var header: macho.mach_header_64 = undefined; |
| 1115 | header.magic = macho.MH_MAGIC_64; |
| 1116 | |
| 1117 | const CpuInfo = struct { |
| 1118 | cpu_type: macho.cpu_type_t, |
| 1119 | cpu_subtype: macho.cpu_subtype_t, |
| 1120 | }; |
| 1121 | |
| 1122 | const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) { |
| 1123 | .aarch64 => .{ |
| 1124 | .cpu_type = macho.CPU_TYPE_ARM64, |
| 1125 | .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL, |
| 1126 | }, |
| 1127 | .x86_64 => .{ |
| 1128 | .cpu_type = macho.CPU_TYPE_X86_64, |
| 1129 | .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL, |
| 1130 | }, |
| 1131 | else => return error.UnsupportedMachOArchitecture, |
| 1132 | }; |
| 1133 | header.cputype = cpu_info.cpu_type; |
| 1134 | header.cpusubtype = cpu_info.cpu_subtype; |
| 1135 | |
| 1136 | const filetype: u32 = switch (self.base.options.output_mode) { |
| 1137 | .Exe => macho.MH_EXECUTE, |
| 1138 | .Obj => macho.MH_OBJECT, |
| 1139 | .Lib => switch (self.base.options.link_mode) { |
| 1140 | .Static => return error.TODOStaticLibMachOType, |
| 1141 | .Dynamic => macho.MH_DYLIB, |
| 1142 | }, |
| 1143 | }; |
| 1144 | header.filetype = filetype; |
| 1145 | // These will get populated at the end of flushing the results to file. |
| 1146 | header.ncmds = 0; |
| 1147 | header.sizeofcmds = 0; |
| 1148 | |
| 1149 | switch (self.base.options.output_mode) { |
| 1150 | .Exe => { |
| 1151 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE; |
| 1152 | }, |
| 1153 | else => { |
| 1154 | header.flags = 0; |
| 1155 | }, |
| 1156 | } |
| 1157 | header.reserved = 0; |
| 1158 | self.header = header; |
| 1159 | } |
| 1148 | 1160 | if (self.pagezero_segment_cmd_index == null) { |
| 1149 | 1161 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1150 | 1162 | try self.load_commands.append(self.base.allocator, .{ |
| 1151 | | .Segment = .{ |
| 1163 | .Segment = SegmentCommand.empty(.{ |
| 1152 | 1164 | .cmd = macho.LC_SEGMENT_64, |
| 1153 | 1165 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1154 | 1166 | .segname = makeStaticString("__PAGEZERO"), |
| ... | ... | @@ -1160,7 +1172,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1160 | 1172 | .initprot = 0, |
| 1161 | 1173 | .nsects = 0, |
| 1162 | 1174 | .flags = 0, |
| 1163 | | }, |
| 1175 | }), |
| 1164 | 1176 | }); |
| 1165 | 1177 | self.cmd_table_dirty = true; |
| 1166 | 1178 | } |
| ... | ... | @@ -1169,7 +1181,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1169 | 1181 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1170 | 1182 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1171 | 1183 | try self.load_commands.append(self.base.allocator, .{ |
| 1172 | | .Segment = .{ |
| 1184 | .Segment = SegmentCommand.empty(.{ |
| 1173 | 1185 | .cmd = macho.LC_SEGMENT_64, |
| 1174 | 1186 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1175 | 1187 | .segname = makeStaticString("__TEXT"), |
| ... | ... | @@ -1181,45 +1193,45 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1181 | 1193 | .initprot = initprot, |
| 1182 | 1194 | .nsects = 0, |
| 1183 | 1195 | .flags = 0, |
| 1184 | | }, |
| 1196 | }), |
| 1185 | 1197 | }); |
| 1186 | 1198 | self.cmd_table_dirty = true; |
| 1187 | 1199 | } |
| 1188 | 1200 | if (self.text_section_index == null) { |
| 1189 | | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 1190 | 1201 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1202 | self.text_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1191 | 1203 | |
| 1192 | 1204 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1193 | 1205 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); |
| 1194 | 1206 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? |
| 1195 | | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1196 | 1207 | |
| 1197 | 1208 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1198 | 1209 | |
| 1199 | | try self.sections.append(self.base.allocator, .{ |
| 1210 | try text_segment.sections.append(self.base.allocator, .{ |
| 1200 | 1211 | .sectname = makeStaticString("__text"), |
| 1201 | 1212 | .segname = makeStaticString("__TEXT"), |
| 1202 | | .addr = text_segment.vmaddr + off, |
| 1213 | .addr = text_segment.inner.vmaddr + off, |
| 1203 | 1214 | .size = file_size, |
| 1204 | 1215 | .offset = off, |
| 1205 | 1216 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 |
| 1206 | 1217 | .reloff = 0, |
| 1207 | 1218 | .nreloc = 0, |
| 1208 | | .flags = flags, |
| 1219 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1209 | 1220 | .reserved1 = 0, |
| 1210 | 1221 | .reserved2 = 0, |
| 1211 | 1222 | .reserved3 = 0, |
| 1212 | 1223 | }); |
| 1213 | 1224 | |
| 1214 | | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1215 | | text_segment.filesize = file_size + off; |
| 1216 | | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1217 | | text_segment.nsects += 1; |
| 1225 | text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1226 | text_segment.inner.filesize = file_size + off; |
| 1227 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1228 | text_segment.inner.nsects += 1; |
| 1218 | 1229 | self.cmd_table_dirty = true; |
| 1219 | 1230 | } |
| 1220 | 1231 | if (self.got_section_index == null) { |
| 1221 | | self.got_section_index = @intCast(u16, self.sections.items.len); |
| 1222 | | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1232 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1233 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1234 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1223 | 1235 | |
| 1224 | 1236 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1225 | 1237 | // TODO looking for free space should be done *within* a segment it belongs to |
| ... | ... | @@ -1227,7 +1239,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1227 | 1239 | |
| 1228 | 1240 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1229 | 1241 | |
| 1230 | | try self.sections.append(self.base.allocator, .{ |
| 1242 | try text_segment.sections.append(self.base.allocator, .{ |
| 1231 | 1243 | .sectname = makeStaticString("__got"), |
| 1232 | 1244 | .segname = makeStaticString("__TEXT"), |
| 1233 | 1245 | .addr = text_section.addr + text_section.size, |
| ... | ... | @@ -1236,18 +1248,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1236 | 1248 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, |
| 1237 | 1249 | .reloff = 0, |
| 1238 | 1250 | .nreloc = 0, |
| 1239 | | .flags = macho.S_REGULAR, |
| 1251 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1240 | 1252 | .reserved1 = 0, |
| 1241 | 1253 | .reserved2 = 0, |
| 1242 | 1254 | .reserved3 = 0, |
| 1243 | 1255 | }); |
| 1244 | 1256 | |
| 1245 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1246 | 1257 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| 1247 | | text_segment.vmsize += added_size; |
| 1248 | | text_segment.filesize += added_size; |
| 1249 | | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1250 | | text_segment.nsects += 1; |
| 1258 | text_segment.inner.vmsize += added_size; |
| 1259 | text_segment.inner.filesize += added_size; |
| 1260 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1261 | text_segment.inner.nsects += 1; |
| 1251 | 1262 | self.cmd_table_dirty = true; |
| 1252 | 1263 | } |
| 1253 | 1264 | if (self.linkedit_segment_cmd_index == null) { |
| ... | ... | @@ -1255,13 +1266,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1255 | 1266 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1256 | 1267 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1257 | 1268 | const initprot = macho.VM_PROT_READ; |
| 1258 | | const off = text_segment.fileoff + text_segment.filesize; |
| 1269 | const off = text_segment.inner.fileoff + text_segment.inner.filesize; |
| 1259 | 1270 | try self.load_commands.append(self.base.allocator, .{ |
| 1260 | | .Segment = .{ |
| 1271 | .Segment = SegmentCommand.empty(.{ |
| 1261 | 1272 | .cmd = macho.LC_SEGMENT_64, |
| 1262 | 1273 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1263 | 1274 | .segname = makeStaticString("__LINKEDIT"), |
| 1264 | | .vmaddr = text_segment.vmaddr + text_segment.vmsize, |
| 1275 | .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize, |
| 1265 | 1276 | .vmsize = 0, |
| 1266 | 1277 | .fileoff = off, |
| 1267 | 1278 | .filesize = 0, |
| ... | ... | @@ -1269,7 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1269 | 1280 | .initprot = initprot, |
| 1270 | 1281 | .nsects = 0, |
| 1271 | 1282 | .flags = 0, |
| 1272 | | }, |
| 1283 | }), |
| 1273 | 1284 | }); |
| 1274 | 1285 | self.linkedit_segment_next_offset = @intCast(u32, off); |
| 1275 | 1286 | self.cmd_table_dirty = true; |
| ... | ... | @@ -1277,7 +1288,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1277 | 1288 | if (self.dyld_info_cmd_index == null) { |
| 1278 | 1289 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1279 | 1290 | try self.load_commands.append(self.base.allocator, .{ |
| 1280 | | .DyldInfo = .{ |
| 1291 | .DyldInfoOnly = .{ |
| 1281 | 1292 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| 1282 | 1293 | .cmdsize = @sizeOf(macho.dyld_info_command), |
| 1283 | 1294 | .rebase_off = 0, |
| ... | ... | @@ -1339,15 +1350,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1339 | 1350 | if (self.dylinker_cmd_index == null) { |
| 1340 | 1351 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1341 | 1352 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); |
| 1342 | | try self.load_commands.append(self.base.allocator, .{ |
| 1343 | | .Dylinker = .{ |
| 1344 | | .cmd = macho.LC_LOAD_DYLINKER, |
| 1345 | | .cmdsize = @intCast(u32, cmdsize), |
| 1346 | | .name = @sizeOf(macho.dylinker_command), |
| 1347 | | }, |
| 1353 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ |
| 1354 | .cmd = macho.LC_LOAD_DYLINKER, |
| 1355 | .cmdsize = @intCast(u32, cmdsize), |
| 1356 | .name = @sizeOf(macho.dylinker_command), |
| 1348 | 1357 | }); |
| 1358 | dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); |
| 1359 | mem.set(u8, dylinker_cmd.data, 0); |
| 1360 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); |
| 1361 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); |
| 1349 | 1362 | self.cmd_table_dirty = true; |
| 1350 | | self.dylinker_cmd_dirty = true; |
| 1351 | 1363 | } |
| 1352 | 1364 | if (self.libsystem_cmd_index == null) { |
| 1353 | 1365 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -1355,26 +1367,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1355 | 1367 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 1356 | 1368 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. |
| 1357 | 1369 | const min_version = 0x0; |
| 1358 | | const dylib = .{ |
| 1359 | | .name = @sizeOf(macho.dylib_command), |
| 1360 | | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1361 | | .current_version = min_version, |
| 1362 | | .compatibility_version = min_version, |
| 1363 | | }; |
| 1364 | | try self.load_commands.append(self.base.allocator, .{ |
| 1365 | | .Dylib = .{ |
| 1366 | | .cmd = macho.LC_LOAD_DYLIB, |
| 1367 | | .cmdsize = @intCast(u32, cmdsize), |
| 1368 | | .dylib = dylib, |
| 1370 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ |
| 1371 | .cmd = macho.LC_LOAD_DYLIB, |
| 1372 | .cmdsize = @intCast(u32, cmdsize), |
| 1373 | .dylib = .{ |
| 1374 | .name = @sizeOf(macho.dylib_command), |
| 1375 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1376 | .current_version = min_version, |
| 1377 | .compatibility_version = min_version, |
| 1369 | 1378 | }, |
| 1370 | 1379 | }); |
| 1380 | dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); |
| 1381 | mem.set(u8, dylib_cmd.data, 0); |
| 1382 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 1383 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 1371 | 1384 | self.cmd_table_dirty = true; |
| 1372 | | self.libsystem_cmd_dirty = true; |
| 1373 | 1385 | } |
| 1374 | 1386 | if (self.main_cmd_index == null) { |
| 1375 | 1387 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1376 | 1388 | try self.load_commands.append(self.base.allocator, .{ |
| 1377 | | .EntryPoint = .{ |
| 1389 | .Main = .{ |
| 1378 | 1390 | .cmd = macho.LC_MAIN, |
| 1379 | 1391 | .cmdsize = @sizeOf(macho.entry_point_command), |
| 1380 | 1392 | .entryoff = 0x0, |
| ... | ... | @@ -1395,7 +1407,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1395 | 1407 | const ver = self.base.options.target.os.version_range.semver.min; |
| 1396 | 1408 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; |
| 1397 | 1409 | try self.load_commands.append(self.base.allocator, .{ |
| 1398 | | .MinVersion = .{ |
| 1410 | .VersionMin = .{ |
| 1399 | 1411 | .cmd = cmd, |
| 1400 | 1412 | .cmdsize = @sizeOf(macho.version_min_command), |
| 1401 | 1413 | .version = version, |
| ... | ... | @@ -1438,7 +1450,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1438 | 1450 | } |
| 1439 | 1451 | |
| 1440 | 1452 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 1441 | | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1453 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1454 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1442 | 1455 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 1443 | 1456 | |
| 1444 | 1457 | // We use these to indicate our intention to update metadata, placing the new block, |
| ... | ... | @@ -1536,7 +1549,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1536 | 1549 | return vaddr; |
| 1537 | 1550 | } |
| 1538 | 1551 | |
| 1539 | | fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 1552 | pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 1540 | 1553 | var buf = [_]u8{0} ** 16; |
| 1541 | 1554 | if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); |
| 1542 | 1555 | mem.copy(u8, buf[0..], bytes); |
| ... | ... | @@ -1580,15 +1593,18 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1580 | 1593 | return test_end; |
| 1581 | 1594 | } |
| 1582 | 1595 | } |
| 1583 | | for (self.sections.items) |section| { |
| 1584 | | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 1585 | | const test_end = section.offset + increased_size; |
| 1586 | | if (end > section.offset and start < test_end) { |
| 1587 | | return test_end; |
| 1596 | if (self.text_segment_cmd_index) |text_index| { |
| 1597 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1598 | for (text_segment.sections.items) |section| { |
| 1599 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 1600 | const test_end = section.offset + increased_size; |
| 1601 | if (end > section.offset and start < test_end) { |
| 1602 | return test_end; |
| 1603 | } |
| 1588 | 1604 | } |
| 1589 | 1605 | } |
| 1590 | 1606 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1591 | | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1607 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; |
| 1592 | 1608 | const tight_size = dyld_info.export_size; |
| 1593 | 1609 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; |
| 1594 | 1610 | const test_end = dyld_info.export_off + increased_size; |
| ... | ... | @@ -1625,12 +1641,15 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 1625 | 1641 | const off = @sizeOf(macho.mach_header_64); |
| 1626 | 1642 | if (off > start and off < min_pos) min_pos = off; |
| 1627 | 1643 | } |
| 1628 | | for (self.sections.items) |section| { |
| 1629 | | if (section.offset <= start) continue; |
| 1630 | | if (section.offset < min_pos) min_pos = section.offset; |
| 1644 | if (self.text_segment_cmd_index) |text_index| { |
| 1645 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1646 | for (text_segment.sections.items) |section| { |
| 1647 | if (section.offset <= start) continue; |
| 1648 | if (section.offset < min_pos) min_pos = section.offset; |
| 1649 | } |
| 1631 | 1650 | } |
| 1632 | 1651 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1633 | | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1652 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; |
| 1634 | 1653 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; |
| 1635 | 1654 | } |
| 1636 | 1655 | if (self.symtab_cmd_index) |symtab_index| { |
| ... | ... | @@ -1650,7 +1669,8 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { |
| 1650 | 1669 | } |
| 1651 | 1670 | |
| 1652 | 1671 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1653 | | const sect = &self.sections.items[self.got_section_index.?]; |
| 1672 | const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1673 | const sect = &text_semgent.sections.items[self.got_section_index.?]; |
| 1654 | 1674 | const off = sect.offset + @sizeOf(u64) * index; |
| 1655 | 1675 | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| 1656 | 1676 | |
| ... | ... | @@ -1718,9 +1738,9 @@ fn writeSymbolTable(self: *MachO) !void { |
| 1718 | 1738 | |
| 1719 | 1739 | // Advance size of __LINKEDIT segment |
| 1720 | 1740 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1721 | | linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1722 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1723 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1741 | linkedit.inner.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1742 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1743 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1724 | 1744 | } |
| 1725 | 1745 | self.cmd_table_dirty = true; |
| 1726 | 1746 | } |
| ... | ... | @@ -1728,16 +1748,16 @@ fn writeSymbolTable(self: *MachO) !void { |
| 1728 | 1748 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1729 | 1749 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1730 | 1750 | const fileoff = self.linkedit_segment_next_offset.?; |
| 1731 | | const datasize: u32 = 0x1000; // TODO Calculate the expected size of the signature. |
| 1751 | const datasize = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff); |
| 1732 | 1752 | code_sig_cmd.dataoff = fileoff; |
| 1733 | 1753 | code_sig_cmd.datasize = datasize; |
| 1734 | 1754 | |
| 1735 | 1755 | self.linkedit_segment_next_offset = fileoff + datasize; |
| 1736 | 1756 | // Advance size of __LINKEDIT segment |
| 1737 | 1757 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1738 | | linkedit.filesize += datasize; |
| 1739 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1740 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1758 | linkedit.inner.filesize += datasize; |
| 1759 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1760 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1741 | 1761 | } |
| 1742 | 1762 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); |
| 1743 | 1763 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| ... | ... | @@ -1746,15 +1766,21 @@ fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1746 | 1766 | } |
| 1747 | 1767 | |
| 1748 | 1768 | fn writeCodeSignature(self: *MachO) !void { |
| 1749 | | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1769 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1770 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1771 | |
| 1750 | 1772 | var code_sig = CodeSignature.init(self.base.allocator); |
| 1751 | 1773 | defer code_sig.deinit(); |
| 1752 | | |
| 1753 | | try code_sig.calcAdhocSignature(self); |
| 1774 | try code_sig.calcAdhocSignature( |
| 1775 | self.base.file.?, |
| 1776 | self.base.options.emit.?.sub_path, |
| 1777 | text_segment.inner, |
| 1778 | code_sig_cmd, |
| 1779 | self.base.options.output_mode, |
| 1780 | ); |
| 1754 | 1781 | |
| 1755 | 1782 | var buffer = try self.base.allocator.alloc(u8, code_sig.size()); |
| 1756 | 1783 | defer self.base.allocator.free(buffer); |
| 1757 | | |
| 1758 | 1784 | code_sig.write(buffer); |
| 1759 | 1785 | |
| 1760 | 1786 | log.debug("writing code signature from 0x{x} to 0x{x}\n", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); |
| ... | ... | @@ -1772,10 +1798,10 @@ fn writeExportTrie(self: *MachO) !void { |
| 1772 | 1798 | for (self.global_symbols.items) |symbol| { |
| 1773 | 1799 | // TODO figure out if we should put all global symbols into the export trie |
| 1774 | 1800 | const name = self.getString(symbol.n_strx); |
| 1775 | | assert(symbol.n_value >= text_segment.vmaddr); |
| 1801 | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| 1776 | 1802 | try trie.put(self.base.allocator, .{ |
| 1777 | 1803 | .name = name, |
| 1778 | | .vmaddr_offset = symbol.n_value - text_segment.vmaddr, |
| 1804 | .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, |
| 1779 | 1805 | .export_flags = 0, // TODO workout creation of export flags |
| 1780 | 1806 | }); |
| 1781 | 1807 | } |
| ... | ... | @@ -1785,7 +1811,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 1785 | 1811 | |
| 1786 | 1812 | try trie.writeULEB128Mem(self.base.allocator, &buffer); |
| 1787 | 1813 | |
| 1788 | | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1814 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1789 | 1815 | const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); |
| 1790 | 1816 | dyld_info.export_off = self.linkedit_segment_next_offset.?; |
| 1791 | 1817 | dyld_info.export_size = export_size; |
| ... | ... | @@ -1801,9 +1827,9 @@ fn writeExportTrie(self: *MachO) !void { |
| 1801 | 1827 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; |
| 1802 | 1828 | // Advance size of __LINKEDIT segment |
| 1803 | 1829 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1804 | | linkedit.filesize += dyld_info.export_size; |
| 1805 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1806 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1830 | linkedit.inner.filesize += dyld_info.export_size; |
| 1831 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1832 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1807 | 1833 | } |
| 1808 | 1834 | self.cmd_table_dirty = true; |
| 1809 | 1835 | } |
| ... | ... | @@ -1826,103 +1852,41 @@ fn writeStringTable(self: *MachO) !void { |
| 1826 | 1852 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; |
| 1827 | 1853 | // Advance size of __LINKEDIT segment |
| 1828 | 1854 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1829 | | linkedit.filesize += symtab.strsize; |
| 1830 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1831 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1855 | linkedit.inner.filesize += symtab.strsize; |
| 1856 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1857 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1832 | 1858 | } |
| 1833 | 1859 | self.cmd_table_dirty = true; |
| 1834 | 1860 | } |
| 1835 | 1861 | |
| 1836 | | fn writeCmdHeaders(self: *MachO) !void { |
| 1837 | | assert(self.cmd_table_dirty); |
| 1838 | | |
| 1839 | | // Write all load command headers first. |
| 1840 | | // Since command sizes are up-to-date and accurate, we will correctly |
| 1841 | | // leave space for any section headers that any of the segment load |
| 1842 | | // commands might consist of. |
| 1843 | | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 1844 | | for (self.load_commands.items) |cmd| { |
| 1845 | | try cmd.write(&self.base.file.?, last_cmd_offset); |
| 1846 | | last_cmd_offset += cmd.cmdsize(); |
| 1862 | /// Writes all load commands and section headers. |
| 1863 | fn writeLoadCommands(self: *MachO) !void { |
| 1864 | var sizeofcmds: usize = 0; |
| 1865 | for (self.load_commands.items) |lc| { |
| 1866 | sizeofcmds += lc.cmdsize(); |
| 1847 | 1867 | } |
| 1848 | | { |
| 1849 | | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { |
| 1850 | | var i: usize = 0; |
| 1851 | | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| 1852 | | while (i < text_segment_index) : (i += 1) { |
| 1853 | | cmdsize += self.load_commands.items[i].cmdsize(); |
| 1854 | | } |
| 1855 | | break :blk cmdsize; |
| 1856 | | } else { |
| 1857 | | // If we've landed in here, we are building a MachO object file, so we have |
| 1858 | | // only one, noname segment to append this section header to. |
| 1859 | | return error.TODOImplementWritingObjFiles; |
| 1860 | | }; |
| 1861 | | // write sections belonging to __TEXT segment |
| 1862 | | // TODO section indices should belong to each Segment, and we should iterate dynamically. |
| 1863 | | const id = self.text_section_index.?; |
| 1864 | | log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); |
| 1865 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); |
| 1868 | |
| 1869 | var buffer = try self.base.allocator.alloc(u8, sizeofcmds); |
| 1870 | defer self.base.allocator.free(buffer); |
| 1871 | var writer = std.io.fixedBufferStream(buffer).writer(); |
| 1872 | for (self.load_commands.items) |lc| { |
| 1873 | try lc.write(writer); |
| 1866 | 1874 | } |
| 1875 | |
| 1876 | try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); |
| 1867 | 1877 | } |
| 1868 | 1878 | |
| 1869 | 1879 | /// Writes Mach-O file header. |
| 1870 | | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1871 | | /// variables. |
| 1872 | | fn writeMachOHeader(self: *MachO) !void { |
| 1873 | | var hdr: macho.mach_header_64 = undefined; |
| 1874 | | hdr.magic = macho.MH_MAGIC_64; |
| 1875 | | |
| 1876 | | const CpuInfo = struct { |
| 1877 | | cpu_type: macho.cpu_type_t, |
| 1878 | | cpu_subtype: macho.cpu_subtype_t, |
| 1879 | | }; |
| 1880 | | |
| 1881 | | const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) { |
| 1882 | | .aarch64 => .{ |
| 1883 | | .cpu_type = macho.CPU_TYPE_ARM64, |
| 1884 | | .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL, |
| 1885 | | }, |
| 1886 | | .x86_64 => .{ |
| 1887 | | .cpu_type = macho.CPU_TYPE_X86_64, |
| 1888 | | .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL, |
| 1889 | | }, |
| 1890 | | else => return error.UnsupportedMachOArchitecture, |
| 1891 | | }; |
| 1892 | | hdr.cputype = cpu_info.cpu_type; |
| 1893 | | hdr.cpusubtype = cpu_info.cpu_subtype; |
| 1894 | | |
| 1895 | | const filetype: u32 = switch (self.base.options.output_mode) { |
| 1896 | | .Exe => macho.MH_EXECUTE, |
| 1897 | | .Obj => macho.MH_OBJECT, |
| 1898 | | .Lib => switch (self.base.options.link_mode) { |
| 1899 | | .Static => return error.TODOStaticLibMachOType, |
| 1900 | | .Dynamic => macho.MH_DYLIB, |
| 1901 | | }, |
| 1902 | | }; |
| 1903 | | hdr.filetype = filetype; |
| 1904 | | hdr.ncmds = @intCast(u32, self.load_commands.items.len); |
| 1905 | | |
| 1880 | fn writeHeader(self: *MachO) !void { |
| 1881 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); |
| 1906 | 1882 | var sizeofcmds: u32 = 0; |
| 1907 | 1883 | for (self.load_commands.items) |cmd| { |
| 1908 | 1884 | sizeofcmds += cmd.cmdsize(); |
| 1909 | 1885 | } |
| 1910 | | |
| 1911 | | hdr.sizeofcmds = sizeofcmds; |
| 1912 | | |
| 1913 | | switch (self.base.options.output_mode) { |
| 1914 | | .Exe => { |
| 1915 | | hdr.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE; |
| 1916 | | }, |
| 1917 | | else => { |
| 1918 | | hdr.flags = 0; |
| 1919 | | }, |
| 1920 | | } |
| 1921 | | hdr.reserved = 0; |
| 1922 | | |
| 1923 | | log.debug("writing Mach-O header {}\n", .{hdr}); |
| 1924 | | |
| 1925 | | try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0); |
| 1886 | self.header.?.sizeofcmds = sizeofcmds; |
| 1887 | log.debug("writing Mach-O header {}\n", .{self.header.?}); |
| 1888 | const slice = [1]macho.mach_header_64{self.header.?}; |
| 1889 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0); |
| 1926 | 1890 | } |
| 1927 | 1891 | |
| 1928 | 1892 | /// Saturating multiplication |
| ... | ... | @@ -1930,3 +1894,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1930 | 1894 | const T = @TypeOf(a, b); |
| 1931 | 1895 | return std.math.mul(T, a, b) catch std.math.maxInt(T); |
| 1932 | 1896 | } |
| 1897 | |
| 1898 | /// Parse MachO contents from existing binary file. |
| 1899 | /// TODO This method is incomplete and currently parses only the header |
| 1900 | /// plus the load commands. |
| 1901 | fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 1902 | self.base.file = file; |
| 1903 | var reader = file.reader(); |
| 1904 | const header = try reader.readStruct(macho.mach_header_64); |
| 1905 | try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds); |
| 1906 | var i: u16 = 0; |
| 1907 | while (i < header.ncmds) : (i += 1) { |
| 1908 | const cmd = try LoadCommand.read(self.base.allocator, reader); |
| 1909 | switch (cmd.cmd()) { |
| 1910 | macho.LC_SEGMENT_64 => { |
| 1911 | const x = cmd.Segment; |
| 1912 | if (isSegmentOrSection(&x.inner.segname, "__LINKEDIT")) { |
| 1913 | self.linkedit_segment_cmd_index = i; |
| 1914 | } else if (isSegmentOrSection(&x.inner.segname, "__TEXT")) { |
| 1915 | self.text_segment_cmd_index = i; |
| 1916 | for (x.sections.items) |sect, j| { |
| 1917 | if (isSegmentOrSection(&sect.sectname, "__text")) { |
| 1918 | self.text_section_index = @intCast(u16, j); |
| 1919 | } |
| 1920 | } |
| 1921 | } |
| 1922 | }, |
| 1923 | macho.LC_SYMTAB => { |
| 1924 | self.symtab_cmd_index = i; |
| 1925 | }, |
| 1926 | macho.LC_CODE_SIGNATURE => { |
| 1927 | self.code_signature_cmd_index = i; |
| 1928 | }, |
| 1929 | // TODO populate more MachO fields |
| 1930 | else => {}, |
| 1931 | } |
| 1932 | self.load_commands.appendAssumeCapacity(cmd); |
| 1933 | } |
| 1934 | self.header = header; |
| 1935 | |
| 1936 | // TODO parse memory mapped segments |
| 1937 | } |
| 1938 | |
| 1939 | fn isSegmentOrSection(name: *const [16]u8, needle: []const u8) bool { |
| 1940 | return mem.eql(u8, mem.trimRight(u8, name.*[0..], &[_]u8{0}), needle); |
| 1941 | } |