authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-02 18:19:07+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-02 18:19:07+02:00
log5af13f35f98c75d063cadf5e5607d066cbdb3e0e
tree7b6a145530fd88918396a61f2c9fece2d91dd749
parent4741c04254790fedcebdce7d13b27c6ba31ac412

macho: implement basic section movement and reallocation


2 files changed, 87 insertions(+), 8 deletions(-)

src/link/MachO.zig+86-7
......@@ -1925,7 +1925,7 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
19251925 var atom_placement: ?*TextBlock = null;
19261926
19271927 // TODO converge with `allocateTextBlock` and handle free list
1928 const vaddr = if (self.blocks.get(match)) |last| blk: {
1928 var vaddr = if (self.blocks.get(match)) |last| blk: {
19291929 const last_atom_sym = self.locals.items[last.local_sym_index];
19301930 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
19311931 const ideal_capacity_end_vaddr = last_atom_sym.n_value + ideal_capacity;
......@@ -1939,6 +1939,7 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
19391939
19401940 const expand_section = atom_placement == null or atom_placement.?.next == null;
19411941 if (expand_section) {
1942 const needed_size = (vaddr + atom.size) - sect.addr;
19421943 const sect_offset: u64 = blk: {
19431944 if (self.data_segment_cmd_index.? == match.seg) {
19441945 if (self.bss_section_index) |idx| {
......@@ -1952,8 +1953,35 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
19521953 };
19531954 const file_offset = sect_offset + vaddr - sect.addr;
19541955 const max_size = seg.allocatedSize(file_offset);
1955 log.debug(" (atom size 0x{x}, max available size 0x{x})", .{ atom.size, max_size });
1956 assert(atom.size <= max_size); // TODO must expand the section
1956 log.debug(" (section {s},{s} needed size 0x{x}, max available size 0x{x})", .{
1957 commands.segmentName(sect.*),
1958 commands.sectionName(sect.*),
1959 needed_size,
1960 max_size,
1961 });
1962
1963 if (needed_size > max_size) {
1964 const old_base_addr = sect.addr;
1965 sect.size = 0;
1966 const padding: ?u64 = if (match.seg == self.text_segment_cmd_index.?) self.header_pad else null;
1967 const new_offset = @intCast(u32, seg.findFreeSpace(needed_size, atom.alignment, padding));
1968 sect.offset = new_offset;
1969 sect.addr = seg.inner.vmaddr + sect.offset - seg.inner.fileoff;
1970 log.debug(" (found new {s},{s} free space from 0x{x} to 0x{x})", .{
1971 commands.segmentName(sect.*),
1972 commands.sectionName(sect.*),
1973 new_offset,
1974 new_offset + needed_size,
1975 });
1976 try self.allocateLocalSymbols(match, old_base_addr);
1977 vaddr = @intCast(
1978 u64,
1979 @intCast(i64, vaddr) + @intCast(i64, sect.addr) - @intCast(i64, old_base_addr),
1980 );
1981 }
1982
1983 sect.size = needed_size;
1984 self.load_commands_dirty = true;
19571985 }
19581986 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
19591987 sym.n_value = vaddr;
......@@ -2017,6 +2045,32 @@ pub fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
20172045 try self.writeLocalSymbol(atom.local_sym_index);
20182046}
20192047
2048fn allocateLocalSymbols(self: *MachO, match: MatchingSection, old_base_addr: u64) !void {
2049 var atom = self.blocks.get(match) orelse return;
2050 const seg = self.load_commands.items[match.seg].Segment;
2051 const sect = seg.sections.items[match.sect];
2052 const offset = @intCast(i64, sect.addr) - @intCast(i64, old_base_addr);
2053
2054 while (true) {
2055 const atom_sym = &self.locals.items[atom.local_sym_index];
2056 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
2057
2058 for (atom.aliases.items) |index| {
2059 const alias_sym = &self.locals.items[index];
2060 alias_sym.n_value = @intCast(u64, @intCast(i64, alias_sym.n_value) + offset);
2061 }
2062
2063 for (atom.contained.items) |sym_at_off| {
2064 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
2065 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2066 }
2067
2068 if (atom.prev) |prev| {
2069 atom = prev;
2070 } else break;
2071 }
2072}
2073
20202074fn allocateGlobalSymbols(self: *MachO) !void {
20212075 // TODO should we do this in `allocateAtom` (or similar)? Then, we would need to
20222076 // store the link atom -> globals somewhere.
......@@ -3943,8 +3997,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
39433997 if (self.text_segment_cmd_index == null) {
39443998 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
39453999 const program_code_size_hint = self.base.options.program_code_size_hint;
4000 // const program_code_size_hint = 10;
39464001 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
3947 const ideal_size = self.header_pad + program_code_size_hint + 3 * got_size_hint;
4002 const ideal_size = self.header_pad + program_code_size_hint + got_size_hint;
39484003 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
39494004
39504005 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
......@@ -3971,6 +4026,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
39714026 else => unreachable, // unhandled architecture type
39724027 };
39734028 const needed_size = self.base.options.program_code_size_hint;
4029 // const needed_size = 10;
39744030 self.text_section_index = try self.allocateSection(
39754031 self.text_segment_cmd_index.?,
39764032 "__text",
......@@ -4521,7 +4577,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
45214577
45224578 // First we look for an appropriately sized free list node.
45234579 // The list is unordered. We'll just take the first thing that works.
4524 const vaddr = blk: {
4580 var vaddr = blk: {
45254581 var i: usize = 0;
45264582 while (i < text_block_free_list.items.len) {
45274583 const big_block = text_block_free_list.items[i];
......@@ -4576,8 +4632,31 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
45764632 if (expand_text_section) {
45774633 const needed_size = (vaddr + new_block_size) - text_section.addr;
45784634 const max_size = text_segment.allocatedSize(vaddr - pagezero_vmsize);
4579 log.debug(" (atom needed size 0x{x}, max available size 0x{x})", .{ needed_size, max_size });
4580 assert(needed_size <= max_size); // TODO must expand the section
4635 log.debug(" (section __TEXT,__text needed size 0x{x}, max available size 0x{x})", .{ needed_size, max_size });
4636
4637 if (needed_size > max_size) {
4638 const old_base_addr = text_section.addr;
4639 text_section.size = 0;
4640 const new_offset = @intCast(u32, text_segment.findFreeSpace(needed_size, alignment, self.header_pad));
4641 text_section.offset = new_offset;
4642 text_section.addr = text_segment.inner.vmaddr + text_section.offset - text_segment.inner.fileoff;
4643 log.debug(" (found new __TEXT,__text free space from 0x{x} to 0x{x})", .{
4644 new_offset,
4645 new_offset + needed_size,
4646 });
4647 try self.allocateLocalSymbols(.{
4648 .seg = self.text_segment_cmd_index.?,
4649 .sect = self.text_section_index.?,
4650 }, old_base_addr);
4651 vaddr = @intCast(
4652 u64,
4653 @intCast(i64, vaddr) + @intCast(i64, text_section.addr) - @intCast(i64, old_base_addr),
4654 );
4655 }
4656
4657 text_section.size = needed_size;
4658 self.load_commands_dirty = true;
4659
45814660 _ = try self.blocks.put(self.base.allocator, match, text_block);
45824661 }
45834662 text_block.size = new_block_size;
src/link/MachO/commands.zig+1-1
......@@ -267,7 +267,7 @@ pub const SegmentCommand = struct {
267267 return null;
268268 }
269269
270 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u32, start: ?u64) u64 {
270 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
271271 var offset: u64 = if (start) |v| v else self.inner.fileoff;
272272 while (self.detectAllocCollision(offset, object_size)) |item_end| {
273273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);