Commit 4eff0f4ea1

Jakub Konka <kubkon@jakubkonka.com>
2021-09-01 16:49:16
macho: fix condition for checking available size for an atom
1 parent 7a99cd0
Changed files (2)
src
src/link/MachO/commands.zig
@@ -246,10 +246,8 @@ pub const SegmentCommand = struct {
     }
 
     pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
-        assert(start > 0);
-        if (start == self.inner.fileoff)
-            return 0;
-        var min_pos: u64 = std.math.maxInt(u64);
+        assert(start >= self.inner.fileoff);
+        var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
         for (self.sections.items) |section| {
             if (section.offset <= start) continue;
             if (section.offset < min_pos) min_pos = section.offset;
src/link/MachO.zig
@@ -281,6 +281,10 @@ const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
 const minimum_text_block_size = 64;
 pub const min_text_capacity = padToIdeal(minimum_text_block_size);
 
+/// Virtual memory offset corresponds to the size of __PAGEZERO segment and start of
+/// __TEXT segment.
+const pagezero_vmsize: u64 = 0x100000000;
+
 pub const Export = struct {
     sym_index: ?u32 = null,
 };
@@ -1903,13 +1907,19 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
     const vaddr = outer: {
         if (!use_stage1) {
             const sym = &self.locals.items[atom.local_sym_index];
+            const needs_padding = blk: {
+                // TODO is __text the only section that benefits from padding?
+                if (match.seg == self.text_segment_cmd_index.? and
+                    match.sect == self.text_section_index.?) break :blk true;
+                break :blk false;
+            };
 
             var atom_placement: ?*TextBlock = null;
 
             // TODO converge with `allocateTextBlock` and handle free list
             const vaddr = if (self.blocks.get(match)) |last| blk: {
                 const last_atom_sym = self.locals.items[last.local_sym_index];
-                const ideal_capacity = padToIdeal(last.size);
+                const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
                 const ideal_capacity_end_vaddr = last_atom_sym.n_value + ideal_capacity;
                 const last_atom_alignment = try math.powi(u32, 2, atom.alignment);
                 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, last_atom_alignment);
@@ -1921,18 +1931,9 @@ pub fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64
 
             const expand_section = atom_placement == null or atom_placement.?.next == null;
             if (expand_section) {
-                const needed_size = (vaddr + atom.size) - sect.addr;
-                const end_addr = blk: {
-                    const next_ordinal = self.section_ordinals.getIndex(match).?; // Ordinals are +1 to begin with.
-                    const end_addr = if (self.section_ordinals.keys().len > next_ordinal) inner: {
-                        const next_match = self.section_ordinals.keys()[next_ordinal];
-                        const next_seg = self.load_commands.items[next_match.seg].Segment;
-                        const next_sect = next_seg.sections.items[next_match.sect];
-                        break :inner next_sect.addr;
-                    } else seg.inner.filesize;
-                    break :blk end_addr;
-                };
-                assert(needed_size <= end_addr); // TODO must expand the section
+                const max_size = seg.allocatedSize(vaddr - pagezero_vmsize);
+                log.debug("  (atom size 0x{x}, max available size 0x{x})", .{ atom.size, max_size });
+                assert(atom.size <= max_size); // TODO must expand the section
             }
             const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
             sym.n_value = vaddr;
@@ -3912,7 +3913,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
             .Segment = .{
                 .inner = .{
                     .segname = makeStaticString("__PAGEZERO"),
-                    .vmsize = 0x100000000, // size always set to 4GB
+                    .vmsize = pagezero_vmsize,
                 },
             },
         });
@@ -3932,7 +3933,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
             .Segment = .{
                 .inner = .{
                     .segname = makeStaticString("__TEXT"),
-                    .vmaddr = 0x100000000, // always starts at 4GB
+                    .vmaddr = pagezero_vmsize,
                     .vmsize = needed_size,
                     .filesize = needed_size,
                     .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
@@ -4452,8 +4453,6 @@ fn allocateSection(
         const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
         const off = seg.findFreeSpace(size, alignment_pow_2, padding);
 
-        assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
-
         log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
             commands.segmentName(sect),
             commands.sectionName(sect),
@@ -4556,7 +4555,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
     const expand_text_section = block_placement == null or block_placement.?.next == null;
     if (expand_text_section) {
         const needed_size = (vaddr + new_block_size) - text_section.addr;
-        assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section.
+        const max_size = text_segment.allocatedSize(vaddr - pagezero_vmsize);
+        log.debug("  (atom needed size 0x{x}, max available size 0x{x})", .{ needed_size, max_size });
+        assert(needed_size <= max_size); // TODO must expand the section
         _ = try self.blocks.put(self.base.allocator, match, text_block);
     }
     text_block.size = new_block_size;