authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-06 16:46:48+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-06 17:31:20+02:00
log29d2e19c3ea50c9caec036b52e978297a04bb969
tree618a6dd0d7d13c3057cbe5e326ff31620676bded
parent2914ea9e3388da2bf0240b9bd6b0474f9686322c

macho: allocate sections one after the other and grow if needed


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

src/link/MachO.zig+171-201
...@@ -1647,110 +1647,12 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64...@@ -1647,110 +1647,12 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
1647 break :blk new_start_vaddr;1647 break :blk new_start_vaddr;
1648 } else sect.addr;1648 } else sect.addr;
16491649
1650 log.debug("allocating atom for symbol {s} at address 0x{x}", .{ self.getString(sym.n_strx), vaddr });1650 log.warn("allocating atom for symbol {s} at address 0x{x}", .{ self.getString(sym.n_strx), vaddr });
16511651
1652 const expand_section = atom_placement == null or atom_placement.?.next == null;1652 const expand_section = atom_placement == null or atom_placement.?.next == null;
1653 if (expand_section) {1653 if (expand_section) {
1654 const needed_size = (vaddr + atom.size) - sect.addr;1654 const needed_size = @intCast(u32, (vaddr + atom.size) - sect.addr);
1655 const sect_offset = self.getPtrToSectionOffset(match);1655 try self.growSection(match, needed_size);
1656 const file_offset = sect_offset.* + vaddr - sect.addr;
1657 const max_size = seg.allocatedSize(file_offset);
1658 log.debug(" (section {s},{s} needed size 0x{x}, max available size 0x{x})", .{
1659 commands.segmentName(sect.*),
1660 commands.sectionName(sect.*),
1661 needed_size,
1662 max_size,
1663 });
1664
1665 if (needed_size > max_size) {
1666 const old_base_addr = sect.addr;
1667 sect.size = 0;
1668 const padding: ?u64 = if (match.seg == self.text_segment_cmd_index.?) self.header_pad else null;
1669 const atom_alignment = try math.powi(u64, 2, atom.alignment);
1670 const new_offset = @intCast(u32, seg.findFreeSpace(needed_size, atom_alignment, padding));
1671
1672 if (new_offset + needed_size >= seg.inner.fileoff + seg.inner.filesize) {
1673 // Bummer, need to move all segments below down...
1674 // TODO is this the right estimate?
1675 const new_seg_size = mem.alignForwardGeneric(
1676 u64,
1677 padToIdeal(seg.inner.filesize + needed_size),
1678 self.page_size,
1679 );
1680 // TODO actually, we're always required to move in a number of pages so I guess all we need
1681 // to know here is the number of pages to shift downwards.
1682 const offset_amt = @intCast(u32, @intCast(i64, new_seg_size) - @intCast(i64, seg.inner.filesize));
1683 seg.inner.filesize = new_seg_size;
1684 seg.inner.vmsize = new_seg_size;
1685 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1686 seg.inner.segname,
1687 seg.inner.fileoff,
1688 seg.inner.fileoff + seg.inner.filesize,
1689 seg.inner.vmaddr,
1690 seg.inner.vmaddr + seg.inner.vmsize,
1691 });
1692 // TODO We should probably nop the expanded by distance, or put 0s.
1693
1694 // TODO copyRangeAll doesn't automatically extend the file on macOS.
1695 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1696 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
1697 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
1698
1699 var next: usize = match.seg + 1;
1700 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
1701 const next_seg = &self.load_commands.items[next].Segment;
1702 _ = try self.base.file.?.copyRangeAll(
1703 next_seg.inner.fileoff,
1704 self.base.file.?,
1705 next_seg.inner.fileoff + offset_amt,
1706 next_seg.inner.filesize,
1707 );
1708 next_seg.inner.fileoff += offset_amt;
1709 next_seg.inner.vmaddr += offset_amt;
1710 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1711 next_seg.inner.segname,
1712 next_seg.inner.fileoff,
1713 next_seg.inner.fileoff + next_seg.inner.filesize,
1714 next_seg.inner.vmaddr,
1715 next_seg.inner.vmaddr + next_seg.inner.vmsize,
1716 });
1717
1718 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
1719 const moved_match = MatchingSection{
1720 .seg = @intCast(u16, next),
1721 .sect = @intCast(u16, moved_sect_id),
1722 };
1723 const ptr_sect_offset = self.getPtrToSectionOffset(moved_match);
1724 ptr_sect_offset.* += offset_amt;
1725 moved_sect.addr += offset_amt;
1726 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
1727 commands.segmentName(moved_sect.*),
1728 commands.sectionName(moved_sect.*),
1729 ptr_sect_offset.*,
1730 ptr_sect_offset.* + moved_sect.size,
1731 moved_sect.addr,
1732 moved_sect.addr + moved_sect.size,
1733 });
1734
1735 try self.allocateLocalSymbols(moved_match, offset_amt);
1736 }
1737 }
1738 }
1739 sect_offset.* = new_offset;
1740 sect.addr = seg.inner.vmaddr + sect_offset.* - seg.inner.fileoff;
1741 log.debug(" (found new {s},{s} free space from 0x{x} to 0x{x})", .{
1742 commands.segmentName(sect.*),
1743 commands.sectionName(sect.*),
1744 new_offset,
1745 new_offset + needed_size,
1746 });
1747 const offset_amt = @intCast(i64, sect.addr) - @intCast(i64, old_base_addr);
1748 try self.allocateLocalSymbols(match, offset_amt);
1749 vaddr = @intCast(u64, @intCast(i64, vaddr) + offset_amt);
1750 }
1751
1752 sect.size = needed_size;
1753 self.load_commands_dirty = true;
1754 }1656 }
1755 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);1657 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1756 sym.n_value = vaddr;1658 sym.n_value = vaddr;
...@@ -3373,7 +3275,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -3373,7 +3275,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3373 else => unreachable, // unhandled architecture type3275 else => unreachable, // unhandled architecture type
3374 };3276 };
3375 const needed_size = self.base.options.program_code_size_hint;3277 const needed_size = self.base.options.program_code_size_hint;
3376 // const needed_size = 10;
3377 self.text_section_index = try self.allocateSection(3278 self.text_section_index = try self.allocateSection(
3378 self.text_segment_cmd_index.?,3279 self.text_segment_cmd_index.?,
3379 "__text",3280 "__text",
...@@ -3821,9 +3722,9 @@ fn allocateSection(...@@ -3821,9 +3722,9 @@ fn allocateSection(
38213722
3822 const alignment_pow_2 = try math.powi(u32, 2, alignment);3723 const alignment_pow_2 = try math.powi(u32, 2, alignment);
3823 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;3724 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
3824 const off = seg.findFreeSpace(size, alignment_pow_2, padding);3725 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
38253726
3826 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{3727 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
3827 commands.segmentName(sect),3728 commands.segmentName(sect),
3828 commands.sectionName(sect),3729 commands.sectionName(sect),
3829 off,3730 off,
...@@ -3851,6 +3752,170 @@ fn allocateSection(...@@ -3851,6 +3752,170 @@ fn allocateSection(
3851 return index;3752 return index;
3852}3753}
38533754
3755fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {
3756 const seg = self.load_commands.items[segment_id].Segment;
3757 if (seg.sections.items.len == 0) {
3758 return if (start) |v| v else seg.inner.fileoff;
3759 }
3760 const last_sect = seg.sections.items[seg.sections.items.len - 1];
3761 const final_off = last_sect.offset + padToIdeal(last_sect.size);
3762 return mem.alignForwardGeneric(u64, final_off, alignment);
3763}
3764
3765fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
3766 const seg = &self.load_commands.items[match.seg].Segment;
3767 const sect = &seg.sections.items[match.sect];
3768
3769 const alignment = try math.powi(u32, 2, sect.@"align");
3770 const sect_offset = self.getPtrToSectionOffset(match);
3771 const max_size = self.allocatedSize(match.seg, sect_offset.*);
3772 const ideal_size = padToIdeal(new_size);
3773 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
3774
3775 if (needed_size > max_size) blk: {
3776 // Need to move all sections below in file and address spaces.
3777 const offset_amt = offset: {
3778 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);
3779 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
3780 };
3781
3782 // Before we commit to this, check if the segment needs to grow too.
3783 // We assume that each section header is growing linearly with the increasing
3784 // file offset / virtual memory address space.
3785 const last_sect = seg.sections.items[seg.sections.items.len - 1];
3786 const last_sect_off = last_sect.offset + last_sect.size;
3787 const seg_off = seg.inner.fileoff + seg.inner.filesize;
3788
3789 if (last_sect_off + offset_amt > seg_off) {
3790 // Need to grow segment first.
3791 log.warn(" (need to grow segment first)", .{});
3792 const spill_size = (last_sect_off + offset_amt) - seg_off;
3793 const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size);
3794 seg.inner.filesize += seg_offset_amt;
3795 seg.inner.vmsize += seg_offset_amt;
3796
3797 log.warn(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3798 seg.inner.segname,
3799 seg.inner.fileoff,
3800 seg.inner.fileoff + seg.inner.filesize,
3801 seg.inner.vmaddr,
3802 seg.inner.vmaddr + seg.inner.vmsize,
3803 });
3804
3805 // TODO We should probably nop the expanded by distance, or put 0s.
3806
3807 // TODO copyRangeAll doesn't automatically extend the file on macOS.
3808 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3809 const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
3810 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
3811
3812 var next: usize = match.seg + 1;
3813 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
3814 const next_seg = &self.load_commands.items[next].Segment;
3815 _ = try self.base.file.?.copyRangeAll(
3816 next_seg.inner.fileoff,
3817 self.base.file.?,
3818 next_seg.inner.fileoff + seg_offset_amt,
3819 next_seg.inner.filesize,
3820 );
3821 next_seg.inner.fileoff += seg_offset_amt;
3822 next_seg.inner.vmaddr += seg_offset_amt;
3823
3824 log.warn(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3825 next_seg.inner.segname,
3826 next_seg.inner.fileoff,
3827 next_seg.inner.fileoff + next_seg.inner.filesize,
3828 next_seg.inner.vmaddr,
3829 next_seg.inner.vmaddr + next_seg.inner.vmsize,
3830 });
3831
3832 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
3833 const moved_match = MatchingSection{
3834 .seg = @intCast(u16, next),
3835 .sect = @intCast(u16, moved_sect_id),
3836 };
3837 const ptr_sect_offset = self.getPtrToSectionOffset(moved_match);
3838 ptr_sect_offset.* += @intCast(u32, seg_offset_amt);
3839 moved_sect.addr += seg_offset_amt;
3840
3841 log.warn(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3842 commands.segmentName(moved_sect.*),
3843 commands.sectionName(moved_sect.*),
3844 ptr_sect_offset.*,
3845 ptr_sect_offset.* + moved_sect.size,
3846 moved_sect.addr,
3847 moved_sect.addr + moved_sect.size,
3848 });
3849
3850 try self.allocateLocalSymbols(moved_match, @intCast(i64, seg_offset_amt));
3851 }
3852 }
3853 }
3854
3855 if (match.sect + 1 >= seg.sections.items.len) break :blk;
3856
3857 // We have enough space to expand within the segment, so move all sections by
3858 // the required amount and update their header offsets.
3859 const next_sect = seg.sections.items[match.sect + 1];
3860 const total_size = last_sect_off - next_sect.offset;
3861 _ = try self.base.file.?.copyRangeAll(
3862 next_sect.offset,
3863 self.base.file.?,
3864 next_sect.offset + offset_amt,
3865 total_size,
3866 );
3867
3868 var next = match.sect + 1;
3869 while (next < seg.sections.items.len) : (next += 1) {
3870 const moved_match = MatchingSection{
3871 .seg = match.seg,
3872 .sect = next,
3873 };
3874 const moved_sect = &seg.sections.items[next];
3875 const ptr_sect_offset = self.getPtrToSectionOffset(moved_match);
3876 ptr_sect_offset.* += @intCast(u32, offset_amt);
3877 moved_sect.addr += offset_amt;
3878
3879 log.warn(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3880 commands.segmentName(moved_sect.*),
3881 commands.sectionName(moved_sect.*),
3882 ptr_sect_offset.*,
3883 ptr_sect_offset.* + moved_sect.size,
3884 moved_sect.addr,
3885 moved_sect.addr + moved_sect.size,
3886 });
3887
3888 try self.allocateLocalSymbols(moved_match, @intCast(i64, offset_amt));
3889 }
3890 }
3891
3892 sect.size = new_size;
3893 self.load_commands_dirty = true;
3894}
3895
3896fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
3897 const seg = self.load_commands.items[segment_id].Segment;
3898 assert(start >= seg.inner.fileoff);
3899 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
3900 for (seg.sections.items) |section| {
3901 if (section.offset <= start) continue;
3902 if (section.offset < min_pos) min_pos = section.offset;
3903 }
3904 return min_pos - start;
3905}
3906
3907fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
3908 const seg = self.load_commands.items[segment_id].Segment;
3909 var max_alignment: u32 = 1;
3910 var next = start_sect_id;
3911 while (next < seg.sections.items.len) : (next += 1) {
3912 const sect = seg.sections.items[next];
3913 const alignment = try math.powi(u32, 2, sect.@"align");
3914 max_alignment = math.max(max_alignment, alignment);
3915 }
3916 return max_alignment;
3917}
3918
3854fn getPtrToSectionOffset(self: *MachO, match: MatchingSection) *u32 {3919fn getPtrToSectionOffset(self: *MachO, match: MatchingSection) *u32 {
3855 if (self.data_segment_cmd_index.? == match.seg) {3920 if (self.data_segment_cmd_index.? == match.seg) {
3856 if (self.bss_section_index) |idx| {3921 if (self.bss_section_index) |idx| {
...@@ -3943,103 +4008,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -3943,103 +4008,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
39434008
3944 const expand_text_section = block_placement == null or block_placement.?.next == null;4009 const expand_text_section = block_placement == null or block_placement.?.next == null;
3945 if (expand_text_section) {4010 if (expand_text_section) {
3946 const needed_size = (vaddr + new_block_size) - text_section.addr;4011 const needed_size = @intCast(u32, (vaddr + new_block_size) - text_section.addr);
3947 const max_size = text_segment.allocatedSize(vaddr - pagezero_vmsize);4012 try self.growSection(match, needed_size);
3948 log.debug(" (section __TEXT,__text needed size 0x{x}, max available size 0x{x})", .{ needed_size, max_size });
3949
3950 if (needed_size > max_size) {
3951 const old_base_addr = text_section.addr;
3952 text_section.size = 0;
3953 const new_offset = @intCast(u32, text_segment.findFreeSpace(needed_size, alignment, self.header_pad));
3954
3955 if (new_offset + needed_size >= text_segment.inner.fileoff + text_segment.inner.filesize) {
3956 // Bummer, need to move all segments below down...
3957 // TODO is this the right estimate?
3958 const new_seg_size = mem.alignForwardGeneric(
3959 u64,
3960 padToIdeal(text_segment.inner.filesize + needed_size),
3961 self.page_size,
3962 );
3963 // TODO actually, we're always required to move in a number of pages so I guess all we need
3964 // to know here is the number of pages to shift downwards.
3965 const offset_amt = @intCast(
3966 u32,
3967 @intCast(i64, new_seg_size) - @intCast(i64, text_segment.inner.filesize),
3968 );
3969 text_segment.inner.filesize = new_seg_size;
3970 text_segment.inner.vmsize = new_seg_size;
3971 log.debug(" (new __TEXT segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3972 text_segment.inner.fileoff,
3973 text_segment.inner.fileoff + text_segment.inner.filesize,
3974 text_segment.inner.vmaddr,
3975 text_segment.inner.vmaddr + text_segment.inner.vmsize,
3976 });
3977 // TODO We should probably nop the expanded by distance, or put 0s.
3978
3979 // TODO copyRangeAll doesn't automatically extend the file on macOS.
3980 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3981 const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
3982 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
3983
3984 var next: usize = match.seg + 1;
3985 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
3986 const next_seg = &self.load_commands.items[next].Segment;
3987 _ = try self.base.file.?.copyRangeAll(
3988 next_seg.inner.fileoff,
3989 self.base.file.?,
3990 next_seg.inner.fileoff + offset_amt,
3991 next_seg.inner.filesize,
3992 );
3993 next_seg.inner.fileoff += offset_amt;
3994 next_seg.inner.vmaddr += offset_amt;
3995 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
3996 next_seg.inner.segname,
3997 next_seg.inner.fileoff,
3998 next_seg.inner.fileoff + next_seg.inner.filesize,
3999 next_seg.inner.vmaddr,
4000 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4001 });
4002
4003 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
4004 // TODO put below snippet in a function.
4005 const moved_match = MatchingSection{
4006 .seg = @intCast(u16, next),
4007 .sect = @intCast(u16, moved_sect_id),
4008 };
4009 const ptr_sect_offset = self.getPtrToSectionOffset(moved_match);
4010 ptr_sect_offset.* += offset_amt;
4011 moved_sect.addr += offset_amt;
4012 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4013 commands.segmentName(moved_sect.*),
4014 commands.sectionName(moved_sect.*),
4015 ptr_sect_offset.*,
4016 ptr_sect_offset.* + moved_sect.size,
4017 moved_sect.addr,
4018 moved_sect.addr + moved_sect.size,
4019 });
4020
4021 try self.allocateLocalSymbols(moved_match, offset_amt);
4022 }
4023 }
4024 }
4025
4026 text_section.offset = new_offset;
4027 text_section.addr = text_segment.inner.vmaddr + text_section.offset - text_segment.inner.fileoff;
4028 log.debug(" (found new __TEXT,__text free space from 0x{x} to 0x{x})", .{
4029 new_offset,
4030 new_offset + needed_size,
4031 });
4032 const offset_amt = @intCast(i64, text_section.addr) - @intCast(i64, old_base_addr);
4033 try self.allocateLocalSymbols(.{
4034 .seg = self.text_segment_cmd_index.?,
4035 .sect = self.text_section_index.?,
4036 }, offset_amt);
4037 vaddr = @intCast(u64, @intCast(i64, vaddr) + offset_amt);
4038 }
4039
4040 text_section.size = needed_size;
4041 self.load_commands_dirty = true;
4042
4043 _ = try self.blocks.put(self.base.allocator, match, text_block);4013 _ = try self.blocks.put(self.base.allocator, match, text_block);
4044 }4014 }
4045 text_block.size = new_block_size;4015 text_block.size = new_block_size;