authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-22 14:23:55+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-22 14:23:55+01:00
log43dbe86226fe89c6364fa0261297f1a9d8eb2a58
tree44d2b398b48d411ca9d845a952a73837e881ae6b
parent286077fec8f381c7b4d4d5bf351d963564a1dd69
parent34663abc9090d7b3afc2bc83d159c1d950b23e1d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7516 from kubkon/macho-better-space-alloc

macho: space preallocation, and various cleanups and fixes

4 files changed, 575 insertions(+), 322 deletions(-)

src/link/MachO.zig+549-302
...@@ -37,6 +37,10 @@ page_size: u16,...@@ -37,6 +37,10 @@ page_size: u16,
3737
38/// Mach-O header38/// Mach-O header
39header: ?macho.mach_header_64 = null,39header: ?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.
43header_pad: u16 = 0x1000,
4044
41/// Table of all load commands45/// Table of all load commands
42load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},46load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
...@@ -75,14 +79,11 @@ code_signature_cmd_index: ?u16 = null,...@@ -75,14 +79,11 @@ code_signature_cmd_index: ?u16 = null,
7579
76/// Index into __TEXT,__text section.80/// Index into __TEXT,__text section.
77text_section_index: ?u16 = null,81text_section_index: ?u16 = null,
78/// Index into __TEXT,__got section.82/// Index into __TEXT,__ziggot section.
79got_section_index: ?u16 = null,83got_section_index: ?u16 = null,
80/// The absolute address of the entry point.84/// The absolute address of the entry point.
81entry_addr: ?u64 = null,85entry_addr: ?u64 = null,
8286
83/// TODO move this into each Segment aggregator
84linkedit_segment_next_offset: ?u32 = null,
85
86/// Table of all local symbols87/// 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).
88local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},89local_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.
101string_table: std.ArrayListUnmanaged(u8) = .{},102string_table: std.ArrayListUnmanaged(u8) = .{},
102103
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.
106offset_table: std.ArrayListUnmanaged(u64) = .{},105offset_table: std.ArrayListUnmanaged(u64) = .{},
107106
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 = .{},
112111
113error_flags: File.ErrorFlags = File.ErrorFlags{},112error_flags: File.ErrorFlags = File.ErrorFlags{},
114113
115cmd_table_dirty: bool = false,114offset_table_count_dirty: bool = false,
115header_dirty: bool = false,
116load_commands_dirty: bool = false,
117binding_info_dirty: bool = false,
118lazy_binding_info_dirty: bool = false,
119export_info_dirty: bool = false,
120string_table_dirty: bool = false,
116121
117/// A list of text blocks that have surplus capacity. This list can have false122/// 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 added123/// 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();
324333
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 }
337346
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 }
343349
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 }
351357
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);
353365
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;
769781
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 }
776788
...@@ -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;
809823
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 }
815829
...@@ -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 }
825839
826 // Write update dyld info840 // 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 header867 // 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;
863880
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 }
870887
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 command888 // 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;
884900
885 // Pad out space for code signature901 // 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 signature906 // 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);
10031022
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 }
10191039
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);
10801102
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 }
10911115
...@@ -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 4GB1337 .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);
13181353
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);
13221362
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 });
13241364
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_641371 .@"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 });
13391379 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);
13501386
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 to1388 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.
13541391
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 });
13561393
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 });
13711408 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,
16311705
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.
16371710
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;
16401713
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;
16441717
...@@ -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,
1667pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {1740pub 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}
16731746
...@@ -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}
16811755
...@@ -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}
16941768
1695fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {1769const 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);1774fn 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
1793fn 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
1840inline 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;1849fn 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}
17501906
1751fn 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}
17771930
1778fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {1931fn 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}
17851941
1942/// Saturating multiplication
1943fn 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
1786fn writeOffsetTableEntry(self: *MachO, index: usize) !void {1948fn 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;
17911953
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 architecture1980 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}
18181985
1819fn writeSymbolTable(self: *MachO) !void {1986fn 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 });
18222007
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
2017fn 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
2027fn 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);
18272039
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);
18322044
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);
18372049
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);
18532057 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}
18622059
1863fn writeCodeSignaturePadding(self: *MachO) !void {2060fn 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) {
18692070 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 segment2072
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 file2079 // 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}
18822085
1883fn writeCodeSignature(self: *MachO) !void {2086fn 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;
18862092
...@@ -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);
19002106
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 });
19022108
1903 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);2109 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
1904}2110}
19052111
1906fn writeExportTrie(self: *MachO) !void {2112fn 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;
19082115
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();
19112121
...@@ -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);
19302140
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;
19352145
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) {
19372147 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 });
19432152
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 segment2154 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}
19532157
1954fn writeBindingInfoTable(self: *MachO) !void {2158fn 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);
19582167
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());
19612170
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;
19662175
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) {
19682177 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);
19742180
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 segment2182 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}
19842188
1985fn writeLazyBindingInfoTable(self: *MachO) !void {2189fn 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);
19892195
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());
19922198
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 });
19992203
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);
20052208
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 segment2210 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}
20152216
2016fn writeStringTable(self: *MachO) !void {2217fn 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;
20192219
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();
20222222
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));
20242227
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}
20302239
2031 self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize;2240fn updateLinkeditSegmentSizes(self: *MachO) !void {
2032 // Advance size of __LINKEDIT segment2241 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}
20402286
2041/// Writes all load commands and section headers.2287/// Writes all load commands and section headers.
2042fn writeLoadCommands(self: *MachO) !void {2288fn 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 }
20542302
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}
20572308
2058/// Writes Mach-O file header.2309/// Writes Mach-O file header.
2059fn writeHeader(self: *MachO) !void {2310fn 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
2072fn 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}
20762323
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}
21502397
2151fn parseAndCmpName(name: []const u8, needle: []const u8) bool {2398fn 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}
21552402
src/link/MachO/CodeSignature.zig+3-3
...@@ -126,7 +126,7 @@ pub fn calcAdhocSignature(...@@ -126,7 +126,7 @@ pub fn calcAdhocSignature(
126126
127 Sha256.hash(buffer[0..fsize], &hash, .{});127 Sha256.hash(buffer[0..fsize], &hash, .{});
128128
129 cdir.data.appendSliceAssumeCapacity(hash[0..]);129 cdir.data.appendSliceAssumeCapacity(&hash);
130 cdir.inner.nCodeSlots += 1;130 cdir.inner.nCodeSlots += 1;
131 }131 }
132132
...@@ -174,10 +174,10 @@ test "CodeSignature header" {...@@ -174,10 +174,10 @@ test "CodeSignature header" {
174 defer code_sig.deinit();174 defer code_sig.deinit();
175175
176 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;176 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;
177 code_sig.writeHeader(buffer[0..]);177 code_sig.writeHeader(&buffer);
178178
179 const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 };179 const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 };
180 testing.expect(mem.eql(u8, expected[0..], buffer[0..]));180 testing.expect(mem.eql(u8, expected, &buffer));
181}181}
182182
183pub fn calcCodeSignaturePadding(id: []const u8, file_size: u64) u32 {183pub fn calcCodeSignaturePadding(id: []const u8, file_size: u64) u32 {
src/link/MachO/Trie.zig+4-4
...@@ -531,14 +531,14 @@ test "write Trie to a byte stream" {...@@ -531,14 +531,14 @@ test "write Trie to a byte stream" {
531 {531 {
532 const nwritten = try trie.write(stream.writer());532 const nwritten = try trie.write(stream.writer());
533 testing.expect(nwritten == trie.size);533 testing.expect(nwritten == trie.size);
534 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));534 testing.expect(mem.eql(u8, buffer, &exp_buffer));
535 }535 }
536 {536 {
537 // Writing finalized trie again should yield the same result.537 // Writing finalized trie again should yield the same result.
538 try stream.seekTo(0);538 try stream.seekTo(0);
539 const nwritten = try trie.write(stream.writer());539 const nwritten = try trie.write(stream.writer());
540 testing.expect(nwritten == trie.size);540 testing.expect(nwritten == trie.size);
541 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));541 testing.expect(mem.eql(u8, buffer, &exp_buffer));
542 }542 }
543}543}
544544
...@@ -556,7 +556,7 @@ test "parse Trie from byte stream" {...@@ -556,7 +556,7 @@ test "parse Trie from byte stream" {
556 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node556 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
557 };557 };
558558
559 var in_stream = std.io.fixedBufferStream(in_buffer[0..]);559 var in_stream = std.io.fixedBufferStream(&in_buffer);
560 var trie = Trie.init(gpa);560 var trie = Trie.init(gpa);
561 defer trie.deinit();561 defer trie.deinit();
562 const nread = try trie.read(in_stream.reader());562 const nread = try trie.read(in_stream.reader());
...@@ -571,5 +571,5 @@ test "parse Trie from byte stream" {...@@ -571,5 +571,5 @@ test "parse Trie from byte stream" {
571 const nwritten = try trie.write(out_stream.writer());571 const nwritten = try trie.write(out_stream.writer());
572572
573 testing.expect(nwritten == trie.size);573 testing.expect(nwritten == trie.size);
574 testing.expect(mem.eql(u8, in_buffer[0..], out_buffer));574 testing.expect(mem.eql(u8, &in_buffer, out_buffer));
575}575}
src/link/MachO/commands.zig+19-13
...@@ -7,7 +7,7 @@ const macho = std.macho;...@@ -7,7 +7,7 @@ const macho = std.macho;
7const testing = std.testing;7const testing = std.testing;
88
9const Allocator = std.mem.Allocator;9const Allocator = std.mem.Allocator;
10const makeName = @import("../MachO.zig").makeStaticString;10const makeStaticString = @import("../MachO.zig").makeStaticString;
1111
12pub const LoadCommand = union(enum) {12pub const LoadCommand = union(enum) {
13 Segment: SegmentCommand,13 Segment: SegmentCommand,
...@@ -26,9 +26,9 @@ pub const LoadCommand = union(enum) {...@@ -26,9 +26,9 @@ pub const LoadCommand = union(enum) {
26 const header = try reader.readStruct(macho.load_command);26 const header = try reader.readStruct(macho.load_command);
27 var buffer = try allocator.alloc(u8, header.cmdsize);27 var buffer = try allocator.alloc(u8, header.cmdsize);
28 defer allocator.free(buffer);28 defer allocator.free(buffer);
29 mem.copy(u8, buffer[0..], mem.asBytes(&header));29 mem.copy(u8, buffer, mem.asBytes(&header));
30 try reader.readNoEof(buffer[@sizeOf(macho.load_command)..]);30 try reader.readNoEof(buffer[@sizeOf(macho.load_command)..]);
31 var stream = io.fixedBufferStream(buffer[0..]);31 var stream = io.fixedBufferStream(buffer);
3232
33 return switch (header.cmd) {33 return switch (header.cmd) {
34 macho.LC_SEGMENT_64 => LoadCommand{34 macho.LC_SEGMENT_64 => LoadCommand{
...@@ -155,6 +155,12 @@ pub const SegmentCommand = struct {...@@ -155,6 +155,12 @@ pub const SegmentCommand = struct {
155 return .{ .inner = inner };155 return .{ .inner = inner };
156 }156 }
157157
158 pub fn addSection(self: *SegmentCommand, alloc: *Allocator, section: macho.section_64) !void {
159 try self.sections.append(alloc, section);
160 self.inner.cmdsize += @sizeOf(macho.section_64);
161 self.inner.nsects += 1;
162 }
163
158 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {164 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
159 const inner = try reader.readStruct(macho.segment_command_64);165 const inner = try reader.readStruct(macho.segment_command_64);
160 var segment = SegmentCommand{166 var segment = SegmentCommand{
...@@ -210,7 +216,7 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {...@@ -210,7 +216,7 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
210 const inner = try reader.readStruct(Cmd);216 const inner = try reader.readStruct(Cmd);
211 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));217 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
212 errdefer allocator.free(data);218 errdefer allocator.free(data);
213 try reader.readNoEof(data[0..]);219 try reader.readNoEof(data);
214 return Self{220 return Self{
215 .inner = inner,221 .inner = inner,
216 .data = data,222 .data = data,
...@@ -277,7 +283,7 @@ test "read-write segment command" {...@@ -277,7 +283,7 @@ test "read-write segment command" {
277 .inner = .{283 .inner = .{
278 .cmd = macho.LC_SEGMENT_64,284 .cmd = macho.LC_SEGMENT_64,
279 .cmdsize = 152,285 .cmdsize = 152,
280 .segname = makeName("__TEXT"),286 .segname = makeStaticString("__TEXT"),
281 .vmaddr = 4294967296,287 .vmaddr = 4294967296,
282 .vmsize = 294912,288 .vmsize = 294912,
283 .fileoff = 0,289 .fileoff = 0,
...@@ -289,8 +295,8 @@ test "read-write segment command" {...@@ -289,8 +295,8 @@ test "read-write segment command" {
289 },295 },
290 };296 };
291 try cmd.sections.append(gpa, .{297 try cmd.sections.append(gpa, .{
292 .sectname = makeName("__text"),298 .sectname = makeStaticString("__text"),
293 .segname = makeName("__TEXT"),299 .segname = makeStaticString("__TEXT"),
294 .addr = 4294983680,300 .addr = 4294983680,
295 .size = 448,301 .size = 448,
296 .offset = 16384,302 .offset = 16384,
...@@ -303,10 +309,10 @@ test "read-write segment command" {...@@ -303,10 +309,10 @@ test "read-write segment command" {
303 .reserved3 = 0,309 .reserved3 = 0,
304 });310 });
305 defer cmd.deinit(gpa);311 defer cmd.deinit(gpa);
306 try testRead(gpa, in_buffer[0..], LoadCommand{ .Segment = cmd });312 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
307313
308 var out_buffer: [in_buffer.len]u8 = undefined;314 var out_buffer: [in_buffer.len]u8 = undefined;
309 try testWrite(out_buffer[0..], LoadCommand{ .Segment = cmd }, in_buffer[0..]);315 try testWrite(&out_buffer, LoadCommand{ .Segment = cmd }, in_buffer);
310}316}
311317
312test "read-write generic command with data" {318test "read-write generic command with data" {
...@@ -342,10 +348,10 @@ test "read-write generic command with data" {...@@ -342,10 +348,10 @@ test "read-write generic command with data" {
342 cmd.data[5] = 0x0;348 cmd.data[5] = 0x0;
343 cmd.data[6] = 0x0;349 cmd.data[6] = 0x0;
344 cmd.data[7] = 0x0;350 cmd.data[7] = 0x0;
345 try testRead(gpa, in_buffer[0..], LoadCommand{ .Dylib = cmd });351 try testRead(gpa, in_buffer, LoadCommand{ .Dylib = cmd });
346352
347 var out_buffer: [in_buffer.len]u8 = undefined;353 var out_buffer: [in_buffer.len]u8 = undefined;
348 try testWrite(out_buffer[0..], LoadCommand{ .Dylib = cmd }, in_buffer[0..]);354 try testWrite(&out_buffer, LoadCommand{ .Dylib = cmd }, in_buffer);
349}355}
350356
351test "read-write C struct command" {357test "read-write C struct command" {
...@@ -362,8 +368,8 @@ test "read-write C struct command" {...@@ -362,8 +368,8 @@ test "read-write C struct command" {
362 .entryoff = 16644,368 .entryoff = 16644,
363 .stacksize = 0,369 .stacksize = 0,
364 };370 };
365 try testRead(gpa, in_buffer[0..], LoadCommand{ .Main = cmd });371 try testRead(gpa, in_buffer, LoadCommand{ .Main = cmd });
366372
367 var out_buffer: [in_buffer.len]u8 = undefined;373 var out_buffer: [in_buffer.len]u8 = undefined;
368 try testWrite(out_buffer[0..], LoadCommand{ .Main = cmd }, in_buffer[0..]);374 try testWrite(&out_buffer, LoadCommand{ .Main = cmd }, in_buffer);
369}375}