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...@@ -1925,7 +1925,7 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
1925 var atom_placement: ?*TextBlock = null;1925 var atom_placement: ?*TextBlock = null;
19261926
1927 // TODO converge with `allocateTextBlock` and handle free list1927 // 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: {
1929 const last_atom_sym = self.locals.items[last.local_sym_index];1929 const last_atom_sym = self.locals.items[last.local_sym_index];
1930 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;1930 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
1931 const ideal_capacity_end_vaddr = last_atom_sym.n_value + ideal_capacity;1931 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...@@ -1939,6 +1939,7 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
19391939
1940 const expand_section = atom_placement == null or atom_placement.?.next == null;1940 const expand_section = atom_placement == null or atom_placement.?.next == null;
1941 if (expand_section) {1941 if (expand_section) {
1942 const needed_size = (vaddr + atom.size) - sect.addr;
1942 const sect_offset: u64 = blk: {1943 const sect_offset: u64 = blk: {
1943 if (self.data_segment_cmd_index.? == match.seg) {1944 if (self.data_segment_cmd_index.? == match.seg) {
1944 if (self.bss_section_index) |idx| {1945 if (self.bss_section_index) |idx| {
...@@ -1952,8 +1953,35 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64...@@ -1952,8 +1953,35 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
1952 };1953 };
1953 const file_offset = sect_offset + vaddr - sect.addr;1954 const file_offset = sect_offset + vaddr - sect.addr;
1954 const max_size = seg.allocatedSize(file_offset);1955 const max_size = seg.allocatedSize(file_offset);
1955 log.debug(" (atom size 0x{x}, max available size 0x{x})", .{ atom.size, max_size });1956 log.debug(" (section {s},{s} needed size 0x{x}, max available size 0x{x})", .{
1956 assert(atom.size <= max_size); // TODO must expand the section1957 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;
1957 }1985 }
1958 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);1986 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1959 sym.n_value = vaddr;1987 sym.n_value = vaddr;
...@@ -2017,6 +2045,32 @@ pub fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {...@@ -2017,6 +2045,32 @@ pub fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
2017 try self.writeLocalSymbol(atom.local_sym_index);2045 try self.writeLocalSymbol(atom.local_sym_index);
2018}2046}
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
2020fn allocateGlobalSymbols(self: *MachO) !void {2074fn allocateGlobalSymbols(self: *MachO) !void {
2021 // TODO should we do this in `allocateAtom` (or similar)? Then, we would need to2075 // TODO should we do this in `allocateAtom` (or similar)? Then, we would need to
2022 // store the link atom -> globals somewhere.2076 // store the link atom -> globals somewhere.
...@@ -3943,8 +3997,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -3943,8 +3997,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3943 if (self.text_segment_cmd_index == null) {3997 if (self.text_segment_cmd_index == null) {
3944 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);3998 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3945 const program_code_size_hint = self.base.options.program_code_size_hint;3999 const program_code_size_hint = self.base.options.program_code_size_hint;
4000 // const program_code_size_hint = 10;
3946 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;4001 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;
3948 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);4003 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
39494004
3950 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });4005 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 {...@@ -3971,6 +4026,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3971 else => unreachable, // unhandled architecture type4026 else => unreachable, // unhandled architecture type
3972 };4027 };
3973 const needed_size = self.base.options.program_code_size_hint;4028 const needed_size = self.base.options.program_code_size_hint;
4029 // const needed_size = 10;
3974 self.text_section_index = try self.allocateSection(4030 self.text_section_index = try self.allocateSection(
3975 self.text_segment_cmd_index.?,4031 self.text_segment_cmd_index.?,
3976 "__text",4032 "__text",
...@@ -4521,7 +4577,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -4521,7 +4577,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
45214577
4522 // First we look for an appropriately sized free list node.4578 // First we look for an appropriately sized free list node.
4523 // The list is unordered. We'll just take the first thing that works.4579 // The list is unordered. We'll just take the first thing that works.
4524 const vaddr = blk: {4580 var vaddr = blk: {
4525 var i: usize = 0;4581 var i: usize = 0;
4526 while (i < text_block_free_list.items.len) {4582 while (i < text_block_free_list.items.len) {
4527 const big_block = text_block_free_list.items[i];4583 const big_block = text_block_free_list.items[i];
...@@ -4576,8 +4632,31 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -4576,8 +4632,31 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4576 if (expand_text_section) {4632 if (expand_text_section) {
4577 const needed_size = (vaddr + new_block_size) - text_section.addr;4633 const needed_size = (vaddr + new_block_size) - text_section.addr;
4578 const max_size = text_segment.allocatedSize(vaddr - pagezero_vmsize);4634 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 });4635 log.debug(" (section __TEXT,__text needed size 0x{x}, max available size 0x{x})", .{ needed_size, max_size });
4580 assert(needed_size <= max_size); // TODO must expand the section4636
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
4581 _ = try self.blocks.put(self.base.allocator, match, text_block);4660 _ = try self.blocks.put(self.base.allocator, match, text_block);
4582 }4661 }
4583 text_block.size = new_block_size;4662 text_block.size = new_block_size;
src/link/MachO/commands.zig+1-1
...@@ -267,7 +267,7 @@ pub const SegmentCommand = struct {...@@ -267,7 +267,7 @@ pub const SegmentCommand = struct {
267 return null;267 return null;
268 }268 }
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 {
271 var offset: u64 = if (start) |v| v else self.inner.fileoff;271 var offset: u64 = if (start) |v| v else self.inner.fileoff;
272 while (self.detectAllocCollision(offset, object_size)) |item_end| {272 while (self.detectAllocCollision(offset, object_size)) |item_end| {
273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);