authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-13 10:05:21+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-18 10:00:04+02:00
log79ab46ec918edc5d31c87a2535a30b8d2207228c
tree6018a8aacd9999384e9d00a5dc6c5dd2ed721f65
parent53bd7bd044fccc70699b04d9ae37151423f70165

macho: start separating linking contexts


2 files changed, 714 insertions(+), 325 deletions(-)

src/link/MachO.zig+48-318
...@@ -269,7 +269,7 @@ pub const SymbolWithLoc = struct {...@@ -269,7 +269,7 @@ pub const SymbolWithLoc = struct {
269269
270/// When allocating, the ideal_capacity is calculated by270/// When allocating, the ideal_capacity is calculated by
271/// actual_capacity + (actual_capacity / ideal_factor)271/// actual_capacity + (actual_capacity / ideal_factor)
272const ideal_factor = 4;272const ideal_factor = 3;
273273
274/// Default path to dyld274/// Default path to dyld
275const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";275const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
...@@ -4322,7 +4322,7 @@ pub fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u...@@ -4322,7 +4322,7 @@ pub fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u
4322 return .{ .vmaddr = 0, .fileoff = 0 };4322 return .{ .vmaddr = 0, .fileoff = 0 };
4323}4323}
43244324
4325pub fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {4325fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4326 for (self.segments.items) |seg, i| {4326 for (self.segments.items) |seg, i| {
4327 const indexes = self.getSectionIndexes(@intCast(u8, i));4327 const indexes = self.getSectionIndexes(@intCast(u8, i));
4328 var out_seg = seg;4328 var out_seg = seg;
...@@ -4351,20 +4351,18 @@ pub fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {...@@ -4351,20 +4351,18 @@ pub fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4351 }4351 }
4352}4352}
43534353
4354pub fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4354fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4355 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4355 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4356 seg.filesize = 0;4356 seg.filesize = 0;
4357 seg.vmsize = 0;4357 seg.vmsize = 0;
43584358
4359 try self.writeDyldInfoData(ncmds, lc_writer);4359 try self.writeDyldInfoData(ncmds, lc_writer);
4360 try self.writeFunctionStarts(ncmds, lc_writer);
4361 try self.writeDataInCode(ncmds, lc_writer);
4362 try self.writeSymtabs(ncmds, lc_writer);4360 try self.writeSymtabs(ncmds, lc_writer);
43634361
4364 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);4362 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4365}4363}
43664364
4367pub fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4365fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4368 const tracy = trace(@src());4366 const tracy = trace(@src());
4369 defer tracy.end();4367 defer tracy.end();
43704368
...@@ -4680,155 +4678,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4680,155 +4678,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4680 }4678 }
4681}4679}
46824680
4683const asc_u64 = std.sort.asc(u64);4681fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4684
4685pub fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4686 const tracy = trace(@src());
4687 defer tracy.end();
4688
4689 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
4690 const text_sect_index = macho_file.text_section_index orelse return;
4691 const text_seg = macho_file.segments.items[text_seg_index];
4692
4693 const gpa = macho_file.base.allocator;
4694
4695 // We need to sort by address first
4696 var addresses = std.ArrayList(u64).init(gpa);
4697 defer addresses.deinit();
4698 try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len);
4699
4700 for (macho_file.globals.items) |global| {
4701 const sym = macho_file.getSymbol(global);
4702 if (sym.undf()) continue;
4703 if (sym.n_desc == MachO.N_DESC_GCED) continue;
4704 const sect_id = sym.n_sect - 1;
4705 if (sect_id != text_sect_index) continue;
4706
4707 addresses.appendAssumeCapacity(sym.n_value);
4708 }
4709
4710 std.sort.sort(u64, addresses.items, {}, asc_u64);
4711
4712 var offsets = std.ArrayList(u32).init(gpa);
4713 defer offsets.deinit();
4714 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
4715
4716 var last_off: u32 = 0;
4717 for (addresses.items) |addr| {
4718 const offset = @intCast(u32, addr - text_seg.vmaddr);
4719 const diff = offset - last_off;
4720
4721 if (diff == 0) continue;
4722
4723 offsets.appendAssumeCapacity(diff);
4724 last_off = offset;
4725 }
4726
4727 var buffer = std.ArrayList(u8).init(gpa);
4728 defer buffer.deinit();
4729
4730 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
4731 try buffer.ensureTotalCapacity(max_size);
4732
4733 for (offsets.items) |offset| {
4734 try std.leb.writeULEB128(buffer.writer(), offset);
4735 }
4736
4737 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
4738 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
4739 const needed_size = buffer.items.len;
4740 link_seg.filesize = offset + needed_size - link_seg.fileoff;
4741
4742 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4743
4744 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
4745
4746 try lc_writer.writeStruct(macho.linkedit_data_command{
4747 .cmd = .FUNCTION_STARTS,
4748 .cmdsize = @sizeOf(macho.linkedit_data_command),
4749 .dataoff = @intCast(u32, offset),
4750 .datasize = @intCast(u32, needed_size),
4751 });
4752 ncmds.* += 1;
4753}
4754
4755fn filterDataInCode(
4756 dices: []align(1) const macho.data_in_code_entry,
4757 start_addr: u64,
4758 end_addr: u64,
4759) []align(1) const macho.data_in_code_entry {
4760 const Predicate = struct {
4761 addr: u64,
4762
4763 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
4764 return dice.offset >= macho_file.addr;
4765 }
4766 };
4767
4768 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
4769 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
4770
4771 return dices[start..end];
4772}
4773
4774pub fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4775 const tracy = trace(@src());
4776 defer tracy.end();
4777
4778 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
4779 defer out_dice.deinit();
4780
4781 const text_sect_id = macho_file.text_section_index orelse return;
4782 const text_sect_header = macho_file.sections.items(.header)[text_sect_id];
4783
4784 for (macho_file.objects.items) |object| {
4785 const dice = object.parseDataInCode() orelse continue;
4786 try out_dice.ensureUnusedCapacity(dice.len);
4787
4788 for (object.managed_atoms.items) |atom| {
4789 const sym = atom.getSymbol(macho_file);
4790 if (sym.n_desc == MachO.N_DESC_GCED) continue;
4791
4792 const sect_id = sym.n_sect - 1;
4793 if (sect_id != macho_file.text_section_index.?) {
4794 continue;
4795 }
4796
4797 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
4798 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
4799 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
4800 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
4801 return error.Overflow;
4802
4803 for (filtered_dice) |single| {
4804 const offset = single.offset - source_addr + base;
4805 out_dice.appendAssumeCapacity(.{
4806 .offset = offset,
4807 .length = single.length,
4808 .kind = single.kind,
4809 });
4810 }
4811 }
4812 }
4813
4814 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
4815 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
4816 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
4817 seg.filesize = offset + needed_size - seg.fileoff;
4818
4819 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4820
4821 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
4822 try lc_writer.writeStruct(macho.linkedit_data_command{
4823 .cmd = .DATA_IN_CODE,
4824 .cmdsize = @sizeOf(macho.linkedit_data_command),
4825 .dataoff = @intCast(u32, offset),
4826 .datasize = @intCast(u32, needed_size),
4827 });
4828 ncmds.* += 1;
4829}
4830
4831pub fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4832 var symtab_cmd = macho.symtab_command{4682 var symtab_cmd = macho.symtab_command{
4833 .cmdsize = @sizeOf(macho.symtab_command),4683 .cmdsize = @sizeOf(macho.symtab_command),
4834 .symoff = 0,4684 .symoff = 0,
...@@ -4866,7 +4716,7 @@ pub fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4866,7 +4716,7 @@ pub fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4866 ncmds.* += 2;4716 ncmds.* += 2;
4867}4717}
48684718
4869pub fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {4719fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
4870 const gpa = self.base.allocator;4720 const gpa = self.base.allocator;
48714721
4872 var locals = std.ArrayList(macho.nlist_64).init(gpa);4722 var locals = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -4892,10 +4742,6 @@ pub fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -4892,10 +4742,6 @@ pub fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
4892 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));4742 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
4893 try locals.append(out_sym);4743 try locals.append(out_sym);
4894 }4744 }
4895
4896 if (!self.base.options.strip) {
4897 try self.generateSymbolStabs(object, &locals);
4898 }
4899 }4745 }
49004746
4901 var exports = std.ArrayList(macho.nlist_64).init(gpa);4747 var exports = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -5056,7 +4902,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi...@@ -5056,7 +4902,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
5056 lc.nindirectsyms = nindirectsyms;4902 lc.nindirectsyms = nindirectsyms;
5057}4903}
50584904
5059pub fn writeCodeSignaturePadding(4905fn writeCodeSignaturePadding(
5060 self: *MachO,4906 self: *MachO,
5061 code_sig: *CodeSignature,4907 code_sig: *CodeSignature,
5062 ncmds: *u32,4908 ncmds: *u32,
...@@ -5085,7 +4931,7 @@ pub fn writeCodeSignaturePadding(...@@ -5085,7 +4931,7 @@ pub fn writeCodeSignaturePadding(
5085 return @intCast(u32, offset);4931 return @intCast(u32, offset);
5086}4932}
50874933
5088pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {4934fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
5089 const seg = self.segments.items[self.text_segment_cmd_index.?];4935 const seg = self.segments.items[self.text_segment_cmd_index.?];
50904936
5091 var buffer = std.ArrayList(u8).init(self.base.allocator);4937 var buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -5109,7 +4955,7 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !...@@ -5109,7 +4955,7 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !
5109}4955}
51104956
5111/// Writes Mach-O file header.4957/// Writes Mach-O file header.
5112pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {4958fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
5113 var header: macho.mach_header_64 = .{};4959 var header: macho.mach_header_64 = .{};
5114 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;4960 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
51154961
...@@ -5157,6 +5003,45 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -5157,6 +5003,45 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
5157 std.math.maxInt(@TypeOf(actual_size));5003 std.math.maxInt(@TypeOf(actual_size));
5158}5004}
51595005
5006fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
5007 // TODO: header and load commands have to be part of the __TEXT segment
5008 const header_size = default_headerpad_size;
5009 if (start < header_size)
5010 return header_size;
5011
5012 const end = start + padToIdeal(size);
5013
5014 for (self.sections.items(.header)) |header| {
5015 const tight_size = header.size;
5016 const increased_size = padToIdeal(tight_size);
5017 const test_end = header.offset + increased_size;
5018 if (end > header.offset and start < test_end) {
5019 return test_end;
5020 }
5021 }
5022
5023 return null;
5024}
5025
5026// fn allocatedSize(self: *MachO, start: u64) u64 {
5027// if (start == 0)
5028// return 0;
5029// var min_pos: u64 = std.math.maxInt(u64);
5030// for (self.sections.items(.header)) |header| {
5031// if (header.offset <= start) continue;
5032// if (header.offset < min_pos) min_pos = header.offset;
5033// }
5034// return min_pos - start;
5035// }
5036
5037fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
5038 var start: u64 = 0;
5039 while (self.detectAllocCollision(start, object_size)) |item_end| {
5040 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
5041 }
5042 return start;
5043}
5044
5160pub fn makeStaticString(bytes: []const u8) [16]u8 {5045pub fn makeStaticString(bytes: []const u8) [16]u8 {
5161 var buf = [_]u8{0} ** 16;5046 var buf = [_]u8{0} ** 16;
5162 assert(bytes.len <= buf.len);5047 assert(bytes.len <= buf.len);
...@@ -5321,161 +5206,6 @@ pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, p...@@ -5321,161 +5206,6 @@ pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, p
5321 return i;5206 return i;
5322}5207}
53235208
5324pub fn generateSymbolStabs(
5325 self: *MachO,
5326 object: Object,
5327 locals: *std.ArrayList(macho.nlist_64),
5328) !void {
5329 assert(!self.base.options.strip);
5330
5331 log.debug("parsing debug info in '{s}'", .{object.name});
5332
5333 const gpa = self.base.allocator;
5334 var debug_info = try object.parseDwarfInfo();
5335 defer debug_info.deinit(gpa);
5336 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
5337
5338 // We assume there is only one CU.
5339 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
5340 error.MissingDebugInfo => {
5341 // TODO audit cases with missing debug info and audit our dwarf.zig module.
5342 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
5343 return;
5344 },
5345 else => |e| return e,
5346 };
5347
5348 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*);
5349 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*);
5350
5351 // Open scope
5352 try locals.ensureUnusedCapacity(3);
5353 locals.appendAssumeCapacity(.{
5354 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
5355 .n_type = macho.N_SO,
5356 .n_sect = 0,
5357 .n_desc = 0,
5358 .n_value = 0,
5359 });
5360 locals.appendAssumeCapacity(.{
5361 .n_strx = try self.strtab.insert(gpa, tu_name),
5362 .n_type = macho.N_SO,
5363 .n_sect = 0,
5364 .n_desc = 0,
5365 .n_value = 0,
5366 });
5367 locals.appendAssumeCapacity(.{
5368 .n_strx = try self.strtab.insert(gpa, object.name),
5369 .n_type = macho.N_OSO,
5370 .n_sect = 0,
5371 .n_desc = 1,
5372 .n_value = object.mtime,
5373 });
5374
5375 var stabs_buf: [4]macho.nlist_64 = undefined;
5376
5377 for (object.managed_atoms.items) |atom| {
5378 const stabs = try self.generateSymbolStabsForSymbol(
5379 atom.getSymbolWithLoc(),
5380 debug_info,
5381 &stabs_buf,
5382 );
5383 try locals.appendSlice(stabs);
5384
5385 for (atom.contained.items) |sym_at_off| {
5386 const sym_loc = SymbolWithLoc{
5387 .sym_index = sym_at_off.sym_index,
5388 .file = atom.file,
5389 };
5390 const contained_stabs = try self.generateSymbolStabsForSymbol(
5391 sym_loc,
5392 debug_info,
5393 &stabs_buf,
5394 );
5395 try locals.appendSlice(contained_stabs);
5396 }
5397 }
5398
5399 // Close scope
5400 try locals.append(.{
5401 .n_strx = 0,
5402 .n_type = macho.N_SO,
5403 .n_sect = 0,
5404 .n_desc = 0,
5405 .n_value = 0,
5406 });
5407}
5408
5409fn generateSymbolStabsForSymbol(
5410 self: *MachO,
5411 sym_loc: SymbolWithLoc,
5412 debug_info: dwarf.DwarfInfo,
5413 buf: *[4]macho.nlist_64,
5414) ![]const macho.nlist_64 {
5415 const gpa = self.base.allocator;
5416 const object = self.objects.items[sym_loc.file.?];
5417 const sym = self.getSymbol(sym_loc);
5418 const sym_name = self.getSymbolName(sym_loc);
5419
5420 if (sym.n_strx == 0) return buf[0..0];
5421 if (sym.n_desc == N_DESC_GCED) return buf[0..0];
5422 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
5423
5424 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
5425 const size: ?u64 = size: {
5426 if (source_sym.tentative()) break :size null;
5427 for (debug_info.func_list.items) |func| {
5428 if (func.pc_range) |range| {
5429 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
5430 break :size range.end - range.start;
5431 }
5432 }
5433 }
5434 break :size null;
5435 };
5436
5437 if (size) |ss| {
5438 buf[0] = .{
5439 .n_strx = 0,
5440 .n_type = macho.N_BNSYM,
5441 .n_sect = sym.n_sect,
5442 .n_desc = 0,
5443 .n_value = sym.n_value,
5444 };
5445 buf[1] = .{
5446 .n_strx = try self.strtab.insert(gpa, sym_name),
5447 .n_type = macho.N_FUN,
5448 .n_sect = sym.n_sect,
5449 .n_desc = 0,
5450 .n_value = sym.n_value,
5451 };
5452 buf[2] = .{
5453 .n_strx = 0,
5454 .n_type = macho.N_FUN,
5455 .n_sect = 0,
5456 .n_desc = 0,
5457 .n_value = ss,
5458 };
5459 buf[3] = .{
5460 .n_strx = 0,
5461 .n_type = macho.N_ENSYM,
5462 .n_sect = sym.n_sect,
5463 .n_desc = 0,
5464 .n_value = ss,
5465 };
5466 return buf;
5467 } else {
5468 buf[0] = .{
5469 .n_strx = try self.strtab.insert(gpa, sym_name),
5470 .n_type = macho.N_STSYM,
5471 .n_sect = sym.n_sect,
5472 .n_desc = 0,
5473 .n_value = sym.n_value,
5474 };
5475 return buf[0..1];
5476 }
5477}
5478
5479// fn snapshotState(self: *MachO) !void {5209// fn snapshotState(self: *MachO) !void {
5480// const emit = self.base.options.emit orelse {5210// const emit = self.base.options.emit orelse {
5481// log.debug("no emit directory found; skipping snapshot...", .{});5211// log.debug("no emit directory found; skipping snapshot...", .{});
src/link/MachO/zld.zig+666-7
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const build_options = @import("build_options");2const build_options = @import("build_options");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const dwarf = std.dwarf;
4const fs = std.fs;5const fs = std.fs;
5const log = std.log.scoped(.link);6const log = std.log.scoped(.link);
6const macho = std.macho;7const macho = std.macho;
...@@ -18,6 +19,7 @@ const CodeSignature = @import("CodeSignature.zig");...@@ -18,6 +19,7 @@ const CodeSignature = @import("CodeSignature.zig");
18const Compilation = @import("../../Compilation.zig");19const Compilation = @import("../../Compilation.zig");
19const Dylib = @import("Dylib.zig");20const Dylib = @import("Dylib.zig");
20const MachO = @import("../MachO.zig");21const MachO = @import("../MachO.zig");
22const Object = @import("Object.zig");
21const SymbolWithLoc = MachO.SymbolWithLoc;23const SymbolWithLoc = MachO.SymbolWithLoc;
22const Trie = @import("Trie.zig");24const Trie = @import("Trie.zig");
2325
...@@ -618,20 +620,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -618,20 +620,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
618 if (macho_file.base.options.entitlements) |path| {620 if (macho_file.base.options.entitlements) |path| {
619 try codesig.addEntitlements(arena, path);621 try codesig.addEntitlements(arena, path);
620 }622 }
621 codesig_offset = try macho_file.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);623 codesig_offset = try writeCodeSignaturePadding(macho_file, &codesig, &ncmds, lc_writer);
622 break :blk codesig;624 break :blk codesig;
623 } else null;625 } else null;
624626
625 var headers_buf = std.ArrayList(u8).init(arena);627 var headers_buf = std.ArrayList(u8).init(arena);
626 try macho_file.writeSegmentHeaders(&ncmds, headers_buf.writer());628 try writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
627629
628 try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));630 try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
629 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);631 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
630632
631 try macho_file.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));633 try writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
632634
633 if (codesig) |*csig| {635 if (codesig) |*csig| {
634 try macho_file.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last636 try writeCodeSignature(macho_file, csig, codesig_offset.?); // code signing always comes last
635 }637 }
636 }638 }
637639
...@@ -964,9 +966,9 @@ fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype)...@@ -964,9 +966,9 @@ fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype)
964 seg.vmsize = 0;966 seg.vmsize = 0;
965967
966 try writeDyldInfoData(macho_file, ncmds, lc_writer);968 try writeDyldInfoData(macho_file, ncmds, lc_writer);
967 try macho_file.writeFunctionStarts(ncmds, lc_writer);969 try writeFunctionStarts(macho_file, ncmds, lc_writer);
968 try macho_file.writeDataInCode(ncmds, lc_writer);970 try writeDataInCode(macho_file, ncmds, lc_writer);
969 try macho_file.writeSymtabs(ncmds, lc_writer);971 try writeSymtabs(macho_file, ncmds, lc_writer);
970972
971 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);973 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
972}974}
...@@ -1280,3 +1282,660 @@ fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !...@@ -1280,3 +1282,660 @@ fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !
1280 try macho_file.base.file.?.pwriteAll(&buf, file_offset);1282 try macho_file.base.file.?.pwriteAll(&buf, file_offset);
1281 }1283 }
1282}1284}
1285
1286const asc_u64 = std.sort.asc(u64);
1287
1288fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1289 const tracy = trace(@src());
1290 defer tracy.end();
1291
1292 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
1293 const text_sect_index = macho_file.text_section_index orelse return;
1294 const text_seg = macho_file.segments.items[text_seg_index];
1295
1296 const gpa = macho_file.base.allocator;
1297
1298 // We need to sort by address first
1299 var addresses = std.ArrayList(u64).init(gpa);
1300 defer addresses.deinit();
1301 try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len);
1302
1303 for (macho_file.globals.items) |global| {
1304 const sym = macho_file.getSymbol(global);
1305 if (sym.undf()) continue;
1306 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1307 const sect_id = sym.n_sect - 1;
1308 if (sect_id != text_sect_index) continue;
1309
1310 addresses.appendAssumeCapacity(sym.n_value);
1311 }
1312
1313 std.sort.sort(u64, addresses.items, {}, asc_u64);
1314
1315 var offsets = std.ArrayList(u32).init(gpa);
1316 defer offsets.deinit();
1317 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
1318
1319 var last_off: u32 = 0;
1320 for (addresses.items) |addr| {
1321 const offset = @intCast(u32, addr - text_seg.vmaddr);
1322 const diff = offset - last_off;
1323
1324 if (diff == 0) continue;
1325
1326 offsets.appendAssumeCapacity(diff);
1327 last_off = offset;
1328 }
1329
1330 var buffer = std.ArrayList(u8).init(gpa);
1331 defer buffer.deinit();
1332
1333 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
1334 try buffer.ensureTotalCapacity(max_size);
1335
1336 for (offsets.items) |offset| {
1337 try std.leb.writeULEB128(buffer.writer(), offset);
1338 }
1339
1340 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1341 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
1342 const needed_size = buffer.items.len;
1343 link_seg.filesize = offset + needed_size - link_seg.fileoff;
1344
1345 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1346
1347 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1348
1349 try lc_writer.writeStruct(macho.linkedit_data_command{
1350 .cmd = .FUNCTION_STARTS,
1351 .cmdsize = @sizeOf(macho.linkedit_data_command),
1352 .dataoff = @intCast(u32, offset),
1353 .datasize = @intCast(u32, needed_size),
1354 });
1355 ncmds.* += 1;
1356}
1357
1358fn filterDataInCode(
1359 dices: []align(1) const macho.data_in_code_entry,
1360 start_addr: u64,
1361 end_addr: u64,
1362) []align(1) const macho.data_in_code_entry {
1363 const Predicate = struct {
1364 addr: u64,
1365
1366 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
1367 return dice.offset >= macho_file.addr;
1368 }
1369 };
1370
1371 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
1372 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
1373
1374 return dices[start..end];
1375}
1376
1377fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1378 const tracy = trace(@src());
1379 defer tracy.end();
1380
1381 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
1382 defer out_dice.deinit();
1383
1384 const text_sect_id = macho_file.text_section_index orelse return;
1385 const text_sect_header = macho_file.sections.items(.header)[text_sect_id];
1386
1387 for (macho_file.objects.items) |object| {
1388 const dice = object.parseDataInCode() orelse continue;
1389 try out_dice.ensureUnusedCapacity(dice.len);
1390
1391 for (object.managed_atoms.items) |atom| {
1392 const sym = atom.getSymbol(macho_file);
1393 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1394
1395 const sect_id = sym.n_sect - 1;
1396 if (sect_id != macho_file.text_section_index.?) {
1397 continue;
1398 }
1399
1400 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
1401 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
1402 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
1403 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
1404 return error.Overflow;
1405
1406 for (filtered_dice) |single| {
1407 const offset = single.offset - source_addr + base;
1408 out_dice.appendAssumeCapacity(.{
1409 .offset = offset,
1410 .length = single.length,
1411 .kind = single.kind,
1412 });
1413 }
1414 }
1415 }
1416
1417 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1418 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1419 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
1420 seg.filesize = offset + needed_size - seg.fileoff;
1421
1422 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1423
1424 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
1425 try lc_writer.writeStruct(macho.linkedit_data_command{
1426 .cmd = .DATA_IN_CODE,
1427 .cmdsize = @sizeOf(macho.linkedit_data_command),
1428 .dataoff = @intCast(u32, offset),
1429 .datasize = @intCast(u32, needed_size),
1430 });
1431 ncmds.* += 1;
1432}
1433
1434fn writeSymtabs(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1435 var symtab_cmd = macho.symtab_command{
1436 .cmdsize = @sizeOf(macho.symtab_command),
1437 .symoff = 0,
1438 .nsyms = 0,
1439 .stroff = 0,
1440 .strsize = 0,
1441 };
1442 var dysymtab_cmd = macho.dysymtab_command{
1443 .cmdsize = @sizeOf(macho.dysymtab_command),
1444 .ilocalsym = 0,
1445 .nlocalsym = 0,
1446 .iextdefsym = 0,
1447 .nextdefsym = 0,
1448 .iundefsym = 0,
1449 .nundefsym = 0,
1450 .tocoff = 0,
1451 .ntoc = 0,
1452 .modtaboff = 0,
1453 .nmodtab = 0,
1454 .extrefsymoff = 0,
1455 .nextrefsyms = 0,
1456 .indirectsymoff = 0,
1457 .nindirectsyms = 0,
1458 .extreloff = 0,
1459 .nextrel = 0,
1460 .locreloff = 0,
1461 .nlocrel = 0,
1462 };
1463 var ctx = try writeSymtab(macho_file, &symtab_cmd);
1464 defer ctx.imports_table.deinit();
1465 try writeDysymtab(macho_file, ctx, &dysymtab_cmd);
1466 try writeStrtab(macho_file, &symtab_cmd);
1467 try lc_writer.writeStruct(symtab_cmd);
1468 try lc_writer.writeStruct(dysymtab_cmd);
1469 ncmds.* += 2;
1470}
1471
1472fn writeSymtab(macho_file: *MachO, lc: *macho.symtab_command) !SymtabCtx {
1473 const gpa = macho_file.base.allocator;
1474
1475 var locals = std.ArrayList(macho.nlist_64).init(gpa);
1476 defer locals.deinit();
1477
1478 for (macho_file.locals.items) |sym, sym_id| {
1479 if (sym.n_strx == 0) continue; // no name, skip
1480 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1481 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
1482 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1483 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1484 try locals.append(sym);
1485 }
1486
1487 for (macho_file.objects.items) |object, object_id| {
1488 for (object.symtab.items) |sym, sym_id| {
1489 if (sym.n_strx == 0) continue; // no name, skip
1490 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1491 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
1492 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1493 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1494 var out_sym = sym;
1495 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(sym_loc));
1496 try locals.append(out_sym);
1497 }
1498
1499 if (!macho_file.base.options.strip) {
1500 try generateSymbolStabs(macho_file, object, &locals);
1501 }
1502 }
1503
1504 var exports = std.ArrayList(macho.nlist_64).init(gpa);
1505 defer exports.deinit();
1506
1507 for (macho_file.globals.items) |global| {
1508 const sym = macho_file.getSymbol(global);
1509 if (sym.undf()) continue; // import, skip
1510 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1511 var out_sym = sym;
1512 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1513 try exports.append(out_sym);
1514 }
1515
1516 var imports = std.ArrayList(macho.nlist_64).init(gpa);
1517 defer imports.deinit();
1518
1519 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
1520
1521 for (macho_file.globals.items) |global| {
1522 const sym = macho_file.getSymbol(global);
1523 if (sym.n_strx == 0) continue; // no name, skip
1524 if (!sym.undf()) continue; // not an import, skip
1525 const new_index = @intCast(u32, imports.items.len);
1526 var out_sym = sym;
1527 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1528 try imports.append(out_sym);
1529 try imports_table.putNoClobber(global, new_index);
1530 }
1531
1532 const nlocals = @intCast(u32, locals.items.len);
1533 const nexports = @intCast(u32, exports.items.len);
1534 const nimports = @intCast(u32, imports.items.len);
1535 const nsyms = nlocals + nexports + nimports;
1536
1537 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1538 const offset = mem.alignForwardGeneric(
1539 u64,
1540 seg.fileoff + seg.filesize,
1541 @alignOf(macho.nlist_64),
1542 );
1543 const needed_size = nsyms * @sizeOf(macho.nlist_64);
1544 seg.filesize = offset + needed_size - seg.fileoff;
1545
1546 var buffer = std.ArrayList(u8).init(gpa);
1547 defer buffer.deinit();
1548 try buffer.ensureTotalCapacityPrecise(needed_size);
1549 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
1550 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
1551 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
1552
1553 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1554 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1555
1556 lc.symoff = @intCast(u32, offset);
1557 lc.nsyms = nsyms;
1558
1559 return SymtabCtx{
1560 .nlocalsym = nlocals,
1561 .nextdefsym = nexports,
1562 .nundefsym = nimports,
1563 .imports_table = imports_table,
1564 };
1565}
1566
1567fn writeStrtab(macho_file: *MachO, lc: *macho.symtab_command) !void {
1568 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1569 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1570 const needed_size = macho_file.strtab.buffer.items.len;
1571 seg.filesize = offset + needed_size - seg.fileoff;
1572
1573 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1574
1575 try macho_file.base.file.?.pwriteAll(macho_file.strtab.buffer.items, offset);
1576
1577 lc.stroff = @intCast(u32, offset);
1578 lc.strsize = @intCast(u32, needed_size);
1579}
1580
1581pub fn generateSymbolStabs(
1582 macho_file: *MachO,
1583 object: Object,
1584 locals: *std.ArrayList(macho.nlist_64),
1585) !void {
1586 assert(!macho_file.base.options.strip);
1587
1588 log.debug("parsing debug info in '{s}'", .{object.name});
1589
1590 const gpa = macho_file.base.allocator;
1591 var debug_info = try object.parseDwarfInfo();
1592 defer debug_info.deinit(gpa);
1593 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
1594
1595 // We assume there is only one CU.
1596 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
1597 error.MissingDebugInfo => {
1598 // TODO audit cases with missing debug info and audit our dwarf.zig module.
1599 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
1600 return;
1601 },
1602 else => |e| return e,
1603 };
1604
1605 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*);
1606 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*);
1607
1608 // Open scope
1609 try locals.ensureUnusedCapacity(3);
1610 locals.appendAssumeCapacity(.{
1611 .n_strx = try macho_file.strtab.insert(gpa, tu_comp_dir),
1612 .n_type = macho.N_SO,
1613 .n_sect = 0,
1614 .n_desc = 0,
1615 .n_value = 0,
1616 });
1617 locals.appendAssumeCapacity(.{
1618 .n_strx = try macho_file.strtab.insert(gpa, tu_name),
1619 .n_type = macho.N_SO,
1620 .n_sect = 0,
1621 .n_desc = 0,
1622 .n_value = 0,
1623 });
1624 locals.appendAssumeCapacity(.{
1625 .n_strx = try macho_file.strtab.insert(gpa, object.name),
1626 .n_type = macho.N_OSO,
1627 .n_sect = 0,
1628 .n_desc = 1,
1629 .n_value = object.mtime,
1630 });
1631
1632 var stabs_buf: [4]macho.nlist_64 = undefined;
1633
1634 for (object.managed_atoms.items) |atom| {
1635 const stabs = try generateSymbolStabsForSymbol(
1636 macho_file,
1637 atom.getSymbolWithLoc(),
1638 debug_info,
1639 &stabs_buf,
1640 );
1641 try locals.appendSlice(stabs);
1642
1643 for (atom.contained.items) |sym_at_off| {
1644 const sym_loc = SymbolWithLoc{
1645 .sym_index = sym_at_off.sym_index,
1646 .file = atom.file,
1647 };
1648 const contained_stabs = try generateSymbolStabsForSymbol(
1649 macho_file,
1650 sym_loc,
1651 debug_info,
1652 &stabs_buf,
1653 );
1654 try locals.appendSlice(contained_stabs);
1655 }
1656 }
1657
1658 // Close scope
1659 try locals.append(.{
1660 .n_strx = 0,
1661 .n_type = macho.N_SO,
1662 .n_sect = 0,
1663 .n_desc = 0,
1664 .n_value = 0,
1665 });
1666}
1667
1668fn generateSymbolStabsForSymbol(
1669 macho_file: *MachO,
1670 sym_loc: SymbolWithLoc,
1671 debug_info: dwarf.DwarfInfo,
1672 buf: *[4]macho.nlist_64,
1673) ![]const macho.nlist_64 {
1674 const gpa = macho_file.base.allocator;
1675 const object = macho_file.objects.items[sym_loc.file.?];
1676 const sym = macho_file.getSymbol(sym_loc);
1677 const sym_name = macho_file.getSymbolName(sym_loc);
1678
1679 if (sym.n_strx == 0) return buf[0..0];
1680 if (sym.n_desc == MachO.N_DESC_GCED) return buf[0..0];
1681 if (macho_file.symbolIsTemp(sym_loc)) return buf[0..0];
1682
1683 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
1684 const size: ?u64 = size: {
1685 if (source_sym.tentative()) break :size null;
1686 for (debug_info.func_list.items) |func| {
1687 if (func.pc_range) |range| {
1688 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
1689 break :size range.end - range.start;
1690 }
1691 }
1692 }
1693 break :size null;
1694 };
1695
1696 if (size) |ss| {
1697 buf[0] = .{
1698 .n_strx = 0,
1699 .n_type = macho.N_BNSYM,
1700 .n_sect = sym.n_sect,
1701 .n_desc = 0,
1702 .n_value = sym.n_value,
1703 };
1704 buf[1] = .{
1705 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1706 .n_type = macho.N_FUN,
1707 .n_sect = sym.n_sect,
1708 .n_desc = 0,
1709 .n_value = sym.n_value,
1710 };
1711 buf[2] = .{
1712 .n_strx = 0,
1713 .n_type = macho.N_FUN,
1714 .n_sect = 0,
1715 .n_desc = 0,
1716 .n_value = ss,
1717 };
1718 buf[3] = .{
1719 .n_strx = 0,
1720 .n_type = macho.N_ENSYM,
1721 .n_sect = sym.n_sect,
1722 .n_desc = 0,
1723 .n_value = ss,
1724 };
1725 return buf;
1726 } else {
1727 buf[0] = .{
1728 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1729 .n_type = macho.N_STSYM,
1730 .n_sect = sym.n_sect,
1731 .n_desc = 0,
1732 .n_value = sym.n_value,
1733 };
1734 return buf[0..1];
1735 }
1736}
1737
1738const SymtabCtx = struct {
1739 nlocalsym: u32,
1740 nextdefsym: u32,
1741 nundefsym: u32,
1742 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
1743};
1744
1745fn writeDysymtab(macho_file: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
1746 const gpa = macho_file.base.allocator;
1747 const nstubs = @intCast(u32, macho_file.stubs_table.count());
1748 const ngot_entries = @intCast(u32, macho_file.got_entries_table.count());
1749 const nindirectsyms = nstubs * 2 + ngot_entries;
1750 const iextdefsym = ctx.nlocalsym;
1751 const iundefsym = iextdefsym + ctx.nextdefsym;
1752
1753 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1754 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1755 const needed_size = nindirectsyms * @sizeOf(u32);
1756 seg.filesize = offset + needed_size - seg.fileoff;
1757
1758 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1759
1760 var buf = std.ArrayList(u8).init(gpa);
1761 defer buf.deinit();
1762 try buf.ensureTotalCapacity(needed_size);
1763 const writer = buf.writer();
1764
1765 if (macho_file.stubs_section_index) |sect_id| {
1766 const stubs = &macho_file.sections.items(.header)[sect_id];
1767 stubs.reserved1 = 0;
1768 for (macho_file.stubs.items) |entry| {
1769 if (entry.sym_index == 0) continue;
1770 const atom_sym = entry.getSymbol(macho_file);
1771 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1772 const target_sym = macho_file.getSymbol(entry.target);
1773 assert(target_sym.undf());
1774 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1775 }
1776 }
1777
1778 if (macho_file.got_section_index) |sect_id| {
1779 const got = &macho_file.sections.items(.header)[sect_id];
1780 got.reserved1 = nstubs;
1781 for (macho_file.got_entries.items) |entry| {
1782 if (entry.sym_index == 0) continue;
1783 const atom_sym = entry.getSymbol(macho_file);
1784 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1785 const target_sym = macho_file.getSymbol(entry.target);
1786 if (target_sym.undf()) {
1787 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1788 } else {
1789 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
1790 }
1791 }
1792 }
1793
1794 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
1795 const la_symbol_ptr = &macho_file.sections.items(.header)[sect_id];
1796 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
1797 for (macho_file.stubs.items) |entry| {
1798 if (entry.sym_index == 0) continue;
1799 const atom_sym = entry.getSymbol(macho_file);
1800 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1801 const target_sym = macho_file.getSymbol(entry.target);
1802 assert(target_sym.undf());
1803 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1804 }
1805 }
1806
1807 assert(buf.items.len == needed_size);
1808 try macho_file.base.file.?.pwriteAll(buf.items, offset);
1809
1810 lc.nlocalsym = ctx.nlocalsym;
1811 lc.iextdefsym = iextdefsym;
1812 lc.nextdefsym = ctx.nextdefsym;
1813 lc.iundefsym = iundefsym;
1814 lc.nundefsym = ctx.nundefsym;
1815 lc.indirectsymoff = @intCast(u32, offset);
1816 lc.nindirectsyms = nindirectsyms;
1817}
1818
1819fn writeCodeSignaturePadding(
1820 macho_file: *MachO,
1821 code_sig: *CodeSignature,
1822 ncmds: *u32,
1823 lc_writer: anytype,
1824) !u32 {
1825 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1826 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
1827 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
1828 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
1829 const needed_size = code_sig.estimateSize(offset);
1830 seg.filesize = offset + needed_size - seg.fileoff;
1831 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
1832 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1833 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1834 // except for code signature data.
1835 try macho_file.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
1836
1837 try lc_writer.writeStruct(macho.linkedit_data_command{
1838 .cmd = .CODE_SIGNATURE,
1839 .cmdsize = @sizeOf(macho.linkedit_data_command),
1840 .dataoff = @intCast(u32, offset),
1841 .datasize = @intCast(u32, needed_size),
1842 });
1843 ncmds.* += 1;
1844
1845 return @intCast(u32, offset);
1846}
1847
1848fn writeCodeSignature(macho_file: *MachO, code_sig: *CodeSignature, offset: u32) !void {
1849 const seg = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1850
1851 var buffer = std.ArrayList(u8).init(macho_file.base.allocator);
1852 defer buffer.deinit();
1853 try buffer.ensureTotalCapacityPrecise(code_sig.size());
1854 try code_sig.writeAdhocSignature(macho_file.base.allocator, .{
1855 .file = macho_file.base.file.?,
1856 .exec_seg_base = seg.fileoff,
1857 .exec_seg_limit = seg.filesize,
1858 .file_size = offset,
1859 .output_mode = macho_file.base.options.output_mode,
1860 }, buffer.writer());
1861 assert(buffer.items.len == code_sig.size());
1862
1863 log.debug("writing code signature from 0x{x} to 0x{x}", .{
1864 offset,
1865 offset + buffer.items.len,
1866 });
1867
1868 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1869}
1870
1871fn writeSegmentHeaders(macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
1872 for (macho_file.segments.items) |seg, i| {
1873 const indexes = macho_file.getSectionIndexes(@intCast(u8, i));
1874 var out_seg = seg;
1875 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
1876 out_seg.nsects = 0;
1877
1878 // Update section headers count; any section with size of 0 is excluded
1879 // since it doesn't have any data in the final binary file.
1880 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1881 if (header.size == 0) continue;
1882 out_seg.cmdsize += @sizeOf(macho.section_64);
1883 out_seg.nsects += 1;
1884 }
1885
1886 if (out_seg.nsects == 0 and
1887 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
1888 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
1889
1890 try writer.writeStruct(out_seg);
1891 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1892 if (header.size == 0) continue;
1893 try writer.writeStruct(header);
1894 }
1895
1896 ncmds.* += 1;
1897 }
1898}
1899
1900/// Writes Mach-O file header.
1901fn writeHeader(macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void {
1902 var header: macho.mach_header_64 = .{};
1903 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
1904
1905 switch (macho_file.base.options.target.cpu.arch) {
1906 .aarch64 => {
1907 header.cputype = macho.CPU_TYPE_ARM64;
1908 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
1909 },
1910 .x86_64 => {
1911 header.cputype = macho.CPU_TYPE_X86_64;
1912 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
1913 },
1914 else => return error.UnsupportedCpuArchitecture,
1915 }
1916
1917 switch (macho_file.base.options.output_mode) {
1918 .Exe => {
1919 header.filetype = macho.MH_EXECUTE;
1920 },
1921 .Lib => {
1922 // By this point, it can only be a dylib.
1923 header.filetype = macho.MH_DYLIB;
1924 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
1925 },
1926 else => unreachable,
1927 }
1928
1929 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
1930 if (macho_file.sections.items(.header)[sect_id].size > 0) {
1931 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
1932 }
1933 }
1934
1935 header.ncmds = ncmds;
1936 header.sizeofcmds = sizeofcmds;
1937
1938 log.debug("writing Mach-O header {}", .{header});
1939
1940 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
1941}