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,
3737
3838/// Mach-O header
3939header: ?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
4145/// Table of all load commands
4246load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
......@@ -75,14 +79,11 @@ code_signature_cmd_index: ?u16 = null,
7579
7680/// Index into __TEXT,__text section.
7781text_section_index: ?u16 = null,
78/// Index into __TEXT,__got section.
82/// Index into __TEXT,__ziggot section.
7983got_section_index: ?u16 = null,
8084/// The absolute address of the entry point.
8185entry_addr: ?u64 = null,
8286
83/// TODO move this into each Segment aggregator
84linkedit_segment_next_offset: ?u32 = null,
85
8687/// Table of all local symbols
8788/// Internally references string table for names (which are optional).
8889local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
......@@ -100,9 +101,7 @@ dyld_stub_binder_index: ?u16 = null,
100101/// Table of symbol names aka the string table.
101102string_table: std.ArrayListUnmanaged(u8) = .{},
102103
103/// Table of symbol vaddr values. The values is the absolute vaddr value.
104/// If the vaddr of the executable __TEXT segment vaddr changes, the entire offset
105/// table needs to be rewritten.
104/// Table of trampolines to the actual symbols in __text section.
106105offset_table: std.ArrayListUnmanaged(u64) = .{},
107106
108107/// Table of binding info entries.
......@@ -112,7 +111,13 @@ lazy_binding_info_table: LazyBindingInfoTable = .{},
112111
113112error_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
117122/// A list of text blocks that have surplus capacity. This list can have false
118123/// positives, as functions grow and shrink over time, only sometimes being added
......@@ -317,10 +322,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
317322 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
318323 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main;
319324 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
325 self.load_commands_dirty = true;
320326 }
327 try self.writeBindingInfoTable();
328 try self.writeLazyBindingInfoTable();
321329 try self.writeExportTrie();
322 try self.writeSymbolTable();
330 try self.writeAllGlobalAndUndefSymbols();
323331 try self.writeStringTable();
332 try self.updateLinkeditSegmentSizes();
324333
325334 if (target.cpu.arch == .aarch64) {
326335 // Preallocate space for the code signature.
......@@ -335,21 +344,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
335344 .Lib => return error.TODOImplementWritingLibFiles,
336345 }
337346
338 if (self.cmd_table_dirty) {
339 try self.writeLoadCommands();
340 try self.writeHeader();
341 self.cmd_table_dirty = false;
342 }
347 try self.writeLoadCommands();
348 try self.writeHeader();
343349
344350 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", .{});
346352 self.error_flags.no_entry_point_found = true;
347353 } else {
348 log.debug("flushing. no_entry_point_found = false\n", .{});
354 log.debug("flushing. no_entry_point_found = false", .{});
349355 self.error_flags.no_entry_point_found = false;
350356 }
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
354366 if (target.cpu.arch == .aarch64) {
355367 switch (output_mode) {
......@@ -768,9 +780,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
768780 const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;
769781
770782 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.", .{});
772 std.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'.", .{});
783 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
784 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
785 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
774786 return error.NotEnoughPadding;
775787 }
776788
......@@ -806,10 +818,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
806818 mem.set(u8, dylib_cmd.data, 0);
807819 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
808820 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
821 self.header_dirty = true;
822 self.load_commands_dirty = true;
809823
810824 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!", .{});
812 std.log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
825 log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
826 log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
813827 return error.NoSymbolTableFound;
814828 }
815829
......@@ -823,7 +837,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
823837 symbol.dylib_ordinal = next_ordinal;
824838 }
825839
826 // Write update dyld info
840 // Write updated dyld info.
827841 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
828842 {
829843 const size = try self.binding_info_table.calcSize();
......@@ -853,6 +867,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
853867 // Write updated load commands and the header
854868 try self.writeLoadCommands();
855869 try self.writeHeader();
870
871 assert(!self.header_dirty);
872 assert(!self.load_commands_dirty);
856873 }
857874 if (self.code_signature_cmd_index == null) outer: {
858875 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 {
862879 const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;
863880
864881 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.", .{});
866 std.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'.", .{});
882 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
883 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
884 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
868885 return error.NotEnoughPadding;
869886 }
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)));
874888 // Add code signature load command
875889 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
876890 try self.load_commands.append(self.base.allocator, .{
......@@ -881,6 +895,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
881895 .datasize = 0,
882896 },
883897 });
898 self.header_dirty = true;
899 self.load_commands_dirty = true;
884900
885901 // Pad out space for code signature
886902 try self.writeCodeSignaturePadding();
......@@ -889,6 +905,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
889905 try self.writeHeader();
890906 // Generate adhoc code signature
891907 try self.writeCodeSignature();
908
909 assert(!self.header_dirty);
910 assert(!self.load_commands_dirty);
892911 }
893912 }
894913 }
......@@ -1002,10 +1021,10 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
10021021 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
10031022
10041023 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 });
10061025 decl.link.macho.local_sym_index = i;
10071026 } 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 });
10091028 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
10101029 _ = self.local_symbols.addOneAssumeCapacity();
10111030 }
......@@ -1015,6 +1034,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
10151034 } else {
10161035 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
10171036 _ = self.offset_table.addOneAssumeCapacity();
1037 self.offset_table_count_dirty = true;
10181038 }
10191039
10201040 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 {
10561076 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
10571077 if (need_realloc) {
10581078 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 });
10601080 if (vaddr != symbol.n_value) {
10611081 symbol.n_value = vaddr;
1062 log.debug(" (writing new offset table entry)\n", .{});
1082 log.debug(" (writing new offset table entry)", .{});
10631083 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;
10641084 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
10651085 }
......@@ -1071,11 +1091,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
10711091 symbol.n_type = macho.N_SECT;
10721092 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
10731093 symbol.n_desc = 0;
1094
1095 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
10741096 } else {
10751097 const decl_name = mem.spanZ(decl.name);
10761098 const name_str_index = try self.makeString(decl_name);
10771099 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 });
10791101 errdefer self.freeTextBlock(&decl.link.macho);
10801102
10811103 symbol.* = .{
......@@ -1086,6 +1108,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
10861108 .n_value = addr,
10871109 };
10881110 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
1111
1112 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
10891113 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
10901114 }
10911115
......@@ -1151,7 +1175,6 @@ pub fn updateDeclExports(
11511175 .Strong => blk: {
11521176 if (mem.eql(u8, exp.options.name, "_start")) {
11531177 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.
11551178 }
11561179 break :blk macho.REFERENCE_FLAG_DEFINED;
11571180 },
......@@ -1179,6 +1202,7 @@ pub fn updateDeclExports(
11791202 const name_str_index = try self.makeString(exp.options.name);
11801203 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
11811204 _ = self.global_symbols.addOneAssumeCapacity();
1205 self.export_info_dirty = true;
11821206 break :blk self.global_symbols.items.len - 1;
11831207 };
11841208 self.global_symbols.items[i] = .{
......@@ -1271,6 +1295,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
12711295 }
12721296 header.reserved = 0;
12731297 self.header = header;
1298 self.header_dirty = true;
12741299 }
12751300 if (self.pagezero_segment_cmd_index == null) {
12761301 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1289,107 +1314,117 @@ pub fn populateMissingMetadata(self: *MachO) !void {
12891314 .flags = 0,
12901315 }),
12911316 });
1292 self.cmd_table_dirty = true;
1317 self.header_dirty = true;
1318 self.load_commands_dirty = true;
12931319 }
12941320 if (self.text_segment_cmd_index == null) {
12951321 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
12961322 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
12971323 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
12981332 try self.load_commands.append(self.base.allocator, .{
12991333 .Segment = SegmentCommand.empty(.{
13001334 .cmd = macho.LC_SEGMENT_64,
13011335 .cmdsize = @sizeOf(macho.segment_command_64),
13021336 .segname = makeStaticString("__TEXT"),
13031337 .vmaddr = 0x100000000, // always starts at 4GB
1304 .vmsize = 0,
1338 .vmsize = needed_size,
13051339 .fileoff = 0,
1306 .filesize = 0,
1340 .filesize = needed_size,
13071341 .maxprot = maxprot,
13081342 .initprot = initprot,
13091343 .nsects = 0,
13101344 .flags = 0,
13111345 }),
13121346 });
1313 self.cmd_table_dirty = true;
1347 self.header_dirty = true;
1348 self.load_commands_dirty = true;
13141349 }
13151350 if (self.text_section_index == null) {
13161351 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
13171352 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;
1320 const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size);
1321 const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly?
1354 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1355 .x86_64 => 0,
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, .{
13261366 .sectname = makeStaticString("__text"),
13271367 .segname = makeStaticString("__TEXT"),
13281368 .addr = text_segment.inner.vmaddr + off,
1329 .size = file_size,
1330 .offset = off,
1331 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64
1369 .size = @intCast(u32, needed_size),
1370 .offset = @intCast(u32, off),
1371 .@"align" = alignment,
13321372 .reloff = 0,
13331373 .nreloc = 0,
1334 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
1374 .flags = flags,
13351375 .reserved1 = 0,
13361376 .reserved2 = 0,
13371377 .reserved3 = 0,
13381378 });
1339
1340 text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
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;
1379 self.header_dirty = true;
1380 self.load_commands_dirty = true;
13451381 }
13461382 if (self.got_section_index == null) {
13471383 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
13481384 const text_section = &text_segment.sections.items[self.text_section_index.?];
13491385 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
13501386
1351 const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1352 // TODO looking for free space should be done *within* a segment it belongs to
1353 const off = @intCast(u32, text_section.offset + text_section.size);
1387 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1388 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
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, .{
1358 .sectname = makeStaticString("__got"),
1394 try text_segment.addSection(self.base.allocator, .{
1395 .sectname = makeStaticString("__ziggot"),
13591396 .segname = makeStaticString("__TEXT"),
1360 .addr = text_section.addr + text_section.size,
1361 .size = file_size,
1362 .offset = off,
1363 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0,
1397 .addr = text_segment.inner.vmaddr + off,
1398 .size = needed_size,
1399 .offset = @intCast(u32, off),
1400 .@"align" = @sizeOf(u64),
13641401 .reloff = 0,
13651402 .nreloc = 0,
1366 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
1403 .flags = flags,
13671404 .reserved1 = 0,
13681405 .reserved2 = 0,
13691406 .reserved3 = 0,
13701407 });
1371
1372 const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size);
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;
1408 self.header_dirty = true;
1409 self.load_commands_dirty = true;
13781410 }
13791411 if (self.linkedit_segment_cmd_index == null) {
13801412 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
13821414 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
13831415 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
13851420 try self.load_commands.append(self.base.allocator, .{
13861421 .Segment = SegmentCommand.empty(.{
13871422 .cmd = macho.LC_SEGMENT_64,
13881423 .cmdsize = @sizeOf(macho.segment_command_64),
13891424 .segname = makeStaticString("__LINKEDIT"),
1390 .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize,
1425 .vmaddr = address_and_offset.address,
13911426 .vmsize = 0,
1392 .fileoff = off,
1427 .fileoff = address_and_offset.offset,
13931428 .filesize = 0,
13941429 .maxprot = maxprot,
13951430 .initprot = initprot,
......@@ -1397,11 +1432,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13971432 .flags = 0,
13981433 }),
13991434 });
1400 self.linkedit_segment_next_offset = @intCast(u32, off);
1401 self.cmd_table_dirty = true;
1435 self.header_dirty = true;
1436 self.load_commands_dirty = true;
14021437 }
14031438 if (self.dyld_info_cmd_index == null) {
14041439 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
14051448 try self.load_commands.append(self.base.allocator, .{
14061449 .DyldInfoOnly = .{
14071450 .cmd = macho.LC_DYLD_INFO_ONLY,
......@@ -1414,28 +1457,48 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14141457 .weak_bind_size = 0,
14151458 .lazy_bind_off = 0,
14161459 .lazy_bind_size = 0,
1417 .export_off = 0,
1418 .export_size = 0,
1460 .export_off = @intCast(u32, export_off),
1461 .export_size = export_size,
14191462 },
14201463 });
1421 self.cmd_table_dirty = true;
1464 self.header_dirty = true;
1465 self.load_commands_dirty = true;
14221466 }
14231467 if (self.symtab_cmd_index == null) {
14241468 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
14251482 try self.load_commands.append(self.base.allocator, .{
14261483 .Symtab = .{
14271484 .cmd = macho.LC_SYMTAB,
14281485 .cmdsize = @sizeOf(macho.symtab_command),
1429 .symoff = 0,
1430 .nsyms = 0,
1431 .stroff = 0,
1432 .strsize = 0,
1486 .symoff = @intCast(u32, symtab_off),
1487 .nsyms = @intCast(u32, self.base.options.symbol_count_hint),
1488 .stroff = @intCast(u32, strtab_off),
1489 .strsize = @intCast(u32, strtab_size),
14331490 },
14341491 });
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;
14361496 }
14371497 if (self.dysymtab_cmd_index == null) {
14381498 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
1499
1500 // TODO Preallocate space for indirect symbol table.
1501
14391502 try self.load_commands.append(self.base.allocator, .{
14401503 .Dysymtab = .{
14411504 .cmd = macho.LC_DYSYMTAB,
......@@ -1460,7 +1523,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14601523 .nlocrel = 0,
14611524 },
14621525 });
1463 self.cmd_table_dirty = true;
1526 self.header_dirty = true;
1527 self.load_commands_dirty = true;
14641528 }
14651529 if (self.dylinker_cmd_index == null) {
14661530 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1474,7 +1538,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14741538 mem.set(u8, dylinker_cmd.data, 0);
14751539 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
14761540 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;
14781543 }
14791544 if (self.libsystem_cmd_index == null) {
14801545 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1496,7 +1561,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14961561 mem.set(u8, dylib_cmd.data, 0);
14971562 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
14981563 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;
15001566 }
15011567 if (self.main_cmd_index == null) {
15021568 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1508,7 +1574,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15081574 .stacksize = 0,
15091575 },
15101576 });
1511 self.cmd_table_dirty = true;
1577 self.header_dirty = true;
1578 self.load_commands_dirty = true;
15121579 }
15131580 if (self.version_min_cmd_index == null) {
15141581 self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1529,6 +1596,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15291596 .sdk = version,
15301597 },
15311598 });
1599 self.header_dirty = true;
1600 self.load_commands_dirty = true;
15321601 }
15331602 if (self.source_version_cmd_index == null) {
15341603 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1539,9 +1608,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15391608 .version = 0x0,
15401609 },
15411610 });
1611 self.header_dirty = true;
1612 self.load_commands_dirty = true;
15421613 }
15431614 if (self.code_signature_cmd_index == null) {
15441615 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;
15451617 try self.load_commands.append(self.base.allocator, .{
15461618 .LinkeditData = .{
15471619 .cmd = macho.LC_CODE_SIGNATURE,
......@@ -1550,6 +1622,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15501622 .datasize = 0,
15511623 },
15521624 });
1625 self.header_dirty = true;
1626 self.load_commands_dirty = true;
15531627 }
15541628 if (self.dyld_stub_binder_index == null) {
15551629 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,
16311705
16321706 const expand_text_section = block_placement == null or block_placement.?.next == null;
16331707 if (expand_text_section) {
1634 const text_capacity = self.allocatedSize(text_section.offset);
16351708 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
16381711 self.last_text_block = text_block;
16391712 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.
16421715 }
16431716 text_block.size = new_block_size;
16441717
......@@ -1667,7 +1740,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
16671740pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
16681741 var buf = [_]u8{0} ** 16;
16691742 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);
16711744 return buf;
16721745}
16731746
......@@ -1676,6 +1749,7 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
16761749 const result = self.string_table.items.len;
16771750 self.string_table.appendSliceAssumeCapacity(bytes);
16781751 self.string_table.appendAssumeCapacity(0);
1752 self.string_table_dirty = true;
16791753 return @intCast(u32, result);
16801754}
16811755
......@@ -1692,103 +1766,196 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
16921766 return self.makeString(new_name);
16931767}
16941768
1695fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
1696 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
1697 if (start < hdr_size) return hdr_size;
1698 const end = start + satMul(size, alloc_num) / alloc_den;
1699 {
1700 const off = @sizeOf(macho.mach_header_64);
1701 var tight_size: u64 = 0;
1702 for (self.load_commands.items) |cmd| {
1703 tight_size += cmd.cmdsize();
1769const NextSegmentAddressAndOffset = struct {
1770 address: u64,
1771 offset: u64,
1772};
1773
1774fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
1775 const prev_segment_idx = blk: {
1776 if (self.data_segment_cmd_index) |idx| {
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;
17041808 }
1705 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1706 const test_end = off + increased_size;
1707 if (end > off and start < test_end) {
1708 return test_end;
1809
1810 if (self.function_starts_cmd_index) |idx| {
1811 const fstart = self.load_commands.items[idx].LinkeditData;
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;
17091818 }
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;
17101845 }
1711 if (self.text_segment_cmd_index) |text_index| {
1712 const text_segment = self.load_commands.items[text_index].Segment;
1713 for (text_segment.sections.items) |section| {
1714 const increased_size = satMul(section.size, alloc_num) / alloc_den;
1715 const test_end = section.offset + increased_size;
1716 if (end > section.offset and start < test_end) {
1717 return test_end;
1846 return null;
1847}
1848
1849fn detectAllocCollision(self: *MachO, segment: *const SegmentCommand, start: u64, size: u64) ?u64 {
1850 const end = start + satMul(size, alloc_num) / alloc_den;
1851
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;
17181877 }
17191878 }
1720 }
1721 if (self.dyld_info_cmd_index) |dyld_info_index| {
1722 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly;
1723 const tight_size = dyld_info.export_size;
1724 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1725 const test_end = dyld_info.export_off + increased_size;
1726 if (end > dyld_info.export_off and start < test_end) {
1727 return test_end;
1879
1880 if (self.function_starts_cmd_index) |idx| outer: {
1881 if (self.load_commands.items.len == idx) break :outer;
1882 const fstart = self.load_commands.items[idx].LinkeditData;
1883 if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| {
1884 return pos;
1885 }
17281886 }
1729 }
1730 if (self.symtab_cmd_index) |symtab_index| {
1731 const symtab = self.load_commands.items[symtab_index].Symtab;
1732 {
1733 const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
1734 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1735 const test_end = symtab.symoff + increased_size;
1736 if (end > symtab.symoff and start < test_end) {
1737 return test_end;
1887
1888 if (self.data_in_code_cmd_index) |idx| outer: {
1889 if (self.load_commands.items.len == idx) break :outer;
1890 const dic = self.load_commands.items[idx].LinkeditData;
1891 if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| {
1892 return pos;
17381893 }
17391894 }
1740 {
1741 const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
1742 const test_end = symtab.stroff + increased_size;
1743 if (end > symtab.stroff and start < test_end) {
1744 return test_end;
1895
1896 if (self.dysymtab_cmd_index) |idx| outer: {
1897 if (self.load_commands.items.len == idx) break :outer;
1898 const dysymtab = self.load_commands.items[idx].Dysymtab;
1899 // Indirect symbol table
1900 const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32);
1901 if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| {
1902 return pos;
17451903 }
1904 // TODO Handle more dynamic symbol table sections.
17461905 }
1747 }
1748 return null;
1749}
17501906
1751fn allocatedSize(self: *MachO, start: u64) u64 {
1752 if (start == 0)
1753 return 0;
1754 var min_pos: u64 = std.math.maxInt(u64);
1755 {
1756 const off = @sizeOf(macho.mach_header_64);
1757 if (off > start and off < min_pos) min_pos = off;
1758 }
1759 if (self.text_segment_cmd_index) |text_index| {
1760 const text_segment = self.load_commands.items[text_index].Segment;
1761 for (text_segment.sections.items) |section| {
1762 if (section.offset <= start) continue;
1763 if (section.offset < min_pos) min_pos = section.offset;
1907 if (self.symtab_cmd_index) |idx| outer: {
1908 if (self.load_commands.items.len == idx) break :outer;
1909 const symtab = self.load_commands.items[idx].Symtab;
1910 // Symbol table
1911 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
1912 if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| {
1913 return pos;
1914 }
1915 // String table
1916 if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| {
1917 return pos;
1918 }
1919 }
1920 } else {
1921 for (segment.sections.items) |section| {
1922 if (checkForCollision(start, end, section.offset, section.size)) |pos| {
1923 return pos;
1924 }
17641925 }
17651926 }
1766 if (self.dyld_info_cmd_index) |dyld_info_index| {
1767 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly;
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;
1927
1928 return null;
17761929}
17771930
1778fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
1779 var start: u64 = 0;
1780 while (self.detectAllocCollision(start, object_size)) |item_end| {
1931fn findFreeSpace(self: *MachO, segment: *const SegmentCommand, object_size: u64, min_alignment: u16) u64 {
1932 var start: u64 = if (parseAndCmpName(&segment.inner.segname, "__TEXT"))
1933 self.header_pad
1934 else
1935 segment.inner.fileoff;
1936 while (self.detectAllocCollision(segment, start, object_size)) |item_end| {
17811937 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
17821938 }
17831939 return start;
17841940}
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
17861948fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1787 const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1788 const sect = &text_semgent.sections.items[self.got_section_index.?];
1949 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1950 const sect = &text_segment.sections.items[self.got_section_index.?];
17891951 const off = sect.offset + @sizeOf(u64) * index;
17901952 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
17921959 var code: [8]u8 = undefined;
17931960 switch (self.base.options.target.cpu.arch) {
17941961 .x86_64 => {
......@@ -1812,75 +1979,114 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
18121979 },
18131980 else => unreachable, // unsupported target architecture
18141981 }
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 });
18161983 try self.base.file.?.pwriteAll(&code, off);
18171984}
18181985
1819fn writeSymbolTable(self: *MachO) !void {
1820 // TODO workout how we can cache these so that we only overwrite symbols that were updated
1986fn relocateSymbolTable(self: *MachO) !void {
18211987 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.?;
1824 const locals_size = self.local_symbols.items.len * @sizeOf(macho.nlist_64);
1825 log.debug("writing local symbols from 0x{x} to 0x{x}\n", .{ locals_off, locals_size + locals_off });
1826 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.local_symbols.items), locals_off);
2008 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);
2009 if (amt != existing_size) return error.InputOutput;
2010 symtab.symoff = @intCast(u32, new_symoff);
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
18282040 const globals_off = locals_off + locals_size;
1829 const globals_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64);
1830 log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ globals_off, globals_size + globals_off });
2041 const globals_size = nglobals * @sizeOf(macho.nlist_64);
2042 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
18312043 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off);
18322044
18332045 const undefs_off = globals_off + globals_size;
1834 const undefs_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64);
1835 log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ undefs_off, undefs_size + undefs_off });
2046 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
2047 log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
18362048 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
18462050 // Update dynamic symbol table.
18472051 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
1848 dysymtab.nlocalsym = nlocals;
1849 dysymtab.iextdefsym = nlocals;
1850 dysymtab.nextdefsym = nglobals;
1851 dysymtab.iundefsym = nlocals + nglobals;
1852 dysymtab.nundefsym = nundefs;
1853
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;
2052 dysymtab.nlocalsym = @intCast(u32, nlocals);
2053 dysymtab.iextdefsym = @intCast(u32, nlocals);
2054 dysymtab.nextdefsym = @intCast(u32, nglobals);
2055 dysymtab.iundefsym = @intCast(u32, nlocals + nglobals);
2056 dysymtab.nundefsym = @intCast(u32, nundefs);
2057 self.load_commands_dirty = true;
18612058}
18622059
18632060fn 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;
18642065 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
1865 const fileoff = self.linkedit_segment_next_offset.?;
1866 const datasize = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff);
1867 code_sig_cmd.dataoff = fileoff;
1868 code_sig_cmd.datasize = datasize;
1869
1870 self.linkedit_segment_next_offset = fileoff + datasize;
1871 // Advance size of __LINKEDIT segment
1872 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1873 linkedit.inner.filesize += datasize;
1874 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
1875 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
1876 }
1877 log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize });
1878 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1879 // except for code signature data.
1880 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + datasize - 1);
2066 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
2067 const needed_size = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff);
2068
2069 if (code_sig_cmd.datasize < needed_size) {
2070 code_sig_cmd.dataoff = @intCast(u32, fileoff);
2071 code_sig_cmd.datasize = needed_size;
2072
2073 // Advance size of __LINKEDIT segment
2074 linkedit_segment.inner.filesize += needed_size;
2075 if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) {
2076 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size);
2077 }
2078 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
2079 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
2080 // except for code signature data.
2081 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
2082 self.load_commands_dirty = true;
2083 }
18812084}
18822085
18832086fn writeCodeSignature(self: *MachO) !void {
2087 const tracy = trace(@src());
2088 defer tracy.end();
2089
18842090 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
18852091 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
18862092
......@@ -1898,14 +2104,18 @@ fn writeCodeSignature(self: *MachO) !void {
18982104 defer self.base.allocator.free(buffer);
18992105 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
19032109 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
19042110}
19052111
19062112fn writeExportTrie(self: *MachO) !void {
2113 if (!self.export_info_dirty) return;
19072114 if (self.global_symbols.items.len == 0) return;
19082115
2116 const tracy = trace(@src());
2117 defer tracy.end();
2118
19092119 var trie = Trie.init(self.base.allocator);
19102120 defer trie.deinit();
19112121
......@@ -1928,118 +2138,156 @@ fn writeExportTrie(self: *MachO) !void {
19282138 const nwritten = try trie.write(stream.writer());
19292139 assert(nwritten == trie.size);
19302140
2141 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
19312142 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)));
1933 dyld_info.export_off = self.linkedit_segment_next_offset.?;
1934 dyld_info.export_size = export_size;
2143 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.export_off);
2144 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
19352145
1936 log.debug("writing export trie from 0x{x} to 0x{x}\n", .{ dyld_info.export_off, dyld_info.export_off + export_size });
1937
1938 if (export_size > buffer.len) {
1939 // Pad out to align(8).
1940 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.export_off + export_size);
2146 if (needed_size > allocated_size) {
2147 dyld_info.export_off = 0;
2148 dyld_info.export_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
19412149 }
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;
1945 // Advance size of __LINKEDIT segment
1946 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
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;
2153 try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);
2154 self.load_commands_dirty = true;
2155 self.export_info_dirty = false;
19522156}
19532157
19542158fn 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();
19562165 var buffer = try self.base.allocator.alloc(u8, size);
19572166 defer self.base.allocator.free(buffer);
19582167
19592168 var stream = std.io.fixedBufferStream(buffer);
19602169 try self.binding_info_table.write(stream.writer());
19612170
2171 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
19622172 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)));
1964 dyld_info.bind_off = self.linkedit_segment_next_offset.?;
1965 dyld_info.bind_size = bind_size;
2173 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.bind_off);
2174 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
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 });
1968
1969 if (bind_size > buffer.len) {
1970 // Pad out to align(8).
1971 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.bind_off + bind_size);
2176 if (needed_size > allocated_size) {
2177 dyld_info.bind_off = 0;
2178 dyld_info.bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
19722179 }
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;
1976 // Advance size of __LINKEDIT segment
1977 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1978 linkedit.inner.filesize += dyld_info.bind_size;
1979 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
1980 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
1981 }
1982 self.cmd_table_dirty = true;
2181 dyld_info.bind_size = @intCast(u32, needed_size);
2182 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
2183
2184 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
2185 self.load_commands_dirty = true;
2186 self.binding_info_dirty = false;
19832187}
19842188
19852189fn 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();
19872193 var buffer = try self.base.allocator.alloc(u8, size);
19882194 defer self.base.allocator.free(buffer);
19892195
19902196 var stream = std.io.fixedBufferStream(buffer);
19912197 try self.lazy_binding_info_table.write(stream.writer());
19922198
2199 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
19932200 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)));
1995 dyld_info.lazy_bind_off = self.linkedit_segment_next_offset.?;
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 });
2201 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.lazy_bind_off);
2202 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
19992203
2000 if (bind_size > buffer.len) {
2001 // Pad out to align(8).
2002 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.lazy_bind_off + bind_size);
2204 if (needed_size > allocated_size) {
2205 dyld_info.lazy_bind_off = 0;
2206 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
20032207 }
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;
2007 // Advance size of __LINKEDIT segment
2008 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2009 linkedit.inner.filesize += dyld_info.lazy_bind_size;
2010 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
2011 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
2012 }
2013 self.cmd_table_dirty = true;
2209 dyld_info.lazy_bind_size = @intCast(u32, needed_size);
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 });
2211
2212 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2213 self.load_commands_dirty = true;
2214 self.lazy_binding_info_dirty = false;
20142215}
20152216
20162217fn writeStringTable(self: *MachO) !void {
2017 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2018 const needed_size = self.string_table.items.len;
2218 if (!self.string_table_dirty) return;
20192219
2020 symtab.stroff = self.linkedit_segment_next_offset.?;
2021 symtab.strsize = @intCast(u32, mem.alignForward(needed_size, @sizeOf(u64)));
2220 const tracy = trace(@src());
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) {
2026 // Pad out to align(8);
2027 try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize);
2228 if (needed_size > allocated_size) {
2229 symtab.strsize = 0;
2230 symtab.stroff = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
20282231 }
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
20292235 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;
2032 // Advance size of __LINKEDIT segment
2033 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2034 linkedit.inner.filesize += symtab.strsize;
2035 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
2036 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
2037 }
2038 self.cmd_table_dirty = true;
2240fn updateLinkeditSegmentSizes(self: *MachO) !void {
2241 if (!self.load_commands_dirty) return;
2242
2243 const tracy = trace(@src());
2244 defer tracy.end();
2245
2246 // Now, we are in position to update __LINKEDIT segment sizes.
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;
20392285}
20402286
20412287/// Writes all load commands and section headers.
20422288fn writeLoadCommands(self: *MachO) !void {
2289 if (!self.load_commands_dirty) return;
2290
20432291 var sizeofcmds: usize = 0;
20442292 for (self.load_commands.items) |lc| {
20452293 sizeofcmds += lc.cmdsize();
......@@ -2052,26 +2300,25 @@ fn writeLoadCommands(self: *MachO) !void {
20522300 try lc.write(writer);
20532301 }
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;
20562307}
20572308
20582309/// Writes Mach-O file header.
20592310fn writeHeader(self: *MachO) !void {
2311 if (!self.header_dirty) return;
2312
20602313 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
20612314 var sizeofcmds: u32 = 0;
20622315 for (self.load_commands.items) |cmd| {
20632316 sizeofcmds += cmd.cmdsize();
20642317 }
20652318 self.header.?.sizeofcmds = sizeofcmds;
2066 log.debug("writing Mach-O header {}\n", .{self.header.?});
2067 const slice = [1]macho.mach_header_64{self.header.?};
2068 try self.base.file.?.pwriteAll(mem.sliceAsBytes(slice[0..1]), 0);
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);
2319 log.debug("writing Mach-O header {}", .{self.header.?});
2320 try self.base.file.?.pwriteAll(mem.asBytes(&self.header.?), 0);
2321 self.header_dirty = false;
20752322}
20762323
20772324/// Parse MachO contents from existing binary file.
......@@ -2088,18 +2335,18 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
20882335 switch (cmd.cmd()) {
20892336 macho.LC_SEGMENT_64 => {
20902337 const x = cmd.Segment;
2091 if (parseAndCmpName(x.inner.segname[0..], "__PAGEZERO")) {
2338 if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) {
20922339 self.pagezero_segment_cmd_index = i;
2093 } else if (parseAndCmpName(x.inner.segname[0..], "__LINKEDIT")) {
2340 } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) {
20942341 self.linkedit_segment_cmd_index = i;
2095 } else if (parseAndCmpName(x.inner.segname[0..], "__TEXT")) {
2342 } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) {
20962343 self.text_segment_cmd_index = i;
20972344 for (x.sections.items) |sect, j| {
2098 if (parseAndCmpName(sect.sectname[0..], "__text")) {
2345 if (parseAndCmpName(&sect.sectname, "__text")) {
20992346 self.text_section_index = @intCast(u16, j);
21002347 }
21012348 }
2102 } else if (parseAndCmpName(x.inner.segname[0..], "__DATA")) {
2349 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
21032350 self.data_segment_cmd_index = i;
21042351 }
21052352 },
......@@ -2140,7 +2387,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
21402387 self.code_signature_cmd_index = i;
21412388 },
21422389 else => {
2143 std.log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
2390 log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
21442391 },
21452392 }
21462393 self.load_commands.appendAssumeCapacity(cmd);
......@@ -2149,7 +2396,7 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
21492396}
21502397
21512398fn 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;
21532400 return mem.eql(u8, name[0..len], needle);
21542401}
21552402
src/link/MachO/CodeSignature.zig+3-3
......@@ -126,7 +126,7 @@ pub fn calcAdhocSignature(
126126
127127 Sha256.hash(buffer[0..fsize], &hash, .{});
128128
129 cdir.data.appendSliceAssumeCapacity(hash[0..]);
129 cdir.data.appendSliceAssumeCapacity(&hash);
130130 cdir.inner.nCodeSlots += 1;
131131 }
132132
......@@ -174,10 +174,10 @@ test "CodeSignature header" {
174174 defer code_sig.deinit();
175175
176176 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;
177 code_sig.writeHeader(buffer[0..]);
177 code_sig.writeHeader(&buffer);
178178
179179 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));
181181}
182182
183183pub 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" {
531531 {
532532 const nwritten = try trie.write(stream.writer());
533533 testing.expect(nwritten == trie.size);
534 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
534 testing.expect(mem.eql(u8, buffer, &exp_buffer));
535535 }
536536 {
537537 // Writing finalized trie again should yield the same result.
538538 try stream.seekTo(0);
539539 const nwritten = try trie.write(stream.writer());
540540 testing.expect(nwritten == trie.size);
541 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
541 testing.expect(mem.eql(u8, buffer, &exp_buffer));
542542 }
543543}
544544
......@@ -556,7 +556,7 @@ test "parse Trie from byte stream" {
556556 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
557557 };
558558
559 var in_stream = std.io.fixedBufferStream(in_buffer[0..]);
559 var in_stream = std.io.fixedBufferStream(&in_buffer);
560560 var trie = Trie.init(gpa);
561561 defer trie.deinit();
562562 const nread = try trie.read(in_stream.reader());
......@@ -571,5 +571,5 @@ test "parse Trie from byte stream" {
571571 const nwritten = try trie.write(out_stream.writer());
572572
573573 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));
575575}
src/link/MachO/commands.zig+19-13
......@@ -7,7 +7,7 @@ const macho = std.macho;
77const testing = std.testing;
88
99const Allocator = std.mem.Allocator;
10const makeName = @import("../MachO.zig").makeStaticString;
10const makeStaticString = @import("../MachO.zig").makeStaticString;
1111
1212pub const LoadCommand = union(enum) {
1313 Segment: SegmentCommand,
......@@ -26,9 +26,9 @@ pub const LoadCommand = union(enum) {
2626 const header = try reader.readStruct(macho.load_command);
2727 var buffer = try allocator.alloc(u8, header.cmdsize);
2828 defer allocator.free(buffer);
29 mem.copy(u8, buffer[0..], mem.asBytes(&header));
29 mem.copy(u8, buffer, mem.asBytes(&header));
3030 try reader.readNoEof(buffer[@sizeOf(macho.load_command)..]);
31 var stream = io.fixedBufferStream(buffer[0..]);
31 var stream = io.fixedBufferStream(buffer);
3232
3333 return switch (header.cmd) {
3434 macho.LC_SEGMENT_64 => LoadCommand{
......@@ -155,6 +155,12 @@ pub const SegmentCommand = struct {
155155 return .{ .inner = inner };
156156 }
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
158164 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
159165 const inner = try reader.readStruct(macho.segment_command_64);
160166 var segment = SegmentCommand{
......@@ -210,7 +216,7 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
210216 const inner = try reader.readStruct(Cmd);
211217 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
212218 errdefer allocator.free(data);
213 try reader.readNoEof(data[0..]);
219 try reader.readNoEof(data);
214220 return Self{
215221 .inner = inner,
216222 .data = data,
......@@ -277,7 +283,7 @@ test "read-write segment command" {
277283 .inner = .{
278284 .cmd = macho.LC_SEGMENT_64,
279285 .cmdsize = 152,
280 .segname = makeName("__TEXT"),
286 .segname = makeStaticString("__TEXT"),
281287 .vmaddr = 4294967296,
282288 .vmsize = 294912,
283289 .fileoff = 0,
......@@ -289,8 +295,8 @@ test "read-write segment command" {
289295 },
290296 };
291297 try cmd.sections.append(gpa, .{
292 .sectname = makeName("__text"),
293 .segname = makeName("__TEXT"),
298 .sectname = makeStaticString("__text"),
299 .segname = makeStaticString("__TEXT"),
294300 .addr = 4294983680,
295301 .size = 448,
296302 .offset = 16384,
......@@ -303,10 +309,10 @@ test "read-write segment command" {
303309 .reserved3 = 0,
304310 });
305311 defer cmd.deinit(gpa);
306 try testRead(gpa, in_buffer[0..], LoadCommand{ .Segment = cmd });
312 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
307313
308314 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);
310316}
311317
312318test "read-write generic command with data" {
......@@ -342,10 +348,10 @@ test "read-write generic command with data" {
342348 cmd.data[5] = 0x0;
343349 cmd.data[6] = 0x0;
344350 cmd.data[7] = 0x0;
345 try testRead(gpa, in_buffer[0..], LoadCommand{ .Dylib = cmd });
351 try testRead(gpa, in_buffer, LoadCommand{ .Dylib = cmd });
346352
347353 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);
349355}
350356
351357test "read-write C struct command" {
......@@ -362,8 +368,8 @@ test "read-write C struct command" {
362368 .entryoff = 16644,
363369 .stacksize = 0,
364370 };
365 try testRead(gpa, in_buffer[0..], LoadCommand{ .Main = cmd });
371 try testRead(gpa, in_buffer, LoadCommand{ .Main = cmd });
366372
367373 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);
369375}