| ... | @@ -37,6 +37,10 @@ page_size: u16, | ... | @@ -37,6 +37,10 @@ page_size: u16, |
| 37 | | 37 | |
| 38 | /// Mach-O header | 38 | /// Mach-O header |
| 39 | header: ?macho.mach_header_64 = null, | 39 | header: ?macho.mach_header_64 = null, |
| | 40 | /// We commit 0x1000 = 4096 bytes of space to the header and |
| | 41 | /// the table of load commands. This should be plenty for any |
| | 42 | /// potential future extensions. |
| | 43 | header_pad: u16 = 0x1000, |
| 40 | | 44 | |
| 41 | /// Table of all load commands | 45 | /// Table of all load commands |
| 42 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 46 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| ... | @@ -75,14 +79,11 @@ code_signature_cmd_index: ?u16 = null, | ... | @@ -75,14 +79,11 @@ code_signature_cmd_index: ?u16 = null, |
| 75 | | 79 | |
| 76 | /// Index into __TEXT,__text section. | 80 | /// Index into __TEXT,__text section. |
| 77 | text_section_index: ?u16 = null, | 81 | text_section_index: ?u16 = null, |
| 78 | /// Index into __TEXT,__got section. | 82 | /// Index into __TEXT,__ziggot section. |
| 79 | got_section_index: ?u16 = null, | 83 | got_section_index: ?u16 = null, |
| 80 | /// The absolute address of the entry point. | 84 | /// The absolute address of the entry point. |
| 81 | entry_addr: ?u64 = null, | 85 | entry_addr: ?u64 = null, |
| 82 | | 86 | |
| 83 | /// TODO move this into each Segment aggregator | | |
| 84 | linkedit_segment_next_offset: ?u32 = null, | | |
| 85 | | | |
| 86 | /// Table of all local symbols | 87 | /// Table of all local symbols |
| 87 | /// Internally references string table for names (which are optional). | 88 | /// Internally references string table for names (which are optional). |
| 88 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 89 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| ... | @@ -100,9 +101,7 @@ dyld_stub_binder_index: ?u16 = null, | ... | @@ -100,9 +101,7 @@ dyld_stub_binder_index: ?u16 = null, |
| 100 | /// Table of symbol names aka the string table. | 101 | /// Table of symbol names aka the string table. |
| 101 | string_table: std.ArrayListUnmanaged(u8) = .{}, | 102 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 102 | | 103 | |
| 103 | /// Table of symbol vaddr values. The values is the absolute vaddr value. | 104 | /// Table of trampolines to the actual symbols in __text section. |
| 104 | /// If the vaddr of the executable __TEXT segment vaddr changes, the entire offset | | |
| 105 | /// table needs to be rewritten. | | |
| 106 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | 105 | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 107 | | 106 | |
| 108 | /// Table of binding info entries. | 107 | /// Table of binding info entries. |
| ... | @@ -112,7 +111,13 @@ lazy_binding_info_table: LazyBindingInfoTable = .{}, | ... | @@ -112,7 +111,13 @@ lazy_binding_info_table: LazyBindingInfoTable = .{}, |
| 112 | | 111 | |
| 113 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 112 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 114 | | 113 | |
| 115 | cmd_table_dirty: bool = false, | 114 | offset_table_count_dirty: bool = false, |
| | 115 | header_dirty: bool = false, |
| | 116 | load_commands_dirty: bool = false, |
| | 117 | binding_info_dirty: bool = false, |
| | 118 | lazy_binding_info_dirty: bool = false, |
| | 119 | export_info_dirty: bool = false, |
| | 120 | string_table_dirty: bool = false, |
| 116 | | 121 | |
| 117 | /// A list of text blocks that have surplus capacity. This list can have false | 122 | /// A list of text blocks that have surplus capacity. This list can have false |
| 118 | /// positives, as functions grow and shrink over time, only sometimes being added | 123 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | @@ -317,10 +322,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -317,10 +322,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 317 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 322 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 318 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; | 323 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; |
| 319 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; | 324 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; |
| | 325 | self.load_commands_dirty = true; |
| 320 | } | 326 | } |
| | 327 | try self.writeBindingInfoTable(); |
| | 328 | try self.writeLazyBindingInfoTable(); |
| 321 | try self.writeExportTrie(); | 329 | try self.writeExportTrie(); |
| 322 | try self.writeSymbolTable(); | 330 | try self.writeAllGlobalAndUndefSymbols(); |
| 323 | try self.writeStringTable(); | 331 | try self.writeStringTable(); |
| | 332 | try self.updateLinkeditSegmentSizes(); |
| 324 | | 333 | |
| 325 | if (target.cpu.arch == .aarch64) { | 334 | if (target.cpu.arch == .aarch64) { |
| 326 | // Preallocate space for the code signature. | 335 | // Preallocate space for the code signature. |
| ... | @@ -335,21 +344,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -335,21 +344,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 335 | .Lib => return error.TODOImplementWritingLibFiles, | 344 | .Lib => return error.TODOImplementWritingLibFiles, |
| 336 | } | 345 | } |
| 337 | | 346 | |
| 338 | if (self.cmd_table_dirty) { | 347 | try self.writeLoadCommands(); |
| 339 | try self.writeLoadCommands(); | 348 | try self.writeHeader(); |
| 340 | try self.writeHeader(); | | |
| 341 | self.cmd_table_dirty = false; | | |
| 342 | } | | |
| 343 | | 349 | |
| 344 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 350 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 345 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 351 | log.debug("flushing. no_entry_point_found = true", .{}); |
| 346 | self.error_flags.no_entry_point_found = true; | 352 | self.error_flags.no_entry_point_found = true; |
| 347 | } else { | 353 | } else { |
| 348 | log.debug("flushing. no_entry_point_found = false\n", .{}); | 354 | log.debug("flushing. no_entry_point_found = false", .{}); |
| 349 | self.error_flags.no_entry_point_found = false; | 355 | self.error_flags.no_entry_point_found = false; |
| 350 | } | 356 | } |
| 351 | | 357 | |
| 352 | assert(!self.cmd_table_dirty); | 358 | assert(!self.offset_table_count_dirty); |
| | 359 | assert(!self.header_dirty); |
| | 360 | assert(!self.load_commands_dirty); |
| | 361 | assert(!self.binding_info_dirty); |
| | 362 | assert(!self.lazy_binding_info_dirty); |
| | 363 | assert(!self.export_info_dirty); |
| | 364 | assert(!self.string_table_dirty); |
| 353 | | 365 | |
| 354 | if (target.cpu.arch == .aarch64) { | 366 | if (target.cpu.arch == .aarch64) { |
| 355 | switch (output_mode) { | 367 | switch (output_mode) { |
| ... | @@ -768,9 +780,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -768,9 +780,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 768 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 780 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; |
| 769 | | 781 | |
| 770 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 782 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 771 | std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | 783 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); |
| 772 | std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | 784 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); |
| 773 | std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | 785 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); |
| 774 | return error.NotEnoughPadding; | 786 | return error.NotEnoughPadding; |
| 775 | } | 787 | } |
| 776 | | 788 | |
| ... | @@ -806,10 +818,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -806,10 +818,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 806 | mem.set(u8, dylib_cmd.data, 0); | 818 | mem.set(u8, dylib_cmd.data, 0); |
| 807 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | 819 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 808 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | 820 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| | 821 | self.header_dirty = true; |
| | 822 | self.load_commands_dirty = true; |
| 809 | | 823 | |
| 810 | if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) { | 824 | if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) { |
| 811 | std.log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{}); | 825 | log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{}); |
| 812 | std.log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{}); | 826 | log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{}); |
| 813 | return error.NoSymbolTableFound; | 827 | return error.NoSymbolTableFound; |
| 814 | } | 828 | } |
| 815 | | 829 | |
| ... | @@ -823,7 +837,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -823,7 +837,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 823 | symbol.dylib_ordinal = next_ordinal; | 837 | symbol.dylib_ordinal = next_ordinal; |
| 824 | } | 838 | } |
| 825 | | 839 | |
| 826 | // Write update dyld info | 840 | // Write updated dyld info. |
| 827 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 841 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 828 | { | 842 | { |
| 829 | const size = try self.binding_info_table.calcSize(); | 843 | const size = try self.binding_info_table.calcSize(); |
| ... | @@ -853,6 +867,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -853,6 +867,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 853 | // Write updated load commands and the header | 867 | // Write updated load commands and the header |
| 854 | try self.writeLoadCommands(); | 868 | try self.writeLoadCommands(); |
| 855 | try self.writeHeader(); | 869 | try self.writeHeader(); |
| | 870 | |
| | 871 | assert(!self.header_dirty); |
| | 872 | assert(!self.load_commands_dirty); |
| 856 | } | 873 | } |
| 857 | if (self.code_signature_cmd_index == null) outer: { | 874 | if (self.code_signature_cmd_index == null) outer: { |
| 858 | if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets. | 875 | if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets. |
| ... | @@ -862,15 +879,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -862,15 +879,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 862 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 879 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; |
| 863 | | 880 | |
| 864 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 881 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 865 | std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | 882 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); |
| 866 | std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | 883 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); |
| 867 | std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | 884 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); |
| 868 | return error.NotEnoughPadding; | 885 | return error.NotEnoughPadding; |
| 869 | } | 886 | } |
| 870 | | 887 | |
| 871 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | | |
| 872 | // TODO This is clunky. | | |
| 873 | self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64))); | | |
| 874 | // Add code signature load command | 888 | // Add code signature load command |
| 875 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | 889 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 876 | try self.load_commands.append(self.base.allocator, .{ | 890 | try self.load_commands.append(self.base.allocator, .{ |
| ... | @@ -881,6 +895,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -881,6 +895,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 881 | .datasize = 0, | 895 | .datasize = 0, |
| 882 | }, | 896 | }, |
| 883 | }); | 897 | }); |
| | 898 | self.header_dirty = true; |
| | 899 | self.load_commands_dirty = true; |
| 884 | | 900 | |
| 885 | // Pad out space for code signature | 901 | // Pad out space for code signature |
| 886 | try self.writeCodeSignaturePadding(); | 902 | try self.writeCodeSignaturePadding(); |
| ... | @@ -889,6 +905,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -889,6 +905,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 889 | try self.writeHeader(); | 905 | try self.writeHeader(); |
| 890 | // Generate adhoc code signature | 906 | // Generate adhoc code signature |
| 891 | try self.writeCodeSignature(); | 907 | try self.writeCodeSignature(); |
| | 908 | |
| | 909 | assert(!self.header_dirty); |
| | 910 | assert(!self.load_commands_dirty); |
| 892 | } | 911 | } |
| 893 | } | 912 | } |
| 894 | } | 913 | } |
| ... | @@ -1002,10 +1021,10 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | ... | @@ -1002,10 +1021,10 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1002 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | 1021 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 1003 | | 1022 | |
| 1004 | if (self.local_symbol_free_list.popOrNull()) |i| { | 1023 | if (self.local_symbol_free_list.popOrNull()) |i| { |
| 1005 | log.debug("reusing symbol index {} for {}\n", .{ i, decl.name }); | 1024 | log.debug("reusing symbol index {} for {}", .{ i, decl.name }); |
| 1006 | decl.link.macho.local_sym_index = i; | 1025 | decl.link.macho.local_sym_index = i; |
| 1007 | } else { | 1026 | } else { |
| 1008 | log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); | 1027 | log.debug("allocating symbol index {} for {}", .{ self.local_symbols.items.len, decl.name }); |
| 1009 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); | 1028 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 1010 | _ = self.local_symbols.addOneAssumeCapacity(); | 1029 | _ = self.local_symbols.addOneAssumeCapacity(); |
| 1011 | } | 1030 | } |
| ... | @@ -1015,6 +1034,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | ... | @@ -1015,6 +1034,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1015 | } else { | 1034 | } else { |
| 1016 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); | 1035 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 1017 | _ = self.offset_table.addOneAssumeCapacity(); | 1036 | _ = self.offset_table.addOneAssumeCapacity(); |
| | 1037 | self.offset_table_count_dirty = true; |
| 1018 | } | 1038 | } |
| 1019 | | 1039 | |
| 1020 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ | 1040 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ |
| ... | @@ -1056,10 +1076,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1056,10 +1076,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1056 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); | 1076 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); |
| 1057 | if (need_realloc) { | 1077 | if (need_realloc) { |
| 1058 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); | 1078 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); |
| 1059 | log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); | 1079 | log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1060 | if (vaddr != symbol.n_value) { | 1080 | if (vaddr != symbol.n_value) { |
| 1061 | symbol.n_value = vaddr; | 1081 | symbol.n_value = vaddr; |
| 1062 | log.debug(" (writing new offset table entry)\n", .{}); | 1082 | log.debug(" (writing new offset table entry)", .{}); |
| 1063 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; | 1083 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; |
| 1064 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 1084 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1065 | } | 1085 | } |
| ... | @@ -1071,11 +1091,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1071,11 +1091,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1071 | symbol.n_type = macho.N_SECT; | 1091 | symbol.n_type = macho.N_SECT; |
| 1072 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; | 1092 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 1073 | symbol.n_desc = 0; | 1093 | symbol.n_desc = 0; |
| | 1094 | |
| | 1095 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1074 | } else { | 1096 | } else { |
| 1075 | const decl_name = mem.spanZ(decl.name); | 1097 | const decl_name = mem.spanZ(decl.name); |
| 1076 | const name_str_index = try self.makeString(decl_name); | 1098 | const name_str_index = try self.makeString(decl_name); |
| 1077 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); | 1099 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); |
| 1078 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); | 1100 | log.debug("allocated text block for {} at 0x{x}", .{ decl_name, addr }); |
| 1079 | errdefer self.freeTextBlock(&decl.link.macho); | 1101 | errdefer self.freeTextBlock(&decl.link.macho); |
| 1080 | | 1102 | |
| 1081 | symbol.* = .{ | 1103 | symbol.* = .{ |
| ... | @@ -1086,6 +1108,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1086,6 +1108,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1086 | .n_value = addr, | 1108 | .n_value = addr, |
| 1087 | }; | 1109 | }; |
| 1088 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | 1110 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| | 1111 | |
| | 1112 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1089 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 1113 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1090 | } | 1114 | } |
| 1091 | | 1115 | |
| ... | @@ -1151,7 +1175,6 @@ pub fn updateDeclExports( | ... | @@ -1151,7 +1175,6 @@ pub fn updateDeclExports( |
| 1151 | .Strong => blk: { | 1175 | .Strong => blk: { |
| 1152 | if (mem.eql(u8, exp.options.name, "_start")) { | 1176 | if (mem.eql(u8, exp.options.name, "_start")) { |
| 1153 | self.entry_addr = decl_sym.n_value; | 1177 | self.entry_addr = decl_sym.n_value; |
| 1154 | self.cmd_table_dirty = true; // TODO This should be handled more granularly instead of invalidating all commands. | | |
| 1155 | } | 1178 | } |
| 1156 | break :blk macho.REFERENCE_FLAG_DEFINED; | 1179 | break :blk macho.REFERENCE_FLAG_DEFINED; |
| 1157 | }, | 1180 | }, |
| ... | @@ -1179,6 +1202,7 @@ pub fn updateDeclExports( | ... | @@ -1179,6 +1202,7 @@ pub fn updateDeclExports( |
| 1179 | const name_str_index = try self.makeString(exp.options.name); | 1202 | const name_str_index = try self.makeString(exp.options.name); |
| 1180 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { | 1203 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { |
| 1181 | _ = self.global_symbols.addOneAssumeCapacity(); | 1204 | _ = self.global_symbols.addOneAssumeCapacity(); |
| | 1205 | self.export_info_dirty = true; |
| 1182 | break :blk self.global_symbols.items.len - 1; | 1206 | break :blk self.global_symbols.items.len - 1; |
| 1183 | }; | 1207 | }; |
| 1184 | self.global_symbols.items[i] = .{ | 1208 | self.global_symbols.items[i] = .{ |
| ... | @@ -1271,6 +1295,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1271,6 +1295,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1271 | } | 1295 | } |
| 1272 | header.reserved = 0; | 1296 | header.reserved = 0; |
| 1273 | self.header = header; | 1297 | self.header = header; |
| | 1298 | self.header_dirty = true; |
| 1274 | } | 1299 | } |
| 1275 | if (self.pagezero_segment_cmd_index == null) { | 1300 | if (self.pagezero_segment_cmd_index == null) { |
| 1276 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1301 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1289,107 +1314,117 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1289,107 +1314,117 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1289 | .flags = 0, | 1314 | .flags = 0, |
| 1290 | }), | 1315 | }), |
| 1291 | }); | 1316 | }); |
| 1292 | self.cmd_table_dirty = true; | 1317 | self.header_dirty = true; |
| | 1318 | self.load_commands_dirty = true; |
| 1293 | } | 1319 | } |
| 1294 | if (self.text_segment_cmd_index == null) { | 1320 | if (self.text_segment_cmd_index == null) { |
| 1295 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1321 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1296 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | 1322 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1297 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; | 1323 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| | 1324 | |
| | 1325 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| | 1326 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| | 1327 | const ideal_size = self.header_pad + program_code_size_hint + offset_table_size_hint; |
| | 1328 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); |
| | 1329 | |
| | 1330 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| | 1331 | |
| 1298 | try self.load_commands.append(self.base.allocator, .{ | 1332 | try self.load_commands.append(self.base.allocator, .{ |
| 1299 | .Segment = SegmentCommand.empty(.{ | 1333 | .Segment = SegmentCommand.empty(.{ |
| 1300 | .cmd = macho.LC_SEGMENT_64, | 1334 | .cmd = macho.LC_SEGMENT_64, |
| 1301 | .cmdsize = @sizeOf(macho.segment_command_64), | 1335 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1302 | .segname = makeStaticString("__TEXT"), | 1336 | .segname = makeStaticString("__TEXT"), |
| 1303 | .vmaddr = 0x100000000, // always starts at 4GB | 1337 | .vmaddr = 0x100000000, // always starts at 4GB |
| 1304 | .vmsize = 0, | 1338 | .vmsize = needed_size, |
| 1305 | .fileoff = 0, | 1339 | .fileoff = 0, |
| 1306 | .filesize = 0, | 1340 | .filesize = needed_size, |
| 1307 | .maxprot = maxprot, | 1341 | .maxprot = maxprot, |
| 1308 | .initprot = initprot, | 1342 | .initprot = initprot, |
| 1309 | .nsects = 0, | 1343 | .nsects = 0, |
| 1310 | .flags = 0, | 1344 | .flags = 0, |
| 1311 | }), | 1345 | }), |
| 1312 | }); | 1346 | }); |
| 1313 | self.cmd_table_dirty = true; | 1347 | self.header_dirty = true; |
| | 1348 | self.load_commands_dirty = true; |
| 1314 | } | 1349 | } |
| 1315 | if (self.text_section_index == null) { | 1350 | if (self.text_section_index == null) { |
| 1316 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1351 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1317 | self.text_section_index = @intCast(u16, text_segment.sections.items.len); | 1352 | self.text_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1318 | | 1353 | |
| 1319 | const program_code_size_hint = self.base.options.program_code_size_hint; | 1354 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { |
| 1320 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); | 1355 | .x86_64 => 0, |
| 1321 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? | 1356 | .aarch64 => 2, |
| | 1357 | else => unreachable, // unhandled architecture type |
| | 1358 | }; |
| | 1359 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| | 1360 | const needed_size = self.base.options.program_code_size_hint; |
| | 1361 | const off = self.findFreeSpace(text_segment, needed_size, @as(u16, 1) << alignment); |
| 1322 | | 1362 | |
| 1323 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1363 | log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1324 | | 1364 | |
| 1325 | try text_segment.sections.append(self.base.allocator, .{ | 1365 | try text_segment.addSection(self.base.allocator, .{ |
| 1326 | .sectname = makeStaticString("__text"), | 1366 | .sectname = makeStaticString("__text"), |
| 1327 | .segname = makeStaticString("__TEXT"), | 1367 | .segname = makeStaticString("__TEXT"), |
| 1328 | .addr = text_segment.inner.vmaddr + off, | 1368 | .addr = text_segment.inner.vmaddr + off, |
| 1329 | .size = file_size, | 1369 | .size = @intCast(u32, needed_size), |
| 1330 | .offset = off, | 1370 | .offset = @intCast(u32, off), |
| 1331 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 | 1371 | .@"align" = alignment, |
| 1332 | .reloff = 0, | 1372 | .reloff = 0, |
| 1333 | .nreloc = 0, | 1373 | .nreloc = 0, |
| 1334 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | 1374 | .flags = flags, |
| 1335 | .reserved1 = 0, | 1375 | .reserved1 = 0, |
| 1336 | .reserved2 = 0, | 1376 | .reserved2 = 0, |
| 1337 | .reserved3 = 0, | 1377 | .reserved3 = 0, |
| 1338 | }); | 1378 | }); |
| 1339 | | 1379 | self.header_dirty = true; |
| 1340 | text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. | 1380 | self.load_commands_dirty = true; |
| 1341 | text_segment.inner.filesize = file_size + off; | | |
| 1342 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); | | |
| 1343 | text_segment.inner.nsects += 1; | | |
| 1344 | self.cmd_table_dirty = true; | | |
| 1345 | } | 1381 | } |
| 1346 | if (self.got_section_index == null) { | 1382 | if (self.got_section_index == null) { |
| 1347 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1383 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1348 | const text_section = &text_segment.sections.items[self.text_section_index.?]; | 1384 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1349 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); | 1385 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1350 | | 1386 | |
| 1351 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1387 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1352 | // TODO looking for free space should be done *within* a segment it belongs to | 1388 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1353 | const off = @intCast(u32, text_section.offset + text_section.size); | 1389 | const off = self.findFreeSpace(text_segment, needed_size, @alignOf(u64)); |
| | 1390 | assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. |
| 1354 | | 1391 | |
| 1355 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1392 | log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1356 | | 1393 | |
| 1357 | try text_segment.sections.append(self.base.allocator, .{ | 1394 | try text_segment.addSection(self.base.allocator, .{ |
| 1358 | .sectname = makeStaticString("__got"), | 1395 | .sectname = makeStaticString("__ziggot"), |
| 1359 | .segname = makeStaticString("__TEXT"), | 1396 | .segname = makeStaticString("__TEXT"), |
| 1360 | .addr = text_section.addr + text_section.size, | 1397 | .addr = text_segment.inner.vmaddr + off, |
| 1361 | .size = file_size, | 1398 | .size = needed_size, |
| 1362 | .offset = off, | 1399 | .offset = @intCast(u32, off), |
| 1363 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, | 1400 | .@"align" = @sizeOf(u64), |
| 1364 | .reloff = 0, | 1401 | .reloff = 0, |
| 1365 | .nreloc = 0, | 1402 | .nreloc = 0, |
| 1366 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | 1403 | .flags = flags, |
| 1367 | .reserved1 = 0, | 1404 | .reserved1 = 0, |
| 1368 | .reserved2 = 0, | 1405 | .reserved2 = 0, |
| 1369 | .reserved3 = 0, | 1406 | .reserved3 = 0, |
| 1370 | }); | 1407 | }); |
| 1371 | | 1408 | self.header_dirty = true; |
| 1372 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); | 1409 | self.load_commands_dirty = true; |
| 1373 | text_segment.inner.vmsize += added_size; | | |
| 1374 | text_segment.inner.filesize += added_size; | | |
| 1375 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); | | |
| 1376 | text_segment.inner.nsects += 1; | | |
| 1377 | self.cmd_table_dirty = true; | | |
| 1378 | } | 1410 | } |
| 1379 | if (self.linkedit_segment_cmd_index == null) { | 1411 | if (self.linkedit_segment_cmd_index == null) { |
| 1380 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1412 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1381 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1413 | |
| 1382 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | 1414 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1383 | const initprot = macho.VM_PROT_READ; | 1415 | const initprot = macho.VM_PROT_READ; |
| 1384 | const off = text_segment.inner.fileoff + text_segment.inner.filesize; | 1416 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| | 1417 | |
| | 1418 | log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset}); |
| | 1419 | |
| 1385 | try self.load_commands.append(self.base.allocator, .{ | 1420 | try self.load_commands.append(self.base.allocator, .{ |
| 1386 | .Segment = SegmentCommand.empty(.{ | 1421 | .Segment = SegmentCommand.empty(.{ |
| 1387 | .cmd = macho.LC_SEGMENT_64, | 1422 | .cmd = macho.LC_SEGMENT_64, |
| 1388 | .cmdsize = @sizeOf(macho.segment_command_64), | 1423 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1389 | .segname = makeStaticString("__LINKEDIT"), | 1424 | .segname = makeStaticString("__LINKEDIT"), |
| 1390 | .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize, | 1425 | .vmaddr = address_and_offset.address, |
| 1391 | .vmsize = 0, | 1426 | .vmsize = 0, |
| 1392 | .fileoff = off, | 1427 | .fileoff = address_and_offset.offset, |
| 1393 | .filesize = 0, | 1428 | .filesize = 0, |
| 1394 | .maxprot = maxprot, | 1429 | .maxprot = maxprot, |
| 1395 | .initprot = initprot, | 1430 | .initprot = initprot, |
| ... | @@ -1397,11 +1432,19 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1397,11 +1432,19 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1397 | .flags = 0, | 1432 | .flags = 0, |
| 1398 | }), | 1433 | }), |
| 1399 | }); | 1434 | }); |
| 1400 | self.linkedit_segment_next_offset = @intCast(u32, off); | 1435 | self.header_dirty = true; |
| 1401 | self.cmd_table_dirty = true; | 1436 | self.load_commands_dirty = true; |
| 1402 | } | 1437 | } |
| 1403 | if (self.dyld_info_cmd_index == null) { | 1438 | if (self.dyld_info_cmd_index == null) { |
| 1404 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | 1439 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 1440 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1441 | |
| | 1442 | // TODO Preallocate rebase, binding, and lazy binding info. |
| | 1443 | const export_size = 2; |
| | 1444 | const export_off = self.findFreeSpace(&linkedit_segment, export_size, 1); |
| | 1445 | |
| | 1446 | log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size }); |
| | 1447 | |
| 1405 | try self.load_commands.append(self.base.allocator, .{ | 1448 | try self.load_commands.append(self.base.allocator, .{ |
| 1406 | .DyldInfoOnly = .{ | 1449 | .DyldInfoOnly = .{ |
| 1407 | .cmd = macho.LC_DYLD_INFO_ONLY, | 1450 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| ... | @@ -1414,28 +1457,48 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1414,28 +1457,48 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1414 | .weak_bind_size = 0, | 1457 | .weak_bind_size = 0, |
| 1415 | .lazy_bind_off = 0, | 1458 | .lazy_bind_off = 0, |
| 1416 | .lazy_bind_size = 0, | 1459 | .lazy_bind_size = 0, |
| 1417 | .export_off = 0, | 1460 | .export_off = @intCast(u32, export_off), |
| 1418 | .export_size = 0, | 1461 | .export_size = export_size, |
| 1419 | }, | 1462 | }, |
| 1420 | }); | 1463 | }); |
| 1421 | self.cmd_table_dirty = true; | 1464 | self.header_dirty = true; |
| | 1465 | self.load_commands_dirty = true; |
| 1422 | } | 1466 | } |
| 1423 | if (self.symtab_cmd_index == null) { | 1467 | if (self.symtab_cmd_index == null) { |
| 1424 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 1468 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 1469 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1470 | |
| | 1471 | const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); |
| | 1472 | const symtab_off = self.findFreeSpace(&linkedit_segment, symtab_size, @sizeOf(macho.nlist_64)); |
| | 1473 | |
| | 1474 | log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); |
| | 1475 | |
| | 1476 | try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. |
| | 1477 | const strtab_size = self.string_table.items.len; |
| | 1478 | const strtab_off = self.findFreeSpace(&linkedit_segment, strtab_size, 1); |
| | 1479 | |
| | 1480 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); |
| | 1481 | |
| 1425 | try self.load_commands.append(self.base.allocator, .{ | 1482 | try self.load_commands.append(self.base.allocator, .{ |
| 1426 | .Symtab = .{ | 1483 | .Symtab = .{ |
| 1427 | .cmd = macho.LC_SYMTAB, | 1484 | .cmd = macho.LC_SYMTAB, |
| 1428 | .cmdsize = @sizeOf(macho.symtab_command), | 1485 | .cmdsize = @sizeOf(macho.symtab_command), |
| 1429 | .symoff = 0, | 1486 | .symoff = @intCast(u32, symtab_off), |
| 1430 | .nsyms = 0, | 1487 | .nsyms = @intCast(u32, self.base.options.symbol_count_hint), |
| 1431 | .stroff = 0, | 1488 | .stroff = @intCast(u32, strtab_off), |
| 1432 | .strsize = 0, | 1489 | .strsize = @intCast(u32, strtab_size), |
| 1433 | }, | 1490 | }, |
| 1434 | }); | 1491 | }); |
| 1435 | self.cmd_table_dirty = true; | 1492 | try self.writeLocalSymbol(0); |
| | 1493 | self.header_dirty = true; |
| | 1494 | self.load_commands_dirty = true; |
| | 1495 | self.string_table_dirty = true; |
| 1436 | } | 1496 | } |
| 1437 | if (self.dysymtab_cmd_index == null) { | 1497 | if (self.dysymtab_cmd_index == null) { |
| 1438 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 1498 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 1499 | |
| | 1500 | // TODO Preallocate space for indirect symbol table. |
| | 1501 | |
| 1439 | try self.load_commands.append(self.base.allocator, .{ | 1502 | try self.load_commands.append(self.base.allocator, .{ |
| 1440 | .Dysymtab = .{ | 1503 | .Dysymtab = .{ |
| 1441 | .cmd = macho.LC_DYSYMTAB, | 1504 | .cmd = macho.LC_DYSYMTAB, |
| ... | @@ -1460,7 +1523,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1460,7 +1523,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1460 | .nlocrel = 0, | 1523 | .nlocrel = 0, |
| 1461 | }, | 1524 | }, |
| 1462 | }); | 1525 | }); |
| 1463 | self.cmd_table_dirty = true; | 1526 | self.header_dirty = true; |
| | 1527 | self.load_commands_dirty = true; |
| 1464 | } | 1528 | } |
| 1465 | if (self.dylinker_cmd_index == null) { | 1529 | if (self.dylinker_cmd_index == null) { |
| 1466 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | 1530 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1474,7 +1538,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1474,7 +1538,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1474 | mem.set(u8, dylinker_cmd.data, 0); | 1538 | mem.set(u8, dylinker_cmd.data, 0); |
| 1475 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); | 1539 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); |
| 1476 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); | 1540 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); |
| 1477 | self.cmd_table_dirty = true; | 1541 | self.header_dirty = true; |
| | 1542 | self.load_commands_dirty = true; |
| 1478 | } | 1543 | } |
| 1479 | if (self.libsystem_cmd_index == null) { | 1544 | if (self.libsystem_cmd_index == null) { |
| 1480 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 1545 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1496,7 +1561,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1496,7 +1561,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1496 | mem.set(u8, dylib_cmd.data, 0); | 1561 | mem.set(u8, dylib_cmd.data, 0); |
| 1497 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | 1562 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 1498 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | 1563 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 1499 | self.cmd_table_dirty = true; | 1564 | self.header_dirty = true; |
| | 1565 | self.load_commands_dirty = true; |
| 1500 | } | 1566 | } |
| 1501 | if (self.main_cmd_index == null) { | 1567 | if (self.main_cmd_index == null) { |
| 1502 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | 1568 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1508,7 +1574,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1508,7 +1574,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1508 | .stacksize = 0, | 1574 | .stacksize = 0, |
| 1509 | }, | 1575 | }, |
| 1510 | }); | 1576 | }); |
| 1511 | self.cmd_table_dirty = true; | 1577 | self.header_dirty = true; |
| | 1578 | self.load_commands_dirty = true; |
| 1512 | } | 1579 | } |
| 1513 | if (self.version_min_cmd_index == null) { | 1580 | if (self.version_min_cmd_index == null) { |
| 1514 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); | 1581 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1529,6 +1596,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1529,6 +1596,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1529 | .sdk = version, | 1596 | .sdk = version, |
| 1530 | }, | 1597 | }, |
| 1531 | }); | 1598 | }); |
| | 1599 | self.header_dirty = true; |
| | 1600 | self.load_commands_dirty = true; |
| 1532 | } | 1601 | } |
| 1533 | if (self.source_version_cmd_index == null) { | 1602 | if (self.source_version_cmd_index == null) { |
| 1534 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); | 1603 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1539,9 +1608,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1539,9 +1608,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1539 | .version = 0x0, | 1608 | .version = 0x0, |
| 1540 | }, | 1609 | }, |
| 1541 | }); | 1610 | }); |
| | 1611 | self.header_dirty = true; |
| | 1612 | self.load_commands_dirty = true; |
| 1542 | } | 1613 | } |
| 1543 | if (self.code_signature_cmd_index == null) { | 1614 | if (self.code_signature_cmd_index == null) { |
| 1544 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | 1615 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 1616 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1545 | try self.load_commands.append(self.base.allocator, .{ | 1617 | try self.load_commands.append(self.base.allocator, .{ |
| 1546 | .LinkeditData = .{ | 1618 | .LinkeditData = .{ |
| 1547 | .cmd = macho.LC_CODE_SIGNATURE, | 1619 | .cmd = macho.LC_CODE_SIGNATURE, |
| ... | @@ -1550,6 +1622,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1550,6 +1622,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1550 | .datasize = 0, | 1622 | .datasize = 0, |
| 1551 | }, | 1623 | }, |
| 1552 | }); | 1624 | }); |
| | 1625 | self.header_dirty = true; |
| | 1626 | self.load_commands_dirty = true; |
| 1553 | } | 1627 | } |
| 1554 | if (self.dyld_stub_binder_index == null) { | 1628 | if (self.dyld_stub_binder_index == null) { |
| 1555 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); | 1629 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); |
| ... | @@ -1631,14 +1705,13 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -1631,14 +1705,13 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1631 | | 1705 | |
| 1632 | const expand_text_section = block_placement == null or block_placement.?.next == null; | 1706 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| 1633 | if (expand_text_section) { | 1707 | if (expand_text_section) { |
| 1634 | const text_capacity = self.allocatedSize(text_section.offset); | | |
| 1635 | const needed_size = (vaddr + new_block_size) - text_section.addr; | 1708 | const needed_size = (vaddr + new_block_size) - text_section.addr; |
| 1636 | assert(needed_size <= text_capacity); // TODO must move the entire text section. | 1709 | assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section. |
| 1637 | | 1710 | |
| 1638 | self.last_text_block = text_block; | 1711 | self.last_text_block = text_block; |
| 1639 | text_section.size = needed_size; | 1712 | text_section.size = needed_size; |
| 1640 | | 1713 | |
| 1641 | self.cmd_table_dirty = true; // TODO Make more granular. | 1714 | self.load_commands_dirty = true; // TODO Make more granular. |
| 1642 | } | 1715 | } |
| 1643 | text_block.size = new_block_size; | 1716 | text_block.size = new_block_size; |
| 1644 | | 1717 | |
| ... | @@ -1667,7 +1740,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -1667,7 +1740,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1667 | pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { | 1740 | pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 1668 | var buf = [_]u8{0} ** 16; | 1741 | var buf = [_]u8{0} ** 16; |
| 1669 | if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); | 1742 | if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); |
| 1670 | mem.copy(u8, buf[0..], bytes); | 1743 | mem.copy(u8, &buf, bytes); |
| 1671 | return buf; | 1744 | return buf; |
| 1672 | } | 1745 | } |
| 1673 | | 1746 | |
| ... | @@ -1676,6 +1749,7 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { | ... | @@ -1676,6 +1749,7 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 1676 | const result = self.string_table.items.len; | 1749 | const result = self.string_table.items.len; |
| 1677 | self.string_table.appendSliceAssumeCapacity(bytes); | 1750 | self.string_table.appendSliceAssumeCapacity(bytes); |
| 1678 | self.string_table.appendAssumeCapacity(0); | 1751 | self.string_table.appendAssumeCapacity(0); |
| | 1752 | self.string_table_dirty = true; |
| 1679 | return @intCast(u32, result); | 1753 | return @intCast(u32, result); |
| 1680 | } | 1754 | } |
| 1681 | | 1755 | |
| ... | @@ -1692,103 +1766,196 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { | ... | @@ -1692,103 +1766,196 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { |
| 1692 | return self.makeString(new_name); | 1766 | return self.makeString(new_name); |
| 1693 | } | 1767 | } |
| 1694 | | 1768 | |
| 1695 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | 1769 | const NextSegmentAddressAndOffset = struct { |
| 1696 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); | 1770 | address: u64, |
| 1697 | if (start < hdr_size) return hdr_size; | 1771 | offset: u64, |
| 1698 | const end = start + satMul(size, alloc_num) / alloc_den; | 1772 | }; |
| 1699 | { | 1773 | |
| 1700 | const off = @sizeOf(macho.mach_header_64); | 1774 | fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { |
| 1701 | var tight_size: u64 = 0; | 1775 | const prev_segment_idx = blk: { |
| 1702 | for (self.load_commands.items) |cmd| { | 1776 | if (self.data_segment_cmd_index) |idx| { |
| 1703 | tight_size += cmd.cmdsize(); | 1777 | break :blk idx; |
| | 1778 | } else if (self.text_segment_cmd_index) |idx| { |
| | 1779 | break :blk idx; |
| | 1780 | } else { |
| | 1781 | unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT |
| | 1782 | } |
| | 1783 | }; |
| | 1784 | const prev_segment = self.load_commands.items[prev_segment_idx].Segment; |
| | 1785 | const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize; |
| | 1786 | const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize; |
| | 1787 | return .{ |
| | 1788 | .address = address, |
| | 1789 | .offset = offset, |
| | 1790 | }; |
| | 1791 | } |
| | 1792 | |
| | 1793 | fn allocatedSize(self: *MachO, segment: *const SegmentCommand, start: u64) u64 { |
| | 1794 | assert(start > 0); |
| | 1795 | var min_pos: u64 = std.math.maxInt(u64); |
| | 1796 | |
| | 1797 | if (parseAndCmpName(&segment.inner.segname, "__LINKEDIT")) { |
| | 1798 | assert(segment.sections.items.len == 0); |
| | 1799 | // __LINKEDIT is a weird segment where sections get their own load commands so we |
| | 1800 | // special-case it. |
| | 1801 | if (self.dyld_info_cmd_index) |idx| { |
| | 1802 | const dyld_info = self.load_commands.items[idx].DyldInfoOnly; |
| | 1803 | if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off; |
| | 1804 | if (dyld_info.bind_off > start and dyld_info.bind_off < min_pos) min_pos = dyld_info.bind_off; |
| | 1805 | if (dyld_info.weak_bind_off > start and dyld_info.weak_bind_off < min_pos) min_pos = dyld_info.weak_bind_off; |
| | 1806 | if (dyld_info.lazy_bind_off > start and dyld_info.lazy_bind_off < min_pos) min_pos = dyld_info.lazy_bind_off; |
| | 1807 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; |
| 1704 | } | 1808 | } |
| 1705 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; | 1809 | |
| 1706 | const test_end = off + increased_size; | 1810 | if (self.function_starts_cmd_index) |idx| { |
| 1707 | if (end > off and start < test_end) { | 1811 | const fstart = self.load_commands.items[idx].LinkeditData; |
| 1708 | return test_end; | 1812 | if (fstart.dataoff > start and fstart.dataoff < min_pos) min_pos = fstart.dataoff; |
| | 1813 | } |
| | 1814 | |
| | 1815 | if (self.data_in_code_cmd_index) |idx| { |
| | 1816 | const dic = self.load_commands.items[idx].LinkeditData; |
| | 1817 | if (dic.dataoff > start and dic.dataoff < min_pos) min_pos = dic.dataoff; |
| 1709 | } | 1818 | } |
| | 1819 | |
| | 1820 | if (self.dysymtab_cmd_index) |idx| { |
| | 1821 | const dysymtab = self.load_commands.items[idx].Dysymtab; |
| | 1822 | if (dysymtab.indirectsymoff > start and dysymtab.indirectsymoff < min_pos) min_pos = dysymtab.indirectsymoff; |
| | 1823 | // TODO Handle more dynamic symbol table sections. |
| | 1824 | } |
| | 1825 | |
| | 1826 | if (self.symtab_cmd_index) |idx| { |
| | 1827 | const symtab = self.load_commands.items[idx].Symtab; |
| | 1828 | if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; |
| | 1829 | if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff; |
| | 1830 | } |
| | 1831 | } else { |
| | 1832 | for (segment.sections.items) |section| { |
| | 1833 | if (section.offset > start and section.offset < min_pos) min_pos = section.offset; |
| | 1834 | } |
| | 1835 | } |
| | 1836 | |
| | 1837 | return min_pos - start; |
| | 1838 | } |
| | 1839 | |
| | 1840 | inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { |
| | 1841 | const increased_size = satMul(size, alloc_num) / alloc_den; |
| | 1842 | const test_end = off + increased_size; |
| | 1843 | if (end > off and start < test_end) { |
| | 1844 | return test_end; |
| 1710 | } | 1845 | } |
| 1711 | if (self.text_segment_cmd_index) |text_index| { | 1846 | return null; |
| 1712 | const text_segment = self.load_commands.items[text_index].Segment; | 1847 | } |
| 1713 | for (text_segment.sections.items) |section| { | 1848 | |
| 1714 | const increased_size = satMul(section.size, alloc_num) / alloc_den; | 1849 | fn detectAllocCollision(self: *MachO, segment: *const SegmentCommand, start: u64, size: u64) ?u64 { |
| 1715 | const test_end = section.offset + increased_size; | 1850 | const end = start + satMul(size, alloc_num) / alloc_den; |
| 1716 | if (end > section.offset and start < test_end) { | 1851 | |
| 1717 | return test_end; | 1852 | if (parseAndCmpName(&segment.inner.segname, "__LINKEDIT")) { |
| | 1853 | assert(segment.sections.items.len == 0); |
| | 1854 | // __LINKEDIT is a weird segment where sections get their own load commands so we |
| | 1855 | // special-case it. |
| | 1856 | if (self.dyld_info_cmd_index) |idx| outer: { |
| | 1857 | if (self.load_commands.items.len == idx) break :outer; |
| | 1858 | const dyld_info = self.load_commands.items[idx].DyldInfoOnly; |
| | 1859 | if (checkForCollision(start, end, dyld_info.rebase_off, dyld_info.rebase_size)) |pos| { |
| | 1860 | return pos; |
| | 1861 | } |
| | 1862 | // Binding info |
| | 1863 | if (checkForCollision(start, end, dyld_info.bind_off, dyld_info.bind_size)) |pos| { |
| | 1864 | return pos; |
| | 1865 | } |
| | 1866 | // Weak binding info |
| | 1867 | if (checkForCollision(start, end, dyld_info.weak_bind_off, dyld_info.weak_bind_size)) |pos| { |
| | 1868 | return pos; |
| | 1869 | } |
| | 1870 | // Lazy binding info |
| | 1871 | if (checkForCollision(start, end, dyld_info.lazy_bind_off, dyld_info.lazy_bind_size)) |pos| { |
| | 1872 | return pos; |
| | 1873 | } |
| | 1874 | // Export info |
| | 1875 | if (checkForCollision(start, end, dyld_info.export_off, dyld_info.export_size)) |pos| { |
| | 1876 | return pos; |
| 1718 | } | 1877 | } |
| 1719 | } | 1878 | } |
| 1720 | } | 1879 | |
| 1721 | if (self.dyld_info_cmd_index) |dyld_info_index| { | 1880 | if (self.function_starts_cmd_index) |idx| outer: { |
| 1722 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; | 1881 | if (self.load_commands.items.len == idx) break :outer; |
| 1723 | const tight_size = dyld_info.export_size; | 1882 | const fstart = self.load_commands.items[idx].LinkeditData; |
| 1724 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; | 1883 | if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| { |
| 1725 | const test_end = dyld_info.export_off + increased_size; | 1884 | return pos; |
| 1726 | if (end > dyld_info.export_off and start < test_end) { | 1885 | } |
| 1727 | return test_end; | | |
| 1728 | } | 1886 | } |
| 1729 | } | 1887 | |
| 1730 | if (self.symtab_cmd_index) |symtab_index| { | 1888 | if (self.data_in_code_cmd_index) |idx| outer: { |
| 1731 | const symtab = self.load_commands.items[symtab_index].Symtab; | 1889 | if (self.load_commands.items.len == idx) break :outer; |
| 1732 | { | 1890 | const dic = self.load_commands.items[idx].LinkeditData; |
| 1733 | const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms; | 1891 | if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| { |
| 1734 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; | 1892 | return pos; |
| 1735 | const test_end = symtab.symoff + increased_size; | | |
| 1736 | if (end > symtab.symoff and start < test_end) { | | |
| 1737 | return test_end; | | |
| 1738 | } | 1893 | } |
| 1739 | } | 1894 | } |
| 1740 | { | 1895 | |
| 1741 | const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den; | 1896 | if (self.dysymtab_cmd_index) |idx| outer: { |
| 1742 | const test_end = symtab.stroff + increased_size; | 1897 | if (self.load_commands.items.len == idx) break :outer; |
| 1743 | if (end > symtab.stroff and start < test_end) { | 1898 | const dysymtab = self.load_commands.items[idx].Dysymtab; |
| 1744 | return test_end; | 1899 | // Indirect symbol table |
| | 1900 | const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32); |
| | 1901 | if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| { |
| | 1902 | return pos; |
| 1745 | } | 1903 | } |
| | 1904 | // TODO Handle more dynamic symbol table sections. |
| 1746 | } | 1905 | } |
| 1747 | } | | |
| 1748 | return null; | | |
| 1749 | } | | |
| 1750 | | 1906 | |
| 1751 | fn allocatedSize(self: *MachO, start: u64) u64 { | 1907 | if (self.symtab_cmd_index) |idx| outer: { |
| 1752 | if (start == 0) | 1908 | if (self.load_commands.items.len == idx) break :outer; |
| 1753 | return 0; | 1909 | const symtab = self.load_commands.items[idx].Symtab; |
| 1754 | var min_pos: u64 = std.math.maxInt(u64); | 1910 | // Symbol table |
| 1755 | { | 1911 | const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1756 | const off = @sizeOf(macho.mach_header_64); | 1912 | if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| { |
| 1757 | if (off > start and off < min_pos) min_pos = off; | 1913 | return pos; |
| 1758 | } | 1914 | } |
| 1759 | if (self.text_segment_cmd_index) |text_index| { | 1915 | // String table |
| 1760 | const text_segment = self.load_commands.items[text_index].Segment; | 1916 | if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| { |
| 1761 | for (text_segment.sections.items) |section| { | 1917 | return pos; |
| 1762 | if (section.offset <= start) continue; | 1918 | } |
| 1763 | if (section.offset < min_pos) min_pos = section.offset; | 1919 | } |
| | 1920 | } else { |
| | 1921 | for (segment.sections.items) |section| { |
| | 1922 | if (checkForCollision(start, end, section.offset, section.size)) |pos| { |
| | 1923 | return pos; |
| | 1924 | } |
| 1764 | } | 1925 | } |
| 1765 | } | 1926 | } |
| 1766 | if (self.dyld_info_cmd_index) |dyld_info_index| { | 1927 | |
| 1767 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; | 1928 | return null; |
| 1768 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; | | |
| 1769 | } | | |
| 1770 | if (self.symtab_cmd_index) |symtab_index| { | | |
| 1771 | const symtab = self.load_commands.items[symtab_index].Symtab; | | |
| 1772 | if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; | | |
| 1773 | if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff; | | |
| 1774 | } | | |
| 1775 | return min_pos - start; | | |
| 1776 | } | 1929 | } |
| 1777 | | 1930 | |
| 1778 | fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { | 1931 | fn findFreeSpace(self: *MachO, segment: *const SegmentCommand, object_size: u64, min_alignment: u16) u64 { |
| 1779 | var start: u64 = 0; | 1932 | var start: u64 = if (parseAndCmpName(&segment.inner.segname, "__TEXT")) |
| 1780 | while (self.detectAllocCollision(start, object_size)) |item_end| { | 1933 | self.header_pad |
| | 1934 | else |
| | 1935 | segment.inner.fileoff; |
| | 1936 | while (self.detectAllocCollision(segment, start, object_size)) |item_end| { |
| 1781 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); | 1937 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| 1782 | } | 1938 | } |
| 1783 | return start; | 1939 | return start; |
| 1784 | } | 1940 | } |
| 1785 | | 1941 | |
| | 1942 | /// Saturating multiplication |
| | 1943 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| | 1944 | const T = @TypeOf(a, b); |
| | 1945 | return std.math.mul(T, a, b) catch std.math.maxInt(T); |
| | 1946 | } |
| | 1947 | |
| 1786 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | 1948 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1787 | const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1949 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1788 | const sect = &text_semgent.sections.items[self.got_section_index.?]; | 1950 | const sect = &text_segment.sections.items[self.got_section_index.?]; |
| 1789 | const off = sect.offset + @sizeOf(u64) * index; | 1951 | const off = sect.offset + @sizeOf(u64) * index; |
| 1790 | const vmaddr = sect.addr + @sizeOf(u64) * index; | 1952 | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| 1791 | | 1953 | |
| | 1954 | if (self.offset_table_count_dirty) { |
| | 1955 | // TODO relocate. |
| | 1956 | self.offset_table_count_dirty = false; |
| | 1957 | } |
| | 1958 | |
| 1792 | var code: [8]u8 = undefined; | 1959 | var code: [8]u8 = undefined; |
| 1793 | switch (self.base.options.target.cpu.arch) { | 1960 | switch (self.base.options.target.cpu.arch) { |
| 1794 | .x86_64 => { | 1961 | .x86_64 => { |
| ... | @@ -1812,75 +1979,114 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -1812,75 +1979,114 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1812 | }, | 1979 | }, |
| 1813 | else => unreachable, // unsupported target architecture | 1980 | else => unreachable, // unsupported target architecture |
| 1814 | } | 1981 | } |
| 1815 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); | 1982 | log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off }); |
| 1816 | try self.base.file.?.pwriteAll(&code, off); | 1983 | try self.base.file.?.pwriteAll(&code, off); |
| 1817 | } | 1984 | } |
| 1818 | | 1985 | |
| 1819 | fn writeSymbolTable(self: *MachO) !void { | 1986 | fn relocateSymbolTable(self: *MachO) !void { |
| 1820 | // TODO workout how we can cache these so that we only overwrite symbols that were updated | | |
| 1821 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1987 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 1988 | const nlocals = self.local_symbols.items.len; |
| | 1989 | const nglobals = self.global_symbols.items.len; |
| | 1990 | const nundefs = self.undef_symbols.items.len; |
| | 1991 | const nsyms = nlocals + nglobals + nundefs; |
| | 1992 | |
| | 1993 | if (symtab.nsyms < nsyms) { |
| | 1994 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1995 | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| | 1996 | if (needed_size > self.allocatedSize(&linkedit_segment, symtab.symoff)) { |
| | 1997 | // Move the entire symbol table to a new location |
| | 1998 | const new_symoff = self.findFreeSpace(&linkedit_segment, needed_size, @alignOf(macho.nlist_64)); |
| | 1999 | const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 2000 | |
| | 2001 | log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ |
| | 2002 | symtab.symoff, |
| | 2003 | symtab.symoff + existing_size, |
| | 2004 | new_symoff, |
| | 2005 | new_symoff + existing_size, |
| | 2006 | }); |
| 1822 | | 2007 | |
| 1823 | const locals_off = self.linkedit_segment_next_offset.?; | 2008 | const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size); |
| 1824 | const locals_size = self.local_symbols.items.len * @sizeOf(macho.nlist_64); | 2009 | if (amt != existing_size) return error.InputOutput; |
| 1825 | log.debug("writing local symbols from 0x{x} to 0x{x}\n", .{ locals_off, locals_size + locals_off }); | 2010 | symtab.symoff = @intCast(u32, new_symoff); |
| 1826 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.local_symbols.items), locals_off); | 2011 | } |
| | 2012 | symtab.nsyms = @intCast(u32, nsyms); |
| | 2013 | self.load_commands_dirty = true; |
| | 2014 | } |
| | 2015 | } |
| | 2016 | |
| | 2017 | fn writeLocalSymbol(self: *MachO, index: usize) !void { |
| | 2018 | const tracy = trace(@src()); |
| | 2019 | defer tracy.end(); |
| | 2020 | try self.relocateSymbolTable(); |
| | 2021 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 2022 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| | 2023 | log.debug("writing local symbol {} at 0x{x}", .{ index, off }); |
| | 2024 | try self.base.file.?.pwriteAll(mem.asBytes(&self.local_symbols.items[index]), off); |
| | 2025 | } |
| | 2026 | |
| | 2027 | fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| | 2028 | const tracy = trace(@src()); |
| | 2029 | defer tracy.end(); |
| | 2030 | |
| | 2031 | try self.relocateSymbolTable(); |
| | 2032 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 2033 | const nlocals = self.local_symbols.items.len; |
| | 2034 | const nglobals = self.global_symbols.items.len; |
| | 2035 | const nundefs = self.undef_symbols.items.len; |
| | 2036 | |
| | 2037 | const locals_off = symtab.symoff; |
| | 2038 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| 1827 | | 2039 | |
| 1828 | const globals_off = locals_off + locals_size; | 2040 | const globals_off = locals_off + locals_size; |
| 1829 | const globals_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); | 2041 | const globals_size = nglobals * @sizeOf(macho.nlist_64); |
| 1830 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ globals_off, globals_size + globals_off }); | 2042 | log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off }); |
| 1831 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); | 2043 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); |
| 1832 | | 2044 | |
| 1833 | const undefs_off = globals_off + globals_size; | 2045 | const undefs_off = globals_off + globals_size; |
| 1834 | const undefs_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); | 2046 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| 1835 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ undefs_off, undefs_size + undefs_off }); | 2047 | log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); |
| 1836 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); | 2048 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); |
| 1837 | | 2049 | |
| 1838 | // Update symbol table. | | |
| 1839 | const nlocals = @intCast(u32, self.local_symbols.items.len); | | |
| 1840 | const nglobals = @intCast(u32, self.global_symbols.items.len); | | |
| 1841 | const nundefs = @intCast(u32, self.undef_symbols.items.len); | | |
| 1842 | symtab.symoff = self.linkedit_segment_next_offset.?; | | |
| 1843 | symtab.nsyms = nlocals + nglobals + nundefs; | | |
| 1844 | self.linkedit_segment_next_offset = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | | |
| 1845 | | | |
| 1846 | // Update dynamic symbol table. | 2050 | // Update dynamic symbol table. |
| 1847 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | 2051 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 1848 | dysymtab.nlocalsym = nlocals; | 2052 | dysymtab.nlocalsym = @intCast(u32, nlocals); |
| 1849 | dysymtab.iextdefsym = nlocals; | 2053 | dysymtab.iextdefsym = @intCast(u32, nlocals); |
| 1850 | dysymtab.nextdefsym = nglobals; | 2054 | dysymtab.nextdefsym = @intCast(u32, nglobals); |
| 1851 | dysymtab.iundefsym = nlocals + nglobals; | 2055 | dysymtab.iundefsym = @intCast(u32, nlocals + nglobals); |
| 1852 | dysymtab.nundefsym = nundefs; | 2056 | dysymtab.nundefsym = @intCast(u32, nundefs); |
| 1853 | | 2057 | self.load_commands_dirty = true; |
| 1854 | // Advance size of __LINKEDIT segment | | |
| 1855 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | | |
| 1856 | linkedit.inner.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); | | |
| 1857 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | | |
| 1858 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | | |
| 1859 | } | | |
| 1860 | self.cmd_table_dirty = true; | | |
| 1861 | } | 2058 | } |
| 1862 | | 2059 | |
| 1863 | fn writeCodeSignaturePadding(self: *MachO) !void { | 2060 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| | 2061 | const tracy = trace(@src()); |
| | 2062 | defer tracy.end(); |
| | 2063 | |
| | 2064 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1864 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | 2065 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1865 | const fileoff = self.linkedit_segment_next_offset.?; | 2066 | const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize; |
| 1866 | const datasize = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff); | 2067 | const needed_size = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff); |
| 1867 | code_sig_cmd.dataoff = fileoff; | 2068 | |
| 1868 | code_sig_cmd.datasize = datasize; | 2069 | if (code_sig_cmd.datasize < needed_size) { |
| 1869 | | 2070 | code_sig_cmd.dataoff = @intCast(u32, fileoff); |
| 1870 | self.linkedit_segment_next_offset = fileoff + datasize; | 2071 | code_sig_cmd.datasize = needed_size; |
| 1871 | // Advance size of __LINKEDIT segment | 2072 | |
| 1872 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2073 | // Advance size of __LINKEDIT segment |
| 1873 | linkedit.inner.filesize += datasize; | 2074 | linkedit_segment.inner.filesize += needed_size; |
| 1874 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | 2075 | if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { |
| 1875 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | 2076 | linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); |
| 1876 | } | 2077 | } |
| 1877 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); | 2078 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); |
| 1878 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | 2079 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 1879 | // except for code signature data. | 2080 | // except for code signature data. |
| 1880 | try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + datasize - 1); | 2081 | try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); |
| | 2082 | self.load_commands_dirty = true; |
| | 2083 | } |
| 1881 | } | 2084 | } |
| 1882 | | 2085 | |
| 1883 | fn writeCodeSignature(self: *MachO) !void { | 2086 | fn writeCodeSignature(self: *MachO) !void { |
| | 2087 | const tracy = trace(@src()); |
| | 2088 | defer tracy.end(); |
| | 2089 | |
| 1884 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 2090 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1885 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | 2091 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1886 | | 2092 | |
| ... | @@ -1898,14 +2104,18 @@ fn writeCodeSignature(self: *MachO) !void { | ... | @@ -1898,14 +2104,18 @@ fn writeCodeSignature(self: *MachO) !void { |
| 1898 | defer self.base.allocator.free(buffer); | 2104 | defer self.base.allocator.free(buffer); |
| 1899 | code_sig.write(buffer); | 2105 | code_sig.write(buffer); |
| 1900 | | 2106 | |
| 1901 | log.debug("writing code signature from 0x{x} to 0x{x}\n", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); | 2107 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); |
| 1902 | | 2108 | |
| 1903 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); | 2109 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); |
| 1904 | } | 2110 | } |
| 1905 | | 2111 | |
| 1906 | fn writeExportTrie(self: *MachO) !void { | 2112 | fn writeExportTrie(self: *MachO) !void { |
| | 2113 | if (!self.export_info_dirty) return; |
| 1907 | if (self.global_symbols.items.len == 0) return; | 2114 | if (self.global_symbols.items.len == 0) return; |
| 1908 | | 2115 | |
| | 2116 | const tracy = trace(@src()); |
| | 2117 | defer tracy.end(); |
| | 2118 | |
| 1909 | var trie = Trie.init(self.base.allocator); | 2119 | var trie = Trie.init(self.base.allocator); |
| 1910 | defer trie.deinit(); | 2120 | defer trie.deinit(); |
| 1911 | | 2121 | |
| ... | @@ -1928,118 +2138,156 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -1928,118 +2138,156 @@ fn writeExportTrie(self: *MachO) !void { |
| 1928 | const nwritten = try trie.write(stream.writer()); | 2138 | const nwritten = try trie.write(stream.writer()); |
| 1929 | assert(nwritten == trie.size); | 2139 | assert(nwritten == trie.size); |
| 1930 | | 2140 | |
| | 2141 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1931 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 2142 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1932 | const export_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64))); | 2143 | const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.export_off); |
| 1933 | dyld_info.export_off = self.linkedit_segment_next_offset.?; | 2144 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| 1934 | dyld_info.export_size = export_size; | | |
| 1935 | | 2145 | |
| 1936 | log.debug("writing export trie from 0x{x} to 0x{x}\n", .{ dyld_info.export_off, dyld_info.export_off + export_size }); | 2146 | if (needed_size > allocated_size) { |
| 1937 | | 2147 | dyld_info.export_off = 0; |
| 1938 | if (export_size > buffer.len) { | 2148 | dyld_info.export_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1)); |
| 1939 | // Pad out to align(8). | | |
| 1940 | try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.export_off + export_size); | | |
| 1941 | } | 2149 | } |
| 1942 | try self.base.file.?.pwriteAll(buffer, dyld_info.export_off); | 2150 | dyld_info.export_size = @intCast(u32, needed_size); |
| | 2151 | log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); |
| 1943 | | 2152 | |
| 1944 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; | 2153 | try self.base.file.?.pwriteAll(buffer, dyld_info.export_off); |
| 1945 | // Advance size of __LINKEDIT segment | 2154 | self.load_commands_dirty = true; |
| 1946 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2155 | self.export_info_dirty = false; |
| 1947 | linkedit.inner.filesize += dyld_info.export_size; | | |
| 1948 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | | |
| 1949 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | | |
| 1950 | } | | |
| 1951 | self.cmd_table_dirty = true; | | |
| 1952 | } | 2156 | } |
| 1953 | | 2157 | |
| 1954 | fn writeBindingInfoTable(self: *MachO) !void { | 2158 | fn writeBindingInfoTable(self: *MachO) !void { |
| 1955 | const size = self.binding_info_table.calcSize(); | 2159 | if (!self.binding_info_dirty) return; |
| | 2160 | |
| | 2161 | const tracy = trace(@src()); |
| | 2162 | defer tracy.end(); |
| | 2163 | |
| | 2164 | const size = try self.binding_info_table.calcSize(); |
| 1956 | var buffer = try self.base.allocator.alloc(u8, size); | 2165 | var buffer = try self.base.allocator.alloc(u8, size); |
| 1957 | defer self.base.allocator.free(buffer); | 2166 | defer self.base.allocator.free(buffer); |
| 1958 | | 2167 | |
| 1959 | var stream = std.io.fixedBufferStream(buffer); | 2168 | var stream = std.io.fixedBufferStream(buffer); |
| 1960 | try self.binding_info_table.write(stream.writer()); | 2169 | try self.binding_info_table.write(stream.writer()); |
| 1961 | | 2170 | |
| | 2171 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1962 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 2172 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1963 | const bind_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64))); | 2173 | const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.bind_off); |
| 1964 | dyld_info.bind_off = self.linkedit_segment_next_offset.?; | 2174 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| 1965 | dyld_info.bind_size = bind_size; | | |
| 1966 | | 2175 | |
| 1967 | log.debug("writing binding info table from 0x{x} to 0x{x}\n", .{ dyld_info.bind_off, dyld_info.bind_off + bind_size }); | 2176 | if (needed_size > allocated_size) { |
| 1968 | | 2177 | dyld_info.bind_off = 0; |
| 1969 | if (bind_size > buffer.len) { | 2178 | dyld_info.bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1)); |
| 1970 | // Pad out to align(8). | | |
| 1971 | try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.bind_off + bind_size); | | |
| 1972 | } | 2179 | } |
| 1973 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); | | |
| 1974 | | 2180 | |
| 1975 | self.linkedit_segment_next_offset = dyld_info.bind_off + dyld_info.bind_size; | 2181 | dyld_info.bind_size = @intCast(u32, needed_size); |
| 1976 | // Advance size of __LINKEDIT segment | 2182 | log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); |
| 1977 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2183 | |
| 1978 | linkedit.inner.filesize += dyld_info.bind_size; | 2184 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| 1979 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | 2185 | self.load_commands_dirty = true; |
| 1980 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | 2186 | self.binding_info_dirty = false; |
| 1981 | } | | |
| 1982 | self.cmd_table_dirty = true; | | |
| 1983 | } | 2187 | } |
| 1984 | | 2188 | |
| 1985 | fn writeLazyBindingInfoTable(self: *MachO) !void { | 2189 | fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 1986 | const size = self.lazy_binding_info_table.calcSize(); | 2190 | if (!self.lazy_binding_info_dirty) return; |
| | 2191 | |
| | 2192 | const size = try self.lazy_binding_info_table.calcSize(); |
| 1987 | var buffer = try self.base.allocator.alloc(u8, size); | 2193 | var buffer = try self.base.allocator.alloc(u8, size); |
| 1988 | defer self.base.allocator.free(buffer); | 2194 | defer self.base.allocator.free(buffer); |
| 1989 | | 2195 | |
| 1990 | var stream = std.io.fixedBufferStream(buffer); | 2196 | var stream = std.io.fixedBufferStream(buffer); |
| 1991 | try self.lazy_binding_info_table.write(stream.writer()); | 2197 | try self.lazy_binding_info_table.write(stream.writer()); |
| 1992 | | 2198 | |
| | 2199 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1993 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 2200 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1994 | const bind_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64))); | 2201 | const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.lazy_bind_off); |
| 1995 | dyld_info.lazy_bind_off = self.linkedit_segment_next_offset.?; | 2202 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| 1996 | dyld_info.lazy_bind_size = bind_size; | | |
| 1997 | | | |
| 1998 | log.debug("writing lazy binding info table from 0x{x} to 0x{x}\n", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + bind_size }); | | |
| 1999 | | 2203 | |
| 2000 | if (bind_size > buffer.len) { | 2204 | if (needed_size > allocated_size) { |
| 2001 | // Pad out to align(8). | 2205 | dyld_info.lazy_bind_off = 0; |
| 2002 | try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.lazy_bind_off + bind_size); | 2206 | dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1)); |
| 2003 | } | 2207 | } |
| 2004 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | | |
| 2005 | | 2208 | |
| 2006 | self.linkedit_segment_next_offset = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; | 2209 | dyld_info.lazy_bind_size = @intCast(u32, needed_size); |
| 2007 | // Advance size of __LINKEDIT segment | 2210 | log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); |
| 2008 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2211 | |
| 2009 | linkedit.inner.filesize += dyld_info.lazy_bind_size; | 2212 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| 2010 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | 2213 | self.load_commands_dirty = true; |
| 2011 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | 2214 | self.lazy_binding_info_dirty = false; |
| 2012 | } | | |
| 2013 | self.cmd_table_dirty = true; | | |
| 2014 | } | 2215 | } |
| 2015 | | 2216 | |
| 2016 | fn writeStringTable(self: *MachO) !void { | 2217 | fn writeStringTable(self: *MachO) !void { |
| 2017 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 2218 | if (!self.string_table_dirty) return; |
| 2018 | const needed_size = self.string_table.items.len; | | |
| 2019 | | 2219 | |
| 2020 | symtab.stroff = self.linkedit_segment_next_offset.?; | 2220 | const tracy = trace(@src()); |
| 2021 | symtab.strsize = @intCast(u32, mem.alignForward(needed_size, @sizeOf(u64))); | 2221 | defer tracy.end(); |
| 2022 | | 2222 | |
| 2023 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | 2223 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 2224 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 2225 | const allocated_size = self.allocatedSize(&linkedit_segment, symtab.stroff); |
| | 2226 | const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64)); |
| 2024 | | 2227 | |
| 2025 | if (symtab.strsize > needed_size) { | 2228 | if (needed_size > allocated_size) { |
| 2026 | // Pad out to align(8); | 2229 | symtab.strsize = 0; |
| 2027 | try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize); | 2230 | symtab.stroff = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1)); |
| 2028 | } | 2231 | } |
| | 2232 | symtab.strsize = @intCast(u32, needed_size); |
| | 2233 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| | 2234 | |
| 2029 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | 2235 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); |
| | 2236 | self.load_commands_dirty = true; |
| | 2237 | self.string_table_dirty = false; |
| | 2238 | } |
| 2030 | | 2239 | |
| 2031 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; | 2240 | fn updateLinkeditSegmentSizes(self: *MachO) !void { |
| 2032 | // Advance size of __LINKEDIT segment | 2241 | if (!self.load_commands_dirty) return; |
| 2033 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2242 | |
| 2034 | linkedit.inner.filesize += symtab.strsize; | 2243 | const tracy = trace(@src()); |
| 2035 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { | 2244 | defer tracy.end(); |
| 2036 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); | 2245 | |
| 2037 | } | 2246 | // Now, we are in position to update __LINKEDIT segment sizes. |
| 2038 | self.cmd_table_dirty = true; | 2247 | // TODO Add checkpointing so that we don't have to do this every single time. |
| | 2248 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 2249 | var final_offset = linkedit_segment.inner.fileoff; |
| | 2250 | |
| | 2251 | if (self.dyld_info_cmd_index) |idx| { |
| | 2252 | const dyld_info = self.load_commands.items[idx].DyldInfoOnly; |
| | 2253 | final_offset = std.math.max(final_offset, dyld_info.rebase_off + dyld_info.rebase_size); |
| | 2254 | final_offset = std.math.max(final_offset, dyld_info.bind_off + dyld_info.bind_size); |
| | 2255 | final_offset = std.math.max(final_offset, dyld_info.weak_bind_off + dyld_info.weak_bind_size); |
| | 2256 | final_offset = std.math.max(final_offset, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size); |
| | 2257 | final_offset = std.math.max(final_offset, dyld_info.export_off + dyld_info.export_size); |
| | 2258 | } |
| | 2259 | if (self.function_starts_cmd_index) |idx| { |
| | 2260 | const fstart = self.load_commands.items[idx].LinkeditData; |
| | 2261 | final_offset = std.math.max(final_offset, fstart.dataoff + fstart.datasize); |
| | 2262 | } |
| | 2263 | if (self.data_in_code_cmd_index) |idx| { |
| | 2264 | const dic = self.load_commands.items[idx].LinkeditData; |
| | 2265 | final_offset = std.math.max(final_offset, dic.dataoff + dic.datasize); |
| | 2266 | } |
| | 2267 | if (self.dysymtab_cmd_index) |idx| { |
| | 2268 | const dysymtab = self.load_commands.items[idx].Dysymtab; |
| | 2269 | const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32); |
| | 2270 | final_offset = std.math.max(final_offset, dysymtab.indirectsymoff + nindirectsize); |
| | 2271 | // TODO Handle more dynamic symbol table sections. |
| | 2272 | } |
| | 2273 | if (self.symtab_cmd_index) |idx| { |
| | 2274 | const symtab = self.load_commands.items[idx].Symtab; |
| | 2275 | const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 2276 | final_offset = std.math.max(final_offset, symtab.symoff + symsize); |
| | 2277 | final_offset = std.math.max(final_offset, symtab.stroff + symtab.strsize); |
| | 2278 | } |
| | 2279 | |
| | 2280 | const filesize = final_offset - linkedit_segment.inner.fileoff; |
| | 2281 | linkedit_segment.inner.filesize = filesize; |
| | 2282 | linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, filesize, self.page_size); |
| | 2283 | try self.base.file.?.pwriteAll(&[_]u8{ 0 }, final_offset); |
| | 2284 | self.load_commands_dirty = true; |
| 2039 | } | 2285 | } |
| 2040 | | 2286 | |
| 2041 | /// Writes all load commands and section headers. | 2287 | /// Writes all load commands and section headers. |
| 2042 | fn writeLoadCommands(self: *MachO) !void { | 2288 | fn writeLoadCommands(self: *MachO) !void { |
| | 2289 | if (!self.load_commands_dirty) return; |
| | 2290 | |
| 2043 | var sizeofcmds: usize = 0; | 2291 | var sizeofcmds: usize = 0; |
| 2044 | for (self.load_commands.items) |lc| { | 2292 | for (self.load_commands.items) |lc| { |
| 2045 | sizeofcmds += lc.cmdsize(); | 2293 | sizeofcmds += lc.cmdsize(); |
| ... | @@ -2052,26 +2300,25 @@ fn writeLoadCommands(self: *MachO) !void { | ... | @@ -2052,26 +2300,25 @@ fn writeLoadCommands(self: *MachO) !void { |
| 2052 | try lc.write(writer); | 2300 | try lc.write(writer); |
| 2053 | } | 2301 | } |
| 2054 | | 2302 | |
| 2055 | try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); | 2303 | const off = @sizeOf(macho.mach_header_64); |
| | 2304 | log.debug("writing {} load commands from 0x{x} to 0x{x}", .{self.load_commands.items.len, off, off + sizeofcmds}); |
| | 2305 | try self.base.file.?.pwriteAll(buffer, off); |
| | 2306 | self.load_commands_dirty = false; |
| 2056 | } | 2307 | } |
| 2057 | | 2308 | |
| 2058 | /// Writes Mach-O file header. | 2309 | /// Writes Mach-O file header. |
| 2059 | fn writeHeader(self: *MachO) !void { | 2310 | fn writeHeader(self: *MachO) !void { |
| | 2311 | if (!self.header_dirty) return; |
| | 2312 | |
| 2060 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); | 2313 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); |
| 2061 | var sizeofcmds: u32 = 0; | 2314 | var sizeofcmds: u32 = 0; |
| 2062 | for (self.load_commands.items) |cmd| { | 2315 | for (self.load_commands.items) |cmd| { |
| 2063 | sizeofcmds += cmd.cmdsize(); | 2316 | sizeofcmds += cmd.cmdsize(); |
| 2064 | } | 2317 | } |
| 2065 | self.header.?.sizeofcmds = sizeofcmds; | 2318 | self.header.?.sizeofcmds = sizeofcmds; |
| 2066 | log.debug("writing Mach-O header {}\n", .{self.header.?}); | 2319 | log.debug("writing Mach-O header {}", .{self.header.?}); |
| 2067 | const slice = [1]macho.mach_header_64{self.header.?}; | 2320 | try self.base.file.?.pwriteAll(mem.asBytes(&self.header.?), 0); |
| 2068 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0); | 2321 | self.header_dirty = false; |
| 2069 | } | | |
| 2070 | | | |
| 2071 | /// Saturating multiplication | | |
| 2072 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | | |
| 2073 | const T = @TypeOf(a, b); | | |
| 2074 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | | |
| 2075 | } | 2322 | } |
| 2076 | | 2323 | |
| 2077 | /// Parse MachO contents from existing binary file. | 2324 | /// Parse MachO contents from existing binary file. |
| ... | @@ -2088,18 +2335,18 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { | ... | @@ -2088,18 +2335,18 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2088 | switch (cmd.cmd()) { | 2335 | switch (cmd.cmd()) { |
| 2089 | macho.LC_SEGMENT_64 => { | 2336 | macho.LC_SEGMENT_64 => { |
| 2090 | const x = cmd.Segment; | 2337 | const x = cmd.Segment; |
| 2091 | if (parseAndCmpName(x.inner.segname[0..], "__PAGEZERO")) { | 2338 | if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) { |
| 2092 | self.pagezero_segment_cmd_index = i; | 2339 | self.pagezero_segment_cmd_index = i; |
| 2093 | } else if (parseAndCmpName(x.inner.segname[0..], "__LINKEDIT")) { | 2340 | } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) { |
| 2094 | self.linkedit_segment_cmd_index = i; | 2341 | self.linkedit_segment_cmd_index = i; |
| 2095 | } else if (parseAndCmpName(x.inner.segname[0..], "__TEXT")) { | 2342 | } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) { |
| 2096 | self.text_segment_cmd_index = i; | 2343 | self.text_segment_cmd_index = i; |
| 2097 | for (x.sections.items) |sect, j| { | 2344 | for (x.sections.items) |sect, j| { |
| 2098 | if (parseAndCmpName(sect.sectname[0..], "__text")) { | 2345 | if (parseAndCmpName(&sect.sectname, "__text")) { |
| 2099 | self.text_section_index = @intCast(u16, j); | 2346 | self.text_section_index = @intCast(u16, j); |
| 2100 | } | 2347 | } |
| 2101 | } | 2348 | } |
| 2102 | } else if (parseAndCmpName(x.inner.segname[0..], "__DATA")) { | 2349 | } else if (parseAndCmpName(&x.inner.segname, "__DATA")) { |
| 2103 | self.data_segment_cmd_index = i; | 2350 | self.data_segment_cmd_index = i; |
| 2104 | } | 2351 | } |
| 2105 | }, | 2352 | }, |
| ... | @@ -2140,7 +2387,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { | ... | @@ -2140,7 +2387,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2140 | self.code_signature_cmd_index = i; | 2387 | self.code_signature_cmd_index = i; |
| 2141 | }, | 2388 | }, |
| 2142 | else => { | 2389 | else => { |
| 2143 | std.log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | 2390 | log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); |
| 2144 | }, | 2391 | }, |
| 2145 | } | 2392 | } |
| 2146 | self.load_commands.appendAssumeCapacity(cmd); | 2393 | self.load_commands.appendAssumeCapacity(cmd); |
| ... | @@ -2149,7 +2396,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { | ... | @@ -2149,7 +2396,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2149 | } | 2396 | } |
| 2150 | | 2397 | |
| 2151 | fn parseAndCmpName(name: []const u8, needle: []const u8) bool { | 2398 | fn parseAndCmpName(name: []const u8, needle: []const u8) bool { |
| 2152 | const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len; | 2399 | const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; |
| 2153 | return mem.eql(u8, name[0..len], needle); | 2400 | return mem.eql(u8, name[0..len], needle); |
| 2154 | } | 2401 | } |
| 2155 | | 2402 | |