diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-21 15:59:20 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-21 20:22:52 +0200 |
| commit | affd8f8b59d9d803f98178ec32ab9e2f6b0b30d2 (patch) | |
| tree | 3a259023be3fd6454aa3d28433a75f2d03f8bc62 /src | |
| parent | a2dd0c387dd9e08c0147490b0667146758b6a43b (diff) | |
| download | zig-affd8f8b59d9d803f98178ec32ab9e2f6b0b30d2.tar.gz zig-affd8f8b59d9d803f98178ec32ab9e2f6b0b30d2.zip | |
macho: fix incorrect segment/section growth calculation
Otherwise, for last sections in segments it could happen we would
not expand the segment when actually required thus exceeding the
segment's size and causing data clobbering and dyld runtime errors.
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 135 |
1 files changed, 73 insertions, 62 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e803dc277a..93b3cc7e93 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -4085,6 +4085,70 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 return mem.alignForwardGeneric(u64, final_off, alignment); } +fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void { + const seg = &self.load_commands.items[seg_id].Segment; + const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size); + assert(new_seg_size > seg.inner.filesize); + const offset_amt = new_seg_size - seg.inner.filesize; + log.debug("growing segment {s} from 0x{x} to 0x{x}", .{ seg.inner.segname, seg.inner.filesize, new_seg_size }); + seg.inner.filesize = new_seg_size; + seg.inner.vmsize = new_seg_size; + + log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ + seg.inner.fileoff, + seg.inner.fileoff + seg.inner.filesize, + seg.inner.vmaddr, + seg.inner.vmaddr + seg.inner.vmsize, + }); + + // TODO We should probably nop the expanded by distance, or put 0s. + + // TODO copyRangeAll doesn't automatically extend the file on macOS. + const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize; + try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1); + + var next: usize = seg_id + 1; + while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) { + const next_seg = &self.load_commands.items[next].Segment; + _ = try self.base.file.?.copyRangeAll( + next_seg.inner.fileoff, + self.base.file.?, + next_seg.inner.fileoff + offset_amt, + next_seg.inner.filesize, + ); + next_seg.inner.fileoff += offset_amt; + next_seg.inner.vmaddr += offset_amt; + + log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ + next_seg.inner.segname, + next_seg.inner.fileoff, + next_seg.inner.fileoff + next_seg.inner.filesize, + next_seg.inner.vmaddr, + next_seg.inner.vmaddr + next_seg.inner.vmsize, + }); + + for (next_seg.sections.items) |*moved_sect, moved_sect_id| { + moved_sect.offset += @intCast(u32, offset_amt); + moved_sect.addr += offset_amt; + + log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ + commands.segmentName(moved_sect.*), + commands.sectionName(moved_sect.*), + moved_sect.offset, + moved_sect.offset + moved_sect.size, + moved_sect.addr, + moved_sect.addr + moved_sect.size, + }); + + try self.allocateLocalSymbols(.{ + .seg = @intCast(u16, next), + .sect = @intCast(u16, moved_sect_id), + }, @intCast(i64, offset_amt)); + } + } +} + fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -4098,7 +4162,14 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment); if (needed_size > max_size) blk: { - log.debug(" (need to grow!)", .{}); + log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size }); + + if (match.sect == seg.sections.items.len - 1) { + // Last section, just grow segments + try self.growSegment(match.seg, seg.inner.filesize + needed_size - max_size); + break :blk; + } + // Need to move all sections below in file and address spaces. const offset_amt = offset: { const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1); @@ -4114,70 +4185,10 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { if (last_sect_off + offset_amt > seg_off) { // Need to grow segment first. - log.debug(" (need to grow segment first)", .{}); const spill_size = (last_sect_off + offset_amt) - seg_off; - const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size); - seg.inner.filesize += seg_offset_amt; - seg.inner.vmsize += seg_offset_amt; - - log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ - seg.inner.segname, - seg.inner.fileoff, - seg.inner.fileoff + seg.inner.filesize, - seg.inner.vmaddr, - seg.inner.vmaddr + seg.inner.vmsize, - }); - - // TODO We should probably nop the expanded by distance, or put 0s. - - // TODO copyRangeAll doesn't automatically extend the file on macOS. - const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize; - try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1); - - var next: usize = match.seg + 1; - while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) { - const next_seg = &self.load_commands.items[next].Segment; - _ = try self.base.file.?.copyRangeAll( - next_seg.inner.fileoff, - self.base.file.?, - next_seg.inner.fileoff + seg_offset_amt, - next_seg.inner.filesize, - ); - next_seg.inner.fileoff += seg_offset_amt; - next_seg.inner.vmaddr += seg_offset_amt; - - log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ - next_seg.inner.segname, - next_seg.inner.fileoff, - next_seg.inner.fileoff + next_seg.inner.filesize, - next_seg.inner.vmaddr, - next_seg.inner.vmaddr + next_seg.inner.vmsize, - }); - - for (next_seg.sections.items) |*moved_sect, moved_sect_id| { - moved_sect.offset += @intCast(u32, seg_offset_amt); - moved_sect.addr += seg_offset_amt; - - log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ - commands.segmentName(moved_sect.*), - commands.sectionName(moved_sect.*), - moved_sect.offset, - moved_sect.offset + moved_sect.size, - moved_sect.addr, - moved_sect.addr + moved_sect.size, - }); - - try self.allocateLocalSymbols(.{ - .seg = @intCast(u16, next), - .sect = @intCast(u16, moved_sect_id), - }, @intCast(i64, seg_offset_amt)); - } - } + try self.growSegment(match.seg, seg.inner.filesize + spill_size); } - if (match.sect + 1 >= seg.sections.items.len) break :blk; - // We have enough space to expand within the segment, so move all sections by // the required amount and update their header offsets. const next_sect = seg.sections.items[match.sect + 1]; |
