authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-05 11:15:48+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-05 11:15:48+02:00
logea8808f87b319c92dd74d75f1bb4fae1180e00ed
treefadca0745c9eca7b2159ce998d8fe93d76b7a541
parentd92b5416e86f2b6bf0cf530c8f98da572bf9efde

macho: add logic for segment expansion


1 files changed, 171 insertions(+), 20 deletions(-)

src/link/MachO.zig+171-20
......@@ -133,8 +133,8 @@ objc_selrefs_section_index: ?u16 = null,
133133objc_classrefs_section_index: ?u16 = null,
134134objc_data_section_index: ?u16 = null,
135135
136bss_file_offset: ?u64 = null,
137tlv_bss_file_offset: ?u64 = null,
136bss_file_offset: ?u32 = null,
137tlv_bss_file_offset: ?u32 = null,
138138
139139locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
140140globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
......@@ -1693,6 +1693,84 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
16931693 const padding: ?u64 = if (match.seg == self.text_segment_cmd_index.?) self.header_pad else null;
16941694 const atom_alignment = try math.powi(u64, 2, atom.alignment);
16951695 const new_offset = @intCast(u32, seg.findFreeSpace(needed_size, atom_alignment, padding));
1696
1697 if (new_offset + needed_size >= seg.inner.fileoff + seg.inner.filesize) {
1698 // Bummer, need to move all segments below down...
1699 // TODO is this the right estimate?
1700 const new_seg_size = mem.alignForwardGeneric(
1701 u64,
1702 padToIdeal(seg.inner.filesize + needed_size),
1703 self.page_size,
1704 );
1705 // TODO actually, we're always required to move in a number of pages so I guess all we need
1706 // to know here is the number of pages to shift downwards.
1707 const offset_amt = @intCast(u32, @intCast(i64, new_seg_size) - @intCast(i64, seg.inner.filesize));
1708 seg.inner.filesize = new_seg_size;
1709 seg.inner.vmsize = new_seg_size;
1710 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1711 seg.inner.segname,
1712 seg.inner.fileoff,
1713 seg.inner.fileoff + seg.inner.filesize,
1714 seg.inner.vmaddr,
1715 seg.inner.vmaddr + seg.inner.vmsize,
1716 });
1717 // TODO We should probably nop the expanded by distance, or put 0s.
1718
1719 // TODO copyRangeAll doesn't automatically extend the file on macOS.
1720 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1721 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
1722 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
1723
1724 var next: usize = match.seg + 1;
1725 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
1726 const next_seg = &self.load_commands.items[next].Segment;
1727 _ = try self.base.file.?.copyRangeAll(
1728 next_seg.inner.fileoff,
1729 self.base.file.?,
1730 next_seg.inner.fileoff + offset_amt,
1731 next_seg.inner.filesize,
1732 );
1733 next_seg.inner.fileoff += offset_amt;
1734 next_seg.inner.vmaddr += offset_amt;
1735 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1736 next_seg.inner.segname,
1737 next_seg.inner.fileoff,
1738 next_seg.inner.fileoff + next_seg.inner.filesize,
1739 next_seg.inner.vmaddr,
1740 next_seg.inner.vmaddr + next_seg.inner.vmsize,
1741 });
1742
1743 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
1744 // TODO put below snippet in a function.
1745 const moved_sect_offset = blk: {
1746 if (self.data_segment_cmd_index.? == next) {
1747 if (self.bss_section_index) |idx| {
1748 if (idx == moved_sect_id) break :blk &self.bss_file_offset.?;
1749 }
1750 if (self.tlv_bss_section_index) |idx| {
1751 if (idx == moved_sect_id) break :blk &self.tlv_bss_file_offset.?;
1752 }
1753 }
1754 break :blk &moved_sect.offset;
1755 };
1756 moved_sect_offset.* += offset_amt;
1757 moved_sect.addr += offset_amt;
1758 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1759 commands.segmentName(moved_sect.*),
1760 commands.sectionName(moved_sect.*),
1761 moved_sect_offset.*,
1762 moved_sect_offset.* + moved_sect.size,
1763 moved_sect.addr,
1764 moved_sect.addr + moved_sect.size,
1765 });
1766
1767 try self.allocateLocalSymbols(.{
1768 .seg = @intCast(u16, next),
1769 .sect = @intCast(u16, moved_sect_id),
1770 }, offset_amt);
1771 }
1772 }
1773 }
16961774 sect.offset = new_offset;
16971775 sect.addr = seg.inner.vmaddr + sect.offset - seg.inner.fileoff;
16981776 log.debug(" (found new {s},{s} free space from 0x{x} to 0x{x})", .{
......@@ -1701,11 +1779,9 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
17011779 new_offset,
17021780 new_offset + needed_size,
17031781 });
1704 try self.allocateLocalSymbols(match, old_base_addr);
1705 vaddr = @intCast(
1706 u64,
1707 @intCast(i64, vaddr) + @intCast(i64, sect.addr) - @intCast(i64, old_base_addr),
1708 );
1782 const offset_amt = @intCast(i64, sect.addr) - @intCast(i64, old_base_addr);
1783 try self.allocateLocalSymbols(match, offset_amt);
1784 vaddr = @intCast(u64, @intCast(i64, vaddr) + offset_amt);
17091785 }
17101786
17111787 sect.size = needed_size;
......@@ -1761,11 +1837,8 @@ pub fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
17611837 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
17621838}
17631839
1764fn allocateLocalSymbols(self: *MachO, match: MatchingSection, old_base_addr: u64) !void {
1840fn allocateLocalSymbols(self: *MachO, match: MatchingSection, offset: i64) !void {
17651841 var atom = self.blocks.get(match) orelse return;
1766 const seg = self.load_commands.items[match.seg].Segment;
1767 const sect = seg.sections.items[match.sect];
1768 const offset = @intCast(i64, sect.addr) - @intCast(i64, old_base_addr);
17691842
17701843 while (true) {
17711844 const atom_sym = &self.locals.items[atom.local_sym_index];
......@@ -3317,9 +3390,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
33173390 if (self.text_segment_cmd_index == null) {
33183391 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
33193392 const program_code_size_hint = self.base.options.program_code_size_hint;
3320 // const program_code_size_hint = 10;
33213393 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
3322 const ideal_size = self.header_pad + (program_code_size_hint + got_size_hint) * 5;
3394 const ideal_size = self.header_pad + program_code_size_hint + got_size_hint;
33233395 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
33243396
33253397 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
......@@ -3413,7 +3485,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
34133485 if (self.data_const_segment_cmd_index == null) {
34143486 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
34153487 const address_and_offset = self.nextSegmentAddressAndOffset();
3416 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint * 1000;
3488 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
34173489 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
34183490
34193491 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
......@@ -3454,7 +3526,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
34543526 if (self.data_segment_cmd_index == null) {
34553527 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
34563528 const address_and_offset = self.nextSegmentAddressAndOffset();
3457 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint * 1000;
3529 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
34583530 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
34593531
34603532 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
......@@ -3905,20 +3977,99 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
39053977 const old_base_addr = text_section.addr;
39063978 text_section.size = 0;
39073979 const new_offset = @intCast(u32, text_segment.findFreeSpace(needed_size, alignment, self.header_pad));
3980
3981 if (new_offset + needed_size >= text_segment.inner.fileoff + text_segment.inner.filesize) {
3982 // Bummer, need to move all segments below down...
3983 // TODO is this the right estimate?
3984 const new_seg_size = mem.alignForwardGeneric(
3985 u64,
3986 padToIdeal(text_segment.inner.filesize + needed_size),
3987 self.page_size,
3988 );
3989 // TODO actually, we're always required to move in a number of pages so I guess all we need
3990 // to know here is the number of pages to shift downwards.
3991 const offset_amt = @intCast(
3992 u32,
3993 @intCast(i64, new_seg_size) - @intCast(i64, text_segment.inner.filesize),
3994 );
3995 text_segment.inner.filesize = new_seg_size;
3996 text_segment.inner.vmsize = new_seg_size;
3997 log.debug(" (new __TEXT segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3998 text_segment.inner.fileoff,
3999 text_segment.inner.fileoff + text_segment.inner.filesize,
4000 text_segment.inner.vmaddr,
4001 text_segment.inner.vmaddr + text_segment.inner.vmsize,
4002 });
4003 // TODO We should probably nop the expanded by distance, or put 0s.
4004
4005 // TODO copyRangeAll doesn't automatically extend the file on macOS.
4006 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4007 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
4008 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
4009
4010 var next: usize = match.seg + 1;
4011 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4012 const next_seg = &self.load_commands.items[next].Segment;
4013 _ = try self.base.file.?.copyRangeAll(
4014 next_seg.inner.fileoff,
4015 self.base.file.?,
4016 next_seg.inner.fileoff + offset_amt,
4017 next_seg.inner.filesize,
4018 );
4019 next_seg.inner.fileoff += offset_amt;
4020 next_seg.inner.vmaddr += offset_amt;
4021 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4022 next_seg.inner.segname,
4023 next_seg.inner.fileoff,
4024 next_seg.inner.fileoff + next_seg.inner.filesize,
4025 next_seg.inner.vmaddr,
4026 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4027 });
4028
4029 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
4030 // TODO put below snippet in a function.
4031 const moved_sect_offset = blk: {
4032 if (self.data_segment_cmd_index.? == next) {
4033 if (self.bss_section_index) |idx| {
4034 if (idx == moved_sect_id) break :blk &self.bss_file_offset.?;
4035 }
4036 if (self.tlv_bss_section_index) |idx| {
4037 if (idx == moved_sect_id) break :blk &self.tlv_bss_file_offset.?;
4038 }
4039 }
4040 break :blk &moved_sect.offset;
4041 };
4042 moved_sect_offset.* += offset_amt;
4043 moved_sect.addr += offset_amt;
4044 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4045 commands.segmentName(moved_sect.*),
4046 commands.sectionName(moved_sect.*),
4047 moved_sect_offset.*,
4048 moved_sect_offset.* + moved_sect.size,
4049 moved_sect.addr,
4050 moved_sect.addr + moved_sect.size,
4051 });
4052
4053 try self.allocateLocalSymbols(.{
4054 .seg = @intCast(u16, next),
4055 .sect = @intCast(u16, moved_sect_id),
4056 }, offset_amt);
4057 }
4058 }
4059 }
4060
39084061 text_section.offset = new_offset;
39094062 text_section.addr = text_segment.inner.vmaddr + text_section.offset - text_segment.inner.fileoff;
39104063 log.debug(" (found new __TEXT,__text free space from 0x{x} to 0x{x})", .{
39114064 new_offset,
39124065 new_offset + needed_size,
39134066 });
4067 const offset_amt = @intCast(i64, text_section.addr) - @intCast(i64, old_base_addr);
39144068 try self.allocateLocalSymbols(.{
39154069 .seg = self.text_segment_cmd_index.?,
39164070 .sect = self.text_section_index.?,
3917 }, old_base_addr);
3918 vaddr = @intCast(
3919 u64,
3920 @intCast(i64, vaddr) + @intCast(i64, text_section.addr) - @intCast(i64, old_base_addr),
3921 );
4071 }, offset_amt);
4072 vaddr = @intCast(u64, @intCast(i64, vaddr) + offset_amt);
39224073 }
39234074
39244075 text_section.size = needed_size;