| ... | @@ -24,55 +24,9 @@ const target_util = @import("../target.zig"); | ... | @@ -24,55 +24,9 @@ const target_util = @import("../target.zig"); |
| 24 | const Trie = @import("MachO/Trie.zig"); | 24 | const Trie = @import("MachO/Trie.zig"); |
| 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | | 26 | |
| 27 | pub const base_tag: File.Tag = File.Tag.macho; | 27 | usingnamespace @import("MachO/commands.zig"); |
| 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 | } | | |
| 70 | | 28 | |
| 71 | fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void { | 29 | pub const base_tag: File.Tag = File.Tag.macho; |
| 72 | const slice = [1]@TypeOf(cmd){cmd}; | | |
| 73 | return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset); | | |
| 74 | } | | |
| 75 | }; | | |
| 76 | | 30 | |
| 77 | base: File, | 31 | base: File, |
| 78 | | 32 | |
| ... | @@ -80,6 +34,9 @@ base: File, | ... | @@ -80,6 +34,9 @@ base: File, |
| 80 | /// 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. |
| 81 | page_size: u16, | 35 | page_size: u16, |
| 82 | | 36 | |
| | 37 | /// Mach-O header |
| | 38 | header: ?macho.mach_header_64 = null, |
| | 39 | |
| 83 | /// Table of all load commands | 40 | /// Table of all load commands |
| 84 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 41 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 85 | /// __PAGEZERO segment | 42 | /// __PAGEZERO segment |
| ... | @@ -115,18 +72,14 @@ source_version_cmd_index: ?u16 = null, | ... | @@ -115,18 +72,14 @@ source_version_cmd_index: ?u16 = null, |
| 115 | /// Code signature | 72 | /// Code signature |
| 116 | code_signature_cmd_index: ?u16 = null, | 73 | code_signature_cmd_index: ?u16 = null, |
| 117 | | 74 | |
| 118 | /// Table of all sections | 75 | /// Index into __TEXT,__text section. |
| 119 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, | | |
| 120 | | | |
| 121 | /// __TEXT,__text section | | |
| 122 | text_section_index: ?u16 = null, | 76 | text_section_index: ?u16 = null, |
| 123 | | 77 | /// Index into __TEXT,__got section. |
| 124 | /// __DATA,__got section | | |
| 125 | got_section_index: ?u16 = null, | 78 | got_section_index: ?u16 = null, |
| 126 | | 79 | /// The absolute address of the entry point. |
| 127 | entry_addr: ?u64 = null, | 80 | entry_addr: ?u64 = null, |
| 128 | | 81 | |
| 129 | // TODO move this into each Segment aggregator | 82 | /// TODO move this into each Segment aggregator |
| 130 | linkedit_segment_next_offset: ?u32 = null, | 83 | linkedit_segment_next_offset: ?u32 = null, |
| 131 | | 84 | |
| 132 | /// Table of all local symbols | 85 | /// Table of all local symbols |
| ... | @@ -154,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, | ... | @@ -154,8 +107,6 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 154 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 155 | | 108 | |
| 156 | cmd_table_dirty: bool = false, | 109 | cmd_table_dirty: bool = false, |
| 157 | dylinker_cmd_dirty: bool = false, | | |
| 158 | libsystem_cmd_dirty: bool = false, | | |
| 159 | | 110 | |
| 160 | /// A list of text blocks that have surplus capacity. This list can have false | 111 | /// A list of text blocks that have surplus capacity. This list can have false |
| 161 | /// positives, as functions grow and shrink over time, only sometimes being added | 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,40 +306,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 355 | if (self.entry_addr) |addr| { | 306 | if (self.entry_addr) |addr| { |
| 356 | // Update LC_MAIN with entry offset. | 307 | // Update LC_MAIN with entry offset. |
| 357 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 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; | 309 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; |
| 359 | main_cmd.entryoff = addr - text_segment.vmaddr; | 310 | main_cmd.entryoff = addr - text_segment.inner.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; | | |
| 386 | } | 311 | } |
| 387 | | | |
| 388 | try self.writeExportTrie(); | 312 | try self.writeExportTrie(); |
| 389 | try self.writeSymbolTable(); | 313 | try self.writeSymbolTable(); |
| 390 | try self.writeStringTable(); | 314 | try self.writeStringTable(); |
| 391 | | | |
| 392 | // Preallocate space for the code signature. | 315 | // Preallocate space for the code signature. |
| 393 | // We need to do this at this stage so that we have the load commands with proper values | 316 | // We need to do this at this stage so that we have the load commands with proper values |
| 394 | // written out to the file. | 317 | // written out to the file. |
| ... | @@ -401,8 +324,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -401,8 +324,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 401 | } | 324 | } |
| 402 | | 325 | |
| 403 | if (self.cmd_table_dirty) { | 326 | if (self.cmd_table_dirty) { |
| 404 | try self.writeCmdHeaders(); | 327 | try self.writeLoadCommands(); |
| 405 | try self.writeMachOHeader(); | 328 | try self.writeHeader(); |
| 406 | self.cmd_table_dirty = false; | 329 | self.cmd_table_dirty = false; |
| 407 | } | 330 | } |
| 408 | | 331 | |
| ... | @@ -415,8 +338,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -415,8 +338,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 415 | } | 338 | } |
| 416 | | 339 | |
| 417 | assert(!self.cmd_table_dirty); | 340 | assert(!self.cmd_table_dirty); |
| 418 | assert(!self.dylinker_cmd_dirty); | | |
| 419 | assert(!self.libsystem_cmd_dirty); | | |
| 420 | | 341 | |
| 421 | switch (self.base.options.output_mode) { | 342 | switch (self.base.options.output_mode) { |
| 422 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last | 343 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last |
| ... | @@ -760,7 +681,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -760,7 +681,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 760 | if (result.term != .Exited or result.term.Exited != 0) { | 681 | if (result.term != .Exited or result.term.Exited != 0) { |
| 761 | // TODO parse this output and surface with the Compilation API rather than | 682 | // TODO parse this output and surface with the Compilation API rather than |
| 762 | // directly outputting to stderr here. | 683 | // directly outputting to stderr here. |
| 763 | std.debug.print("{}", .{result.stderr}); | 684 | std.log.err("{}", .{result.stderr}); |
| 764 | return error.LDReportedFailure; | 685 | return error.LDReportedFailure; |
| 765 | } | 686 | } |
| 766 | } else { | 687 | } else { |
| ... | @@ -795,12 +716,53 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -795,12 +716,53 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 795 | if (!ok) { | 716 | if (!ok) { |
| 796 | // TODO parse this output and surface with the Compilation API rather than | 717 | // TODO parse this output and surface with the Compilation API rather than |
| 797 | // directly outputting to stderr here. | 718 | // directly outputting to stderr here. |
| 798 | std.debug.print("{}", .{stderr_context.data.items}); | 719 | std.log.err("{}", .{stderr_context.data.items}); |
| 799 | return error.LLDReportedFailure; | 720 | return error.LLDReportedFailure; |
| 800 | } | 721 | } |
| 801 | if (stderr_context.data.items.len != 0) { | 722 | if (stderr_context.data.items.len != 0) { |
| 802 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 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,7 +819,9 @@ pub fn deinit(self: *MachO) void { |
| 857 | self.global_symbol_free_list.deinit(self.base.allocator); | 819 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 858 | self.local_symbols.deinit(self.base.allocator); | 820 | self.local_symbols.deinit(self.base.allocator); |
| 859 | self.local_symbol_free_list.deinit(self.base.allocator); | 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 | self.load_commands.deinit(self.base.allocator); | 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,7 +975,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1011 | } | 975 | } |
| 1012 | | 976 | |
| 1013 | // Perform PIE fixups (if any) | 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 | while (self.pie_fixups.popOrNull()) |fixup| { | 980 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1016 | const target_addr = fixup.address; | 981 | const target_addr = fixup.address; |
| 1017 | const this_addr = symbol.n_value + fixup.start; | 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,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 | const section_offset = symbol.n_value - text_section.addr; | 999 | const section_offset = symbol.n_value - text_section.addr; |
| 1035 | const file_offset = text_section.offset + section_offset; | 1000 | const file_offset = text_section.offset + section_offset; |
| 1036 | try self.base.file.?.pwriteAll(code, file_offset); | 1001 | try self.base.file.?.pwriteAll(code, file_offset); |
| ... | @@ -1145,10 +1110,57 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1145,10 +1110,57 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1145 | .Lib => return error.TODOImplementWritingLibFiles, | 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 | if (self.pagezero_segment_cmd_index == null) { | 1160 | if (self.pagezero_segment_cmd_index == null) { |
| 1149 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1161 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1150 | try self.load_commands.append(self.base.allocator, .{ | 1162 | try self.load_commands.append(self.base.allocator, .{ |
| 1151 | .Segment = .{ | 1163 | .Segment = SegmentCommand.empty(.{ |
| 1152 | .cmd = macho.LC_SEGMENT_64, | 1164 | .cmd = macho.LC_SEGMENT_64, |
| 1153 | .cmdsize = @sizeOf(macho.segment_command_64), | 1165 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1154 | .segname = makeStaticString("__PAGEZERO"), | 1166 | .segname = makeStaticString("__PAGEZERO"), |
| ... | @@ -1160,7 +1172,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1160,7 +1172,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1160 | .initprot = 0, | 1172 | .initprot = 0, |
| 1161 | .nsects = 0, | 1173 | .nsects = 0, |
| 1162 | .flags = 0, | 1174 | .flags = 0, |
| 1163 | }, | 1175 | }), |
| 1164 | }); | 1176 | }); |
| 1165 | self.cmd_table_dirty = true; | 1177 | self.cmd_table_dirty = true; |
| 1166 | } | 1178 | } |
| ... | @@ -1169,7 +1181,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1169,7 +1181,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1169 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | 1181 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1170 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; | 1182 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1171 | try self.load_commands.append(self.base.allocator, .{ | 1183 | try self.load_commands.append(self.base.allocator, .{ |
| 1172 | .Segment = .{ | 1184 | .Segment = SegmentCommand.empty(.{ |
| 1173 | .cmd = macho.LC_SEGMENT_64, | 1185 | .cmd = macho.LC_SEGMENT_64, |
| 1174 | .cmdsize = @sizeOf(macho.segment_command_64), | 1186 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1175 | .segname = makeStaticString("__TEXT"), | 1187 | .segname = makeStaticString("__TEXT"), |
| ... | @@ -1181,45 +1193,45 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1181,45 +1193,45 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1181 | .initprot = initprot, | 1193 | .initprot = initprot, |
| 1182 | .nsects = 0, | 1194 | .nsects = 0, |
| 1183 | .flags = 0, | 1195 | .flags = 0, |
| 1184 | }, | 1196 | }), |
| 1185 | }); | 1197 | }); |
| 1186 | self.cmd_table_dirty = true; | 1198 | self.cmd_table_dirty = true; |
| 1187 | } | 1199 | } |
| 1188 | if (self.text_section_index == null) { | 1200 | if (self.text_section_index == null) { |
| 1189 | self.text_section_index = @intCast(u16, self.sections.items.len); | | |
| 1190 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 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 | const program_code_size_hint = self.base.options.program_code_size_hint; | 1204 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1193 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); | 1205 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); |
| 1194 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? | 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 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 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 | .sectname = makeStaticString("__text"), | 1211 | .sectname = makeStaticString("__text"), |
| 1201 | .segname = makeStaticString("__TEXT"), | 1212 | .segname = makeStaticString("__TEXT"), |
| 1202 | .addr = text_segment.vmaddr + off, | 1213 | .addr = text_segment.inner.vmaddr + off, |
| 1203 | .size = file_size, | 1214 | .size = file_size, |
| 1204 | .offset = off, | 1215 | .offset = off, |
| 1205 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 | 1216 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 |
| 1206 | .reloff = 0, | 1217 | .reloff = 0, |
| 1207 | .nreloc = 0, | 1218 | .nreloc = 0, |
| 1208 | .flags = flags, | 1219 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1209 | .reserved1 = 0, | 1220 | .reserved1 = 0, |
| 1210 | .reserved2 = 0, | 1221 | .reserved2 = 0, |
| 1211 | .reserved3 = 0, | 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. | 1225 | text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1215 | text_segment.filesize = file_size + off; | 1226 | text_segment.inner.filesize = file_size + off; |
| 1216 | text_segment.cmdsize += @sizeOf(macho.section_64); | 1227 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1217 | text_segment.nsects += 1; | 1228 | text_segment.inner.nsects += 1; |
| 1218 | self.cmd_table_dirty = true; | 1229 | self.cmd_table_dirty = true; |
| 1219 | } | 1230 | } |
| 1220 | if (self.got_section_index == null) { | 1231 | if (self.got_section_index == null) { |
| 1221 | self.got_section_index = @intCast(u16, self.sections.items.len); | 1232 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1222 | const text_section = &self.sections.items[self.text_section_index.?]; | 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 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1236 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1225 | // TODO looking for free space should be done *within* a segment it belongs to | 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,7 +1239,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1227 | | 1239 | |
| 1228 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 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 | .sectname = makeStaticString("__got"), | 1243 | .sectname = makeStaticString("__got"), |
| 1232 | .segname = makeStaticString("__TEXT"), | 1244 | .segname = makeStaticString("__TEXT"), |
| 1233 | .addr = text_section.addr + text_section.size, | 1245 | .addr = text_section.addr + text_section.size, |
| ... | @@ -1236,18 +1248,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1236,18 +1248,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1236 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, | 1248 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, |
| 1237 | .reloff = 0, | 1249 | .reloff = 0, |
| 1238 | .nreloc = 0, | 1250 | .nreloc = 0, |
| 1239 | .flags = macho.S_REGULAR, | 1251 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1240 | .reserved1 = 0, | 1252 | .reserved1 = 0, |
| 1241 | .reserved2 = 0, | 1253 | .reserved2 = 0, |
| 1242 | .reserved3 = 0, | 1254 | .reserved3 = 0, |
| 1243 | }); | 1255 | }); |
| 1244 | | 1256 | |
| 1245 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | | |
| 1246 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); | 1257 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| 1247 | text_segment.vmsize += added_size; | 1258 | text_segment.inner.vmsize += added_size; |
| 1248 | text_segment.filesize += added_size; | 1259 | text_segment.inner.filesize += added_size; |
| 1249 | text_segment.cmdsize += @sizeOf(macho.section_64); | 1260 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1250 | text_segment.nsects += 1; | 1261 | text_segment.inner.nsects += 1; |
| 1251 | self.cmd_table_dirty = true; | 1262 | self.cmd_table_dirty = true; |
| 1252 | } | 1263 | } |
| 1253 | if (self.linkedit_segment_cmd_index == null) { | 1264 | if (self.linkedit_segment_cmd_index == null) { |
| ... | @@ -1255,13 +1266,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1255,13 +1266,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1255 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1266 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1256 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | 1267 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1257 | const initprot = macho.VM_PROT_READ; | 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 | try self.load_commands.append(self.base.allocator, .{ | 1270 | try self.load_commands.append(self.base.allocator, .{ |
| 1260 | .Segment = .{ | 1271 | .Segment = SegmentCommand.empty(.{ |
| 1261 | .cmd = macho.LC_SEGMENT_64, | 1272 | .cmd = macho.LC_SEGMENT_64, |
| 1262 | .cmdsize = @sizeOf(macho.segment_command_64), | 1273 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1263 | .segname = makeStaticString("__LINKEDIT"), | 1274 | .segname = makeStaticString("__LINKEDIT"), |
| 1264 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, | 1275 | .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize, |
| 1265 | .vmsize = 0, | 1276 | .vmsize = 0, |
| 1266 | .fileoff = off, | 1277 | .fileoff = off, |
| 1267 | .filesize = 0, | 1278 | .filesize = 0, |
| ... | @@ -1269,7 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1269,7 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1269 | .initprot = initprot, | 1280 | .initprot = initprot, |
| 1270 | .nsects = 0, | 1281 | .nsects = 0, |
| 1271 | .flags = 0, | 1282 | .flags = 0, |
| 1272 | }, | 1283 | }), |
| 1273 | }); | 1284 | }); |
| 1274 | self.linkedit_segment_next_offset = @intCast(u32, off); | 1285 | self.linkedit_segment_next_offset = @intCast(u32, off); |
| 1275 | self.cmd_table_dirty = true; | 1286 | self.cmd_table_dirty = true; |
| ... | @@ -1277,7 +1288,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1277,7 +1288,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1277 | if (self.dyld_info_cmd_index == null) { | 1288 | if (self.dyld_info_cmd_index == null) { |
| 1278 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | 1289 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1279 | try self.load_commands.append(self.base.allocator, .{ | 1290 | try self.load_commands.append(self.base.allocator, .{ |
| 1280 | .DyldInfo = .{ | 1291 | .DyldInfoOnly = .{ |
| 1281 | .cmd = macho.LC_DYLD_INFO_ONLY, | 1292 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| 1282 | .cmdsize = @sizeOf(macho.dyld_info_command), | 1293 | .cmdsize = @sizeOf(macho.dyld_info_command), |
| 1283 | .rebase_off = 0, | 1294 | .rebase_off = 0, |
| ... | @@ -1339,15 +1350,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1339,15 +1350,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1339 | if (self.dylinker_cmd_index == null) { | 1350 | if (self.dylinker_cmd_index == null) { |
| 1340 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | 1351 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1341 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); | 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, .{ | 1353 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ |
| 1343 | .Dylinker = .{ | 1354 | .cmd = macho.LC_LOAD_DYLINKER, |
| 1344 | .cmd = macho.LC_LOAD_DYLINKER, | 1355 | .cmdsize = @intCast(u32, cmdsize), |
| 1345 | .cmdsize = @intCast(u32, cmdsize), | 1356 | .name = @sizeOf(macho.dylinker_command), |
| 1346 | .name = @sizeOf(macho.dylinker_command), | | |
| 1347 | }, | | |
| 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 | self.cmd_table_dirty = true; | 1362 | self.cmd_table_dirty = true; |
| 1350 | self.dylinker_cmd_dirty = true; | | |
| 1351 | } | 1363 | } |
| 1352 | if (self.libsystem_cmd_index == null) { | 1364 | if (self.libsystem_cmd_index == null) { |
| 1353 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 1365 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1355,26 +1367,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1355,26 +1367,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1355 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | 1367 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 1356 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. | 1368 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. |
| 1357 | const min_version = 0x0; | 1369 | const min_version = 0x0; |
| 1358 | const dylib = .{ | 1370 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ |
| 1359 | .name = @sizeOf(macho.dylib_command), | 1371 | .cmd = macho.LC_LOAD_DYLIB, |
| 1360 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | 1372 | .cmdsize = @intCast(u32, cmdsize), |
| 1361 | .current_version = min_version, | 1373 | .dylib = .{ |
| 1362 | .compatibility_version = min_version, | 1374 | .name = @sizeOf(macho.dylib_command), |
| 1363 | }; | 1375 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1364 | try self.load_commands.append(self.base.allocator, .{ | 1376 | .current_version = min_version, |
| 1365 | .Dylib = .{ | 1377 | .compatibility_version = min_version, |
| 1366 | .cmd = macho.LC_LOAD_DYLIB, | | |
| 1367 | .cmdsize = @intCast(u32, cmdsize), | | |
| 1368 | .dylib = dylib, | | |
| 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 | self.cmd_table_dirty = true; | 1384 | self.cmd_table_dirty = true; |
| 1372 | self.libsystem_cmd_dirty = true; | | |
| 1373 | } | 1385 | } |
| 1374 | if (self.main_cmd_index == null) { | 1386 | if (self.main_cmd_index == null) { |
| 1375 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | 1387 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1376 | try self.load_commands.append(self.base.allocator, .{ | 1388 | try self.load_commands.append(self.base.allocator, .{ |
| 1377 | .EntryPoint = .{ | 1389 | .Main = .{ |
| 1378 | .cmd = macho.LC_MAIN, | 1390 | .cmd = macho.LC_MAIN, |
| 1379 | .cmdsize = @sizeOf(macho.entry_point_command), | 1391 | .cmdsize = @sizeOf(macho.entry_point_command), |
| 1380 | .entryoff = 0x0, | 1392 | .entryoff = 0x0, |
| ... | @@ -1395,7 +1407,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1395,7 +1407,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1395 | const ver = self.base.options.target.os.version_range.semver.min; | 1407 | const ver = self.base.options.target.os.version_range.semver.min; |
| 1396 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; | 1408 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; |
| 1397 | try self.load_commands.append(self.base.allocator, .{ | 1409 | try self.load_commands.append(self.base.allocator, .{ |
| 1398 | .MinVersion = .{ | 1410 | .VersionMin = .{ |
| 1399 | .cmd = cmd, | 1411 | .cmd = cmd, |
| 1400 | .cmdsize = @sizeOf(macho.version_min_command), | 1412 | .cmdsize = @sizeOf(macho.version_min_command), |
| 1401 | .version = version, | 1413 | .version = version, |
| ... | @@ -1438,7 +1450,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1438,7 +1450,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1438 | } | 1450 | } |
| 1439 | | 1451 | |
| 1440 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | 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 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; | 1455 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 1443 | | 1456 | |
| 1444 | // We use these to indicate our intention to update metadata, placing the new block, | 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,7 +1549,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1536 | return vaddr; | 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 | var buf = [_]u8{0} ** 16; | 1553 | var buf = [_]u8{0} ** 16; |
| 1541 | if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); | 1554 | if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); |
| 1542 | mem.copy(u8, buf[0..], bytes); | 1555 | mem.copy(u8, buf[0..], bytes); |
| ... | @@ -1580,15 +1593,18 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -1580,15 +1593,18 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1580 | return test_end; | 1593 | return test_end; |
| 1581 | } | 1594 | } |
| 1582 | } | 1595 | } |
| 1583 | for (self.sections.items) |section| { | 1596 | if (self.text_segment_cmd_index) |text_index| { |
| 1584 | const increased_size = satMul(section.size, alloc_num) / alloc_den; | 1597 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1585 | const test_end = section.offset + increased_size; | 1598 | for (text_segment.sections.items) |section| { |
| 1586 | if (end > section.offset and start < test_end) { | 1599 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 1587 | return test_end; | 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 | if (self.dyld_info_cmd_index) |dyld_info_index| { | 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 | const tight_size = dyld_info.export_size; | 1608 | const tight_size = dyld_info.export_size; |
| 1593 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; | 1609 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; |
| 1594 | const test_end = dyld_info.export_off + increased_size; | 1610 | const test_end = dyld_info.export_off + increased_size; |
| ... | @@ -1625,12 +1641,15 @@ fn allocatedSize(self: *MachO, start: u64) u64 { | ... | @@ -1625,12 +1641,15 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 1625 | const off = @sizeOf(macho.mach_header_64); | 1641 | const off = @sizeOf(macho.mach_header_64); |
| 1626 | if (off > start and off < min_pos) min_pos = off; | 1642 | if (off > start and off < min_pos) min_pos = off; |
| 1627 | } | 1643 | } |
| 1628 | for (self.sections.items) |section| { | 1644 | if (self.text_segment_cmd_index) |text_index| { |
| 1629 | if (section.offset <= start) continue; | 1645 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1630 | if (section.offset < min_pos) min_pos = section.offset; | 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 | if (self.dyld_info_cmd_index) |dyld_info_index| { | 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 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; | 1653 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; |
| 1635 | } | 1654 | } |
| 1636 | if (self.symtab_cmd_index) |symtab_index| { | 1655 | if (self.symtab_cmd_index) |symtab_index| { |
| ... | @@ -1650,7 +1669,8 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { | ... | @@ -1650,7 +1669,8 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { |
| 1650 | } | 1669 | } |
| 1651 | | 1670 | |
| 1652 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | 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 | const off = sect.offset + @sizeOf(u64) * index; | 1674 | const off = sect.offset + @sizeOf(u64) * index; |
| 1655 | const vmaddr = sect.addr + @sizeOf(u64) * index; | 1675 | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| 1656 | | 1676 | |
| ... | @@ -1718,9 +1738,9 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -1718,9 +1738,9 @@ fn writeSymbolTable(self: *MachO) !void { |
| 1718 | | 1738 | |
| 1719 | // Advance size of __LINKEDIT segment | 1739 | // Advance size of __LINKEDIT segment |
| 1720 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1740 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1721 | linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); | 1741 | linkedit.inner.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1722 | if (linkedit.vmsize < linkedit.filesize) { | 1742 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1723 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); | 1743 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1724 | } | 1744 | } |
| 1725 | self.cmd_table_dirty = true; | 1745 | self.cmd_table_dirty = true; |
| 1726 | } | 1746 | } |
| ... | @@ -1728,16 +1748,16 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -1728,16 +1748,16 @@ fn writeSymbolTable(self: *MachO) !void { |
| 1728 | fn writeCodeSignaturePadding(self: *MachO) !void { | 1748 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1729 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | 1749 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1730 | const fileoff = self.linkedit_segment_next_offset.?; | 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 | code_sig_cmd.dataoff = fileoff; | 1752 | code_sig_cmd.dataoff = fileoff; |
| 1733 | code_sig_cmd.datasize = datasize; | 1753 | code_sig_cmd.datasize = datasize; |
| 1734 | | 1754 | |
| 1735 | self.linkedit_segment_next_offset = fileoff + datasize; | 1755 | self.linkedit_segment_next_offset = fileoff + datasize; |
| 1736 | // Advance size of __LINKEDIT segment | 1756 | // Advance size of __LINKEDIT segment |
| 1737 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1757 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1738 | linkedit.filesize += datasize; | 1758 | linkedit.inner.filesize += datasize; |
| 1739 | if (linkedit.vmsize < linkedit.filesize) { | 1759 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1740 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); | 1760 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1741 | } | 1761 | } |
| 1742 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); | 1762 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); |
| 1743 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | 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,15 +1766,21 @@ fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1746 | } | 1766 | } |
| 1747 | | 1767 | |
| 1748 | fn writeCodeSignature(self: *MachO) !void { | 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 | var code_sig = CodeSignature.init(self.base.allocator); | 1772 | var code_sig = CodeSignature.init(self.base.allocator); |
| 1751 | defer code_sig.deinit(); | 1773 | defer code_sig.deinit(); |
| 1752 | | 1774 | try code_sig.calcAdhocSignature( |
| 1753 | try code_sig.calcAdhocSignature(self); | 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 | var buffer = try self.base.allocator.alloc(u8, code_sig.size()); | 1782 | var buffer = try self.base.allocator.alloc(u8, code_sig.size()); |
| 1756 | defer self.base.allocator.free(buffer); | 1783 | defer self.base.allocator.free(buffer); |
| 1757 | | | |
| 1758 | code_sig.write(buffer); | 1784 | code_sig.write(buffer); |
| 1759 | | 1785 | |
| 1760 | log.debug("writing code signature from 0x{x} to 0x{x}\n", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); | 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,10 +1798,10 @@ fn writeExportTrie(self: *MachO) !void { |
| 1772 | for (self.global_symbols.items) |symbol| { | 1798 | for (self.global_symbols.items) |symbol| { |
| 1773 | // TODO figure out if we should put all global symbols into the export trie | 1799 | // TODO figure out if we should put all global symbols into the export trie |
| 1774 | const name = self.getString(symbol.n_strx); | 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 | try trie.put(self.base.allocator, .{ | 1802 | try trie.put(self.base.allocator, .{ |
| 1777 | .name = name, | 1803 | .name = name, |
| 1778 | .vmaddr_offset = symbol.n_value - text_segment.vmaddr, | 1804 | .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, |
| 1779 | .export_flags = 0, // TODO workout creation of export flags | 1805 | .export_flags = 0, // TODO workout creation of export flags |
| 1780 | }); | 1806 | }); |
| 1781 | } | 1807 | } |
| ... | @@ -1785,7 +1811,7 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -1785,7 +1811,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 1785 | | 1811 | |
| 1786 | try trie.writeULEB128Mem(self.base.allocator, &buffer); | 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 | const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); | 1815 | const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); |
| 1790 | dyld_info.export_off = self.linkedit_segment_next_offset.?; | 1816 | dyld_info.export_off = self.linkedit_segment_next_offset.?; |
| 1791 | dyld_info.export_size = export_size; | 1817 | dyld_info.export_size = export_size; |
| ... | @@ -1801,9 +1827,9 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -1801,9 +1827,9 @@ fn writeExportTrie(self: *MachO) !void { |
| 1801 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; | 1827 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; |
| 1802 | // Advance size of __LINKEDIT segment | 1828 | // Advance size of __LINKEDIT segment |
| 1803 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1829 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1804 | linkedit.filesize += dyld_info.export_size; | 1830 | linkedit.inner.filesize += dyld_info.export_size; |
| 1805 | if (linkedit.vmsize < linkedit.filesize) { | 1831 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1806 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); | 1832 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1807 | } | 1833 | } |
| 1808 | self.cmd_table_dirty = true; | 1834 | self.cmd_table_dirty = true; |
| 1809 | } | 1835 | } |
| ... | @@ -1826,103 +1852,41 @@ fn writeStringTable(self: *MachO) !void { | ... | @@ -1826,103 +1852,41 @@ fn writeStringTable(self: *MachO) !void { |
| 1826 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; | 1852 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; |
| 1827 | // Advance size of __LINKEDIT segment | 1853 | // Advance size of __LINKEDIT segment |
| 1828 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1854 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1829 | linkedit.filesize += symtab.strsize; | 1855 | linkedit.inner.filesize += symtab.strsize; |
| 1830 | if (linkedit.vmsize < linkedit.filesize) { | 1856 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1831 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); | 1857 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1832 | } | 1858 | } |
| 1833 | self.cmd_table_dirty = true; | 1859 | self.cmd_table_dirty = true; |
| 1834 | } | 1860 | } |
| 1835 | | 1861 | |
| 1836 | fn writeCmdHeaders(self: *MachO) !void { | 1862 | /// Writes all load commands and section headers. |
| 1837 | assert(self.cmd_table_dirty); | 1863 | fn writeLoadCommands(self: *MachO) !void { |
| 1838 | | 1864 | var sizeofcmds: usize = 0; |
| 1839 | // Write all load command headers first. | 1865 | for (self.load_commands.items) |lc| { |
| 1840 | // Since command sizes are up-to-date and accurate, we will correctly | 1866 | sizeofcmds += lc.cmdsize(); |
| 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(); | | |
| 1847 | } | 1867 | } |
| 1848 | { | 1868 | |
| 1849 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { | 1869 | var buffer = try self.base.allocator.alloc(u8, sizeofcmds); |
| 1850 | var i: usize = 0; | 1870 | defer self.base.allocator.free(buffer); |
| 1851 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | 1871 | var writer = std.io.fixedBufferStream(buffer).writer(); |
| 1852 | while (i < text_segment_index) : (i += 1) { | 1872 | for (self.load_commands.items) |lc| { |
| 1853 | cmdsize += self.load_commands.items[i].cmdsize(); | 1873 | try lc.write(writer); |
| 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); | | |
| 1866 | } | 1874 | } |
| | 1875 | |
| | 1876 | try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); |
| 1867 | } | 1877 | } |
| 1868 | | 1878 | |
| 1869 | /// Writes Mach-O file header. | 1879 | /// Writes Mach-O file header. |
| 1870 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping | 1880 | fn writeHeader(self: *MachO) !void { |
| 1871 | /// variables. | 1881 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); |
| 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 | | | |
| 1906 | var sizeofcmds: u32 = 0; | 1882 | var sizeofcmds: u32 = 0; |
| 1907 | for (self.load_commands.items) |cmd| { | 1883 | for (self.load_commands.items) |cmd| { |
| 1908 | sizeofcmds += cmd.cmdsize(); | 1884 | sizeofcmds += cmd.cmdsize(); |
| 1909 | } | 1885 | } |
| 1910 | | 1886 | self.header.?.sizeofcmds = sizeofcmds; |
| 1911 | hdr.sizeofcmds = sizeofcmds; | 1887 | log.debug("writing Mach-O header {}\n", .{self.header.?}); |
| 1912 | | 1888 | const slice = [1]macho.mach_header_64{self.header.?}; |
| 1913 | switch (self.base.options.output_mode) { | 1889 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0); |
| 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); | | |
| 1926 | } | 1890 | } |
| 1927 | | 1891 | |
| 1928 | /// Saturating multiplication | 1892 | /// Saturating multiplication |
| ... | @@ -1930,3 +1894,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | ... | @@ -1930,3 +1894,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1930 | const T = @TypeOf(a, b); | 1894 | const T = @TypeOf(a, b); |
| 1931 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | 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 | } |