authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-01 12:28:58+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-03 21:19:41+02:00
logbb532584bc569edb563b757c658fd743731837ec
treeaa0c1356909f0be2431e5420b0e84ca5ae89e5ad
parentf26d5ee7ea97c8fd6e5b2655f845be7e4293930e

macho: update how we insert output sections

Instead of generating sections upfront, allow generation by scanning the object files for input -> output sections mapping. Next, always strive to keep output sections in the final container sorted as they appear in the final binary. This makes the linker less messy wrt handling of output sections sort order for dyld/macOS not to complain. There's still more work to be done for incremental context though to make this work but looks promising already.

5 files changed, 222 insertions(+), 281 deletions(-)

lib/std/macho.zig+6-1
...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
780 return parseName(&sect.segname);780 return parseName(&sect.segname);
781 }781 }
782782
783 pub fn type_(sect: section_64) u8 {783 pub fn @"type"(sect: section_64) u8 {
784 return @truncate(u8, sect.flags & 0xff);784 return @truncate(u8, sect.flags & 0xff);
785 }785 }
786786
...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
794 }794 }
795795
796 pub fn isZerofill(sect: section_64) bool {
797 const tt = sect.@"type"();
798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799 }
800
796 pub fn isDebug(sect: section_64) bool {801 pub fn isDebug(sect: section_64) bool {
797 return sect.attrs() & S_ATTR_DEBUG != 0;802 return sect.attrs() & S_ATTR_DEBUG != 0;
798 }803 }
src/link/MachO.zig+192-272
...@@ -559,6 +559,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -559,6 +559,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
559559
560 if (build_options.enable_logging) {560 if (build_options.enable_logging) {
561 self.logSymtab();561 self.logSymtab();
562 self.logSections();
562 self.logAtoms();563 self.logAtoms();
563 }564 }
564565
...@@ -1140,7 +1141,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1140,7 +1141,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1140 try self.resolveSymbolsInArchives();1141 try self.resolveSymbolsInArchives();
1141 try self.resolveDyldStubBinder();1142 try self.resolveDyldStubBinder();
1142 try self.createDyldPrivateAtom();1143 try self.createDyldPrivateAtom();
1143 try self.createStubHelperPreambleAtom();
1144 try self.resolveSymbolsInDylibs();1144 try self.resolveSymbolsInDylibs();
1145 try self.createMhExecuteHeaderSymbol();1145 try self.createMhExecuteHeaderSymbol();
1146 try self.createDsoHandleSymbol();1146 try self.createDsoHandleSymbol();
...@@ -1156,6 +1156,11 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1156,6 +1156,11 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1156 return error.FrameworkNotFound;1156 return error.FrameworkNotFound;
1157 }1157 }
11581158
1159 for (self.objects.items) |*object| {
1160 try object.scanInputSections(self);
1161 }
1162
1163 try self.createStubHelperPreambleAtom();
1159 try self.createTentativeDefAtoms();1164 try self.createTentativeDefAtoms();
11601165
1161 for (self.objects.items) |*object, object_id| {1166 for (self.objects.items) |*object, object_id| {
...@@ -1166,14 +1171,14 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1166,14 +1171,14 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1166 try dead_strip.gcAtoms(self);1171 try dead_strip.gcAtoms(self);
1167 }1172 }
11681173
1169 try self.pruneAndSortSections();
1170 try self.allocateSegments();1174 try self.allocateSegments();
1171 try self.allocateSymbols();1175 try self.allocateSymbols();
11721176
1173 try self.allocateSpecialSymbols();1177 try self.allocateSpecialSymbols();
11741178
1175 if (build_options.enable_logging) {1179 if (build_options.enable_logging or true) {
1176 self.logSymtab();1180 self.logSymtab();
1181 self.logSections();
1177 self.logAtoms();1182 self.logAtoms();
1178 }1183 }
11791184
...@@ -1691,7 +1696,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {...@@ -1691,7 +1696,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
1691 const segname = sect.segName();1696 const segname = sect.segName();
1692 const sectname = sect.sectName();1697 const sectname = sect.sectName();
1693 const res: ?u8 = blk: {1698 const res: ?u8 = blk: {
1694 switch (sect.type_()) {1699 switch (sect.@"type"()) {
1695 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {1700 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
1696 if (self.text_const_section_index == null) {1701 if (self.text_const_section_index == null) {
1697 self.text_const_section_index = try self.initSection(1702 self.text_const_section_index = try self.initSection(
...@@ -2197,27 +2202,6 @@ fn allocateSymbols(self: *MachO) !void {...@@ -2197,27 +2202,6 @@ fn allocateSymbols(self: *MachO) !void {
2197 }2202 }
2198}2203}
21992204
2200fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
2201 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
2202
2203 while (true) {
2204 const atom_sym = atom.getSymbolPtr(self);
2205 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
2206
2207 for (atom.contained.items) |sym_at_off| {
2208 const contained_sym = self.getSymbolPtr(.{
2209 .sym_index = sym_at_off.sym_index,
2210 .file = atom.file,
2211 });
2212 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2213 }
2214
2215 if (atom.prev) |prev| {
2216 atom = prev;
2217 } else break;
2218 }
2219}
2220
2221fn allocateSpecialSymbols(self: *MachO) !void {2205fn allocateSpecialSymbols(self: *MachO) !void {
2222 for (&[_][]const u8{2206 for (&[_][]const u8{
2223 "___dso_handle",2207 "___dso_handle",
...@@ -2245,9 +2229,10 @@ fn writeAtomsOneShot(self: *MachO) !void {...@@ -2245,9 +2229,10 @@ fn writeAtomsOneShot(self: *MachO) !void {
22452229
2246 for (slice.items(.last_atom)) |last_atom, sect_id| {2230 for (slice.items(.last_atom)) |last_atom, sect_id| {
2247 const header = slice.items(.header)[sect_id];2231 const header = slice.items(.header)[sect_id];
2232 if (header.size == 0) continue;
2248 var atom = last_atom.?;2233 var atom = last_atom.?;
22492234
2250 if (header.flags == macho.S_ZEROFILL or header.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;2235 if (header.isZerofill()) continue;
22512236
2252 var buffer = std.ArrayList(u8).init(gpa);2237 var buffer = std.ArrayList(u8).init(gpa);
2253 defer buffer.deinit();2238 defer buffer.deinit();
...@@ -2334,8 +2319,7 @@ fn writeAtomsIncremental(self: *MachO) !void {...@@ -2334,8 +2319,7 @@ fn writeAtomsIncremental(self: *MachO) !void {
2334 const sect_i = @intCast(u8, i);2319 const sect_i = @intCast(u8, i);
2335 const header = slice.items(.header)[sect_i];2320 const header = slice.items(.header)[sect_i];
23362321
2337 // TODO handle zerofill in stage22322 if (header.isZerofill()) continue;
2338 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
23392323
2340 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });2324 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
23412325
...@@ -3904,7 +3888,12 @@ fn getOutputSectionAtom(...@@ -3904,7 +3888,12 @@ fn getOutputSectionAtom(
3904 // TODO finish and audit this function3888 // TODO finish and audit this function
3905 if (val.isUndefDeep()) {3889 if (val.isUndefDeep()) {
3906 if (mode == .ReleaseFast or mode == .ReleaseSmall) {3890 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3907 break :blk self.bss_section_index.?;3891 break :blk (try self.getOutputSection(.{
3892 .segname = makeStaticString("__DATA"),
3893 .sectname = makeStaticString("__bss"),
3894 .size = code.len,
3895 .@"align" = align_log_2,
3896 })).?;
3908 } else {3897 } else {
3909 break :blk self.data_section_index.?;3898 break :blk self.data_section_index.?;
3910 }3899 }
...@@ -4488,74 +4477,6 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4488,74 +4477,6 @@ fn populateMissingMetadata(self: *MachO) !void {
4488 );4477 );
4489 }4478 }
44904479
4491 if (self.tlv_section_index == null) {
4492 const needed_size = if (self.mode == .incremental)
4493 @sizeOf(u64) * self.base.options.symbol_count_hint
4494 else
4495 0;
4496 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4497 self.tlv_section_index = try self.initSection(
4498 self.data_segment_cmd_index.?,
4499 "__thread_vars",
4500 needed_size,
4501 alignment,
4502 .{
4503 .flags = macho.S_THREAD_LOCAL_VARIABLES,
4504 },
4505 );
4506 }
4507
4508 if (self.tlv_data_section_index == null) {
4509 const needed_size = if (self.mode == .incremental)
4510 @sizeOf(u64) * self.base.options.symbol_count_hint
4511 else
4512 0;
4513 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4514 self.tlv_data_section_index = try self.initSection(
4515 self.data_segment_cmd_index.?,
4516 "__thread_data",
4517 needed_size,
4518 alignment,
4519 .{
4520 .flags = macho.S_THREAD_LOCAL_REGULAR,
4521 },
4522 );
4523 }
4524
4525 if (self.tlv_bss_section_index == null) {
4526 const needed_size = if (self.mode == .incremental)
4527 @sizeOf(u64) * self.base.options.symbol_count_hint
4528 else
4529 0;
4530 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4531 self.tlv_bss_section_index = try self.initSection(
4532 self.data_segment_cmd_index.?,
4533 "__thread_bss",
4534 needed_size,
4535 alignment,
4536 .{
4537 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
4538 },
4539 );
4540 }
4541
4542 if (self.bss_section_index == null) {
4543 const needed_size = if (self.mode == .incremental)
4544 @sizeOf(u64) * self.base.options.symbol_count_hint
4545 else
4546 0;
4547 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4548 self.bss_section_index = try self.initSection(
4549 self.data_segment_cmd_index.?,
4550 "__bss",
4551 needed_size,
4552 alignment,
4553 .{
4554 .flags = macho.S_ZEROFILL,
4555 },
4556 );
4557 }
4558
4559 if (self.linkedit_segment_cmd_index == null) {4480 if (self.linkedit_segment_cmd_index == null) {
4560 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);4481 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
4561 var vmaddr: u64 = 0;4482 var vmaddr: u64 = 0;
...@@ -4690,18 +4611,19 @@ fn allocateSegments(self: *MachO) !void {...@@ -4690,18 +4611,19 @@ fn allocateSegments(self: *MachO) !void {
4690 }, try self.calcMinHeaderPad());4611 }, try self.calcMinHeaderPad());
46914612
4692 if (self.text_segment_cmd_index) |index| blk: {4613 if (self.text_segment_cmd_index) |index| blk: {
4693 const seg = &self.segments.items[index];4614 const indexes = self.getSectionIndexes(index);
4694 if (seg.nsects == 0) break :blk;4615 if (indexes.start == indexes.end) break :blk;
4616 const seg = self.segments.items[index];
46954617
4696 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.4618 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4697 var min_alignment: u32 = 0;4619 var min_alignment: u32 = 0;
4698 for (self.sections.items(.header)[0..seg.nsects]) |header| {4620 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4699 const alignment = try math.powi(u32, 2, header.@"align");4621 const alignment = try math.powi(u32, 2, header.@"align");
4700 min_alignment = math.max(min_alignment, alignment);4622 min_alignment = math.max(min_alignment, alignment);
4701 }4623 }
47024624
4703 assert(min_alignment > 0);4625 assert(min_alignment > 0);
4704 const last_header = self.sections.items(.header)[seg.nsects - 1];4626 const last_header = self.sections.items(.header)[indexes.end - 1];
4705 const shift: u32 = shift: {4627 const shift: u32 = shift: {
4706 const diff = seg.filesize - last_header.offset - last_header.size;4628 const diff = seg.filesize - last_header.offset - last_header.size;
4707 const factor = @divTrunc(diff, min_alignment);4629 const factor = @divTrunc(diff, min_alignment);
...@@ -4709,7 +4631,7 @@ fn allocateSegments(self: *MachO) !void {...@@ -4709,7 +4631,7 @@ fn allocateSegments(self: *MachO) !void {
4709 };4631 };
47104632
4711 if (shift > 0) {4633 if (shift > 0) {
4712 for (self.sections.items(.header)[0..seg.nsects]) |*header| {4634 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4713 header.offset += shift;4635 header.offset += shift;
4714 header.addr += shift;4636 header.addr += shift;
4715 }4637 }
...@@ -4746,16 +4668,14 @@ fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_si...@@ -4746,16 +4668,14 @@ fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_si
4746 seg.vmsize = init_size;4668 seg.vmsize = init_size;
47474669
4748 // Allocate the sections according to their alignment at the beginning of the segment.4670 // Allocate the sections according to their alignment at the beginning of the segment.
4671 const indexes = self.getSectionIndexes(index);
4749 var start = init_size;4672 var start = init_size;
4750 const slice = self.sections.slice();4673 const slice = self.sections.slice();
4751 for (slice.items(.header)) |*header, sect_id| {4674 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4752 const segment_index = slice.items(.segment_index)[sect_id];
4753 if (segment_index != index) continue;
4754 const is_zerofill = header.flags == macho.S_ZEROFILL or header.flags == macho.S_THREAD_LOCAL_ZEROFILL;
4755 const alignment = try math.powi(u32, 2, header.@"align");4675 const alignment = try math.powi(u32, 2, header.@"align");
4756 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);4676 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
47574677
4758 header.offset = if (is_zerofill)4678 header.offset = if (header.isZerofill())
4759 04679 0
4760 else4680 else
4761 @intCast(u32, seg.fileoff + start_aligned);4681 @intCast(u32, seg.fileoff + start_aligned);
...@@ -4763,7 +4683,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_si...@@ -4763,7 +4683,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_si
47634683
4764 start = start_aligned + header.size;4684 start = start_aligned + header.size;
47654685
4766 if (!is_zerofill) {4686 if (!header.isZerofill()) {
4767 seg.filesize = start;4687 seg.filesize = start;
4768 }4688 }
4769 seg.vmsize = start;4689 seg.vmsize = start;
...@@ -4788,7 +4708,7 @@ fn initSection(...@@ -4788,7 +4708,7 @@ fn initSection(
4788 opts: InitSectionOpts,4708 opts: InitSectionOpts,
4789) !u8 {4709) !u8 {
4790 const seg = &self.segments.items[segment_id];4710 const seg = &self.segments.items[segment_id];
4791 var header = macho.section_64{4711 const index = try self.insertSection(segment_id, .{
4792 .sectname = makeStaticString(sectname),4712 .sectname = makeStaticString(sectname),
4793 .segname = seg.segname,4713 .segname = seg.segname,
4794 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,4714 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
...@@ -4796,40 +4716,162 @@ fn initSection(...@@ -4796,40 +4716,162 @@ fn initSection(
4796 .flags = opts.flags,4716 .flags = opts.flags,
4797 .reserved1 = opts.reserved1,4717 .reserved1 = opts.reserved1,
4798 .reserved2 = opts.reserved2,4718 .reserved2 = opts.reserved2,
4799 };4719 });
4720 seg.cmdsize += @sizeOf(macho.section_64);
4721 seg.nsects += 1;
48004722
4801 if (self.mode == .incremental) {4723 if (self.mode == .incremental) {
4724 const header = &self.sections.items(.header)[index];
4725 const prev_end_off = if (index > 0) blk: {
4726 const prev_section = self.sections.get(index - 1);
4727 if (prev_section.segment_index == segment_id) {
4728 const prev_header = prev_section.header;
4729 break :blk prev_header.offset + padToIdeal(prev_header.size);
4730 } else break :blk seg.fileoff;
4731 } else 0;
4802 const alignment_pow_2 = try math.powi(u32, 2, alignment);4732 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4803 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?)4733 const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4804 try self.calcMinHeaderPad()4734 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
4805 else4735 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
4806 null;
4807 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
4808 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4809 header.segName(),
4810 header.sectName(),
4811 off,
4812 off + size,
4813 });
48144736
4815 header.addr = seg.vmaddr + off - seg.fileoff;4737 header.addr = seg.vmaddr + off - seg.fileoff;
48164738
4817 // TODO handle zerofill in stage24739 if (!header.isZerofill()) {
4818 // const is_zerofill = opts.flags == macho.S_ZEROFILL or opts.flags == macho.S_THREAD_LOCAL_ZEROFILL;4740 header.offset = @intCast(u32, off);
4819 header.offset = @intCast(u32, off);4741 }
4742
4743 self.updateSectionOrdinals(index + 1);
4744 }
4745
4746 return index;
4747}
48204748
4821 try self.updateSectionOrdinals();4749fn getSectionPrecedence(header: macho.section_64) u4 {
4750 if (header.isCode()) {
4751 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
4752 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
4753 return 0x2;
4754 }
4755 switch (header.@"type"()) {
4756 macho.S_NON_LAZY_SYMBOL_POINTERS,
4757 macho.S_LAZY_SYMBOL_POINTERS,
4758 => return 0x0,
4759 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
4760 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
4761 macho.S_ZEROFILL => return 0xf,
4762 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
4763 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
4764 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
4765 return 0xf
4766 else
4767 return 0x3,
4822 }4768 }
4769}
48234770
4824 const index = @intCast(u8, self.sections.slice().len);4771fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
4825 try self.sections.append(self.base.allocator, .{4772 const precedence = getSectionPrecedence(header);
4826 .segment_index = segment_id,4773 const indexes = self.getSectionIndexes(segment_index);
4774 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
4775 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
4776 } else indexes.end;
4777 log.debug("inserting section '{s},{s}' at index {d}", .{
4778 header.segName(),
4779 header.sectName(),
4780 insertion_index,
4781 });
4782 // TODO slim it down
4783 for (&[_]*?u8{
4784 // __TEXT
4785 &self.text_section_index,
4786 &self.stubs_section_index,
4787 &self.stub_helper_section_index,
4788 &self.gcc_except_tab_section_index,
4789 &self.cstring_section_index,
4790 &self.ustring_section_index,
4791 &self.text_const_section_index,
4792 &self.objc_methlist_section_index,
4793 &self.objc_methname_section_index,
4794 &self.objc_methtype_section_index,
4795 &self.objc_classname_section_index,
4796 &self.eh_frame_section_index,
4797 // __DATA_CONST
4798 &self.got_section_index,
4799 &self.mod_init_func_section_index,
4800 &self.mod_term_func_section_index,
4801 &self.data_const_section_index,
4802 &self.objc_cfstring_section_index,
4803 &self.objc_classlist_section_index,
4804 &self.objc_imageinfo_section_index,
4805 // __DATA
4806 &self.rustc_section_index,
4807 &self.la_symbol_ptr_section_index,
4808 &self.objc_const_section_index,
4809 &self.objc_selrefs_section_index,
4810 &self.objc_classrefs_section_index,
4811 &self.objc_data_section_index,
4812 &self.data_section_index,
4813 &self.tlv_section_index,
4814 &self.tlv_ptrs_section_index,
4815 &self.tlv_data_section_index,
4816 &self.tlv_bss_section_index,
4817 &self.bss_section_index,
4818 }) |maybe_index| {
4819 const index = maybe_index.* orelse continue;
4820 if (insertion_index <= index) maybe_index.* = index + 1;
4821 }
4822 try self.sections.insert(self.base.allocator, insertion_index, .{
4823 .segment_index = segment_index,
4827 .header = header,4824 .header = header,
4828 });4825 });
4829 seg.cmdsize += @sizeOf(macho.section_64);4826 return insertion_index;
4830 seg.nsects += 1;4827}
48314828
4832 return index;4829fn updateSectionOrdinals(self: *MachO, start: u8) void {
4830 const tracy = trace(@src());
4831 defer tracy.end();
4832
4833 const slice = self.sections.slice();
4834 for (slice.items(.last_atom)[start..]) |last_atom| {
4835 var atom = last_atom.?;
4836
4837 while (true) {
4838 const sym = atom.getSymbolPtr(self);
4839 sym.n_sect = start + 1;
4840
4841 for (atom.contained.items) |sym_at_off| {
4842 const contained_sym = self.getSymbolPtr(.{
4843 .sym_index = sym_at_off.sym_index,
4844 .file = atom.file,
4845 });
4846 contained_sym.n_sect = start + 1;
4847 }
4848
4849 if (atom.prev) |prev| {
4850 atom = prev;
4851 } else break;
4852 }
4853 }
4854}
4855
4856fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
4857 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
4858
4859 while (true) {
4860 const atom_sym = atom.getSymbolPtr(self);
4861 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
4862
4863 for (atom.contained.items) |sym_at_off| {
4864 const contained_sym = self.getSymbolPtr(.{
4865 .sym_index = sym_at_off.sym_index,
4866 .file = atom.file,
4867 });
4868 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
4869 }
4870
4871 if (atom.prev) |prev| {
4872 atom = prev;
4873 } else break;
4874 }
4833}4875}
48344876
4835fn findFreeSpace(self: MachO, segment_id: u8, alignment: u64, start: ?u64) u64 {4877fn findFreeSpace(self: MachO, segment_id: u8, alignment: u64, start: ?u64) u64 {
...@@ -5181,153 +5223,18 @@ fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64,...@@ -5181,153 +5223,18 @@ fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64,
5181 return .{ .vmaddr = 0, .fileoff = 0 };5223 return .{ .vmaddr = 0, .fileoff = 0 };
5182}5224}
51835225
5184fn pruneAndSortSections(self: *MachO) !void {
5185 const gpa = self.base.allocator;
5186
5187 var sections = self.sections.toOwnedSlice();
5188 defer sections.deinit(gpa);
5189 try self.sections.ensureTotalCapacity(gpa, sections.len);
5190
5191 for (&[_]*?u8{
5192 // __TEXT
5193 &self.text_section_index,
5194 &self.stubs_section_index,
5195 &self.stub_helper_section_index,
5196 &self.gcc_except_tab_section_index,
5197 &self.cstring_section_index,
5198 &self.ustring_section_index,
5199 &self.text_const_section_index,
5200 &self.objc_methlist_section_index,
5201 &self.objc_methname_section_index,
5202 &self.objc_methtype_section_index,
5203 &self.objc_classname_section_index,
5204 &self.eh_frame_section_index,
5205 // __DATA_CONST
5206 &self.got_section_index,
5207 &self.mod_init_func_section_index,
5208 &self.mod_term_func_section_index,
5209 &self.data_const_section_index,
5210 &self.objc_cfstring_section_index,
5211 &self.objc_classlist_section_index,
5212 &self.objc_imageinfo_section_index,
5213 // __DATA
5214 &self.rustc_section_index,
5215 &self.la_symbol_ptr_section_index,
5216 &self.objc_const_section_index,
5217 &self.objc_selrefs_section_index,
5218 &self.objc_classrefs_section_index,
5219 &self.objc_data_section_index,
5220 &self.data_section_index,
5221 &self.tlv_section_index,
5222 &self.tlv_ptrs_section_index,
5223 &self.tlv_data_section_index,
5224 &self.tlv_bss_section_index,
5225 &self.bss_section_index,
5226 }) |maybe_index| {
5227 const old_idx = maybe_index.* orelse continue;
5228 const segment_index = sections.items(.segment_index)[old_idx];
5229 const header = sections.items(.header)[old_idx];
5230 const last_atom = sections.items(.last_atom)[old_idx];
5231 if (header.size == 0) {
5232 log.debug("pruning section {s},{s}", .{ header.segName(), header.sectName() });
5233 maybe_index.* = null;
5234 const seg = &self.segments.items[segment_index];
5235 seg.cmdsize -= @sizeOf(macho.section_64);
5236 seg.nsects -= 1;
5237 } else {
5238 maybe_index.* = @intCast(u8, self.sections.slice().len);
5239 self.sections.appendAssumeCapacity(.{
5240 .segment_index = segment_index,
5241 .header = header,
5242 .last_atom = last_atom,
5243 });
5244 }
5245 }
5246
5247 for (self.segments.items) |*seg| {
5248 const segname = seg.segName();
5249 if (seg.nsects == 0 and
5250 !mem.eql(u8, "__TEXT", segname) and
5251 !mem.eql(u8, "__PAGEZERO", segname) and
5252 !mem.eql(u8, "__LINKEDIT", segname))
5253 {
5254 // Segment has now become empty, so mark it as such
5255 log.debug("marking segment {s} as dead", .{seg.segName()});
5256 seg.cmd = @intToEnum(macho.LC, 0);
5257 }
5258 }
5259}
5260
5261fn updateSectionOrdinals(self: *MachO) !void {
5262 _ = self;
5263 const tracy = trace(@src());
5264 defer tracy.end();
5265
5266 @panic("updating section ordinals");
5267
5268 // const gpa = self.base.allocator;
5269
5270 // var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5271 // defer ordinal_remap.deinit();
5272 // var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
5273
5274 // var new_ordinal: u8 = 0;
5275 // for (&[_]?u16{
5276 // self.text_segment_cmd_index,
5277 // self.data_const_segment_cmd_index,
5278 // self.data_segment_cmd_index,
5279 // }) |maybe_index| {
5280 // const index = maybe_index orelse continue;
5281 // const seg = self.load_commands.items[index].segment;
5282 // for (seg.sections.items) |sect, sect_id| {
5283 // const match = MatchingSection{
5284 // .seg = @intCast(u16, index),
5285 // .sect = @intCast(u16, sect_id),
5286 // };
5287 // const old_ordinal = self.getSectionOrdinal(match);
5288 // new_ordinal += 1;
5289 // log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5290 // sect.segName(),
5291 // sect.sectName(),
5292 // old_ordinal,
5293 // new_ordinal,
5294 // });
5295 // try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5296 // try ordinals.putNoClobber(gpa, match, {});
5297 // }
5298 // }
5299
5300 // // FIXME Jakub
5301 // // TODO no need for duping work here; simply walk the atom graph
5302 // for (self.locals.items) |*sym| {
5303 // if (sym.undf()) continue;
5304 // if (sym.n_sect == 0) continue;
5305 // sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5306 // }
5307 // for (self.objects.items) |*object| {
5308 // for (object.symtab.items) |*sym| {
5309 // if (sym.undf()) continue;
5310 // if (sym.n_sect == 0) continue;
5311 // sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5312 // }
5313 // }
5314
5315 // self.section_ordinals.deinit(gpa);
5316 // self.section_ordinals = ordinals;
5317}
5318
5319pub fn writeSegmentHeaders(self: *MachO, start: usize, end: usize, ncmds: *u32, writer: anytype) !void {5226pub fn writeSegmentHeaders(self: *MachO, start: usize, end: usize, ncmds: *u32, writer: anytype) !void {
5320 var count: usize = 0;5227 for (self.segments.items[start..end]) |seg, i| {
5321 for (self.segments.items[start..end]) |seg| {5228 if (seg.nsects == 0 and
5322 if (seg.cmd == .NONE) continue;5229 (mem.eql(u8, seg.segName(), "__DATA_CONST") or
5230 mem.eql(u8, seg.segName(), "__DATA"))) continue;
5323 try writer.writeStruct(seg);5231 try writer.writeStruct(seg);
53245232
5325 // TODO5233 const indexes = self.getSectionIndexes(@intCast(u8, start + i));
5326 for (self.sections.items(.header)[count..][0..seg.nsects]) |header| {5234 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
5327 try writer.writeStruct(header);5235 try writer.writeStruct(header);
5328 }5236 }
53295237
5330 count += seg.nsects;
5331 ncmds.* += 1;5238 ncmds.* += 1;
5332 }5239 }
5333}5240}
...@@ -6644,6 +6551,19 @@ fn generateSymbolStabsForSymbol(...@@ -6644,6 +6551,19 @@ fn generateSymbolStabsForSymbol(
6644// try writer.writeByte(']');6551// try writer.writeByte(']');
6645// }6552// }
66466553
6554fn logSections(self: *MachO) void {
6555 log.debug("sections:", .{});
6556 for (self.sections.items(.header)) |header, i| {
6557 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
6558 i + 1,
6559 header.segName(),
6560 header.sectName(),
6561 header.offset,
6562 header.size,
6563 });
6564 }
6565}
6566
6647fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {6567fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
6648 mem.set(u8, buf[0..4], '_');6568 mem.set(u8, buf[0..4], '_');
6649 mem.set(u8, buf[4..], ' ');6569 mem.set(u8, buf[4..], ' ');
src/link/MachO/Atom.zig+2-2
...@@ -466,7 +466,7 @@ fn addPtrBindingOrRebase(...@@ -466,7 +466,7 @@ fn addPtrBindingOrRebase(
466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
467 const header = section.header;467 const header = section.header;
468 const segment_index = section.segment_index;468 const segment_index = section.segment_index;
469 const sect_type = header.type_();469 const sect_type = header.@"type"();
470470
471 const should_rebase = rebase: {471 const should_rebase = rebase: {
472 if (rel.r_length != 3) break :rebase false;472 if (rel.r_length != 3) break :rebase false;
...@@ -571,7 +571,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -571,7 +571,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
571 const is_tlv = is_tlv: {571 const is_tlv = is_tlv: {
572 const source_sym = self.getSymbol(macho_file);572 const source_sym = self.getSymbol(macho_file);
573 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];573 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
574 break :is_tlv header.type_() == macho.S_THREAD_LOCAL_VARIABLES;574 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
575 };575 };
576 const target_addr = blk: {576 const target_addr = blk: {
577 const target_atom = rel.getTargetAtom(macho_file) orelse {577 const target_atom = rel.getTargetAtom(macho_file) orelse {
src/link/MachO/Object.zig+18-5
...@@ -214,6 +214,23 @@ fn filterRelocs(...@@ -214,6 +214,23 @@ fn filterRelocs(
214 return relocs[start..end];214 return relocs[start..end];
215}215}
216216
217pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
218 for (self.sections.items) |sect| {
219 const match = (try macho_file.getOutputSection(sect)) orelse {
220 log.debug(" unhandled section", .{});
221 continue;
222 };
223 const output = macho_file.sections.items(.header)[match];
224 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
225 sect.segName(),
226 sect.sectName(),
227 match + 1,
228 output.segName(),
229 output.sectName(),
230 });
231 }
232}
233
217/// Splits object into atoms assuming one-shot linking mode.234/// Splits object into atoms assuming one-shot linking mode.
218pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {235pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
219 assert(macho_file.mode == .one_shot);236 assert(macho_file.mode == .one_shot);
...@@ -280,13 +297,9 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -280,13 +297,9 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
280 });297 });
281298
282 const cpu_arch = macho_file.base.options.target.cpu.arch;299 const cpu_arch = macho_file.base.options.target.cpu.arch;
283 const is_zerofill = blk: {
284 const section_type = sect.type_();
285 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
286 };
287300
288 // Read section's code301 // Read section's code
289 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect) else null;302 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
290303
291 // Read section's list of relocations304 // Read section's list of relocations
292 const relocs = @ptrCast(305 const relocs = @ptrCast(
src/link/MachO/dead_strip.zig+4-1
...@@ -43,6 +43,9 @@ fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {...@@ -43,6 +43,9 @@ fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
43 // The section will be GCed in the next step.43 // The section will be GCed in the next step.
44 section.last_atom = null;44 section.last_atom = null;
45 section.header.size = 0;45 section.header.size = 0;
46 const segment = &macho_file.segments.items[section.segment_index];
47 segment.cmdsize -= @sizeOf(macho.section_64);
48 segment.nsects -= 1;
46 }49 }
47 }50 }
4851
...@@ -93,7 +96,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void...@@ -93,7 +96,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
93 const is_gc_root = blk: {96 const is_gc_root = blk: {
94 if (source_sect.isDontDeadStrip()) break :blk true;97 if (source_sect.isDontDeadStrip()) break :blk true;
95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;98 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {99 switch (source_sect.@"type"()) {
97 macho.S_MOD_INIT_FUNC_POINTERS,100 macho.S_MOD_INIT_FUNC_POINTERS,
98 macho.S_MOD_TERM_FUNC_POINTERS,101 macho.S_MOD_TERM_FUNC_POINTERS,
99 => break :blk true,102 => break :blk true,