From 8378cde74369ddb1cc618d444970e963a4ab1110 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 9 Sep 2022 00:01:20 +0200 Subject: [PATCH] macho: prefill any space between __DATA and __LINKEDIT with 0s if required If there are zerofill sections, the loader may copy the contents of the physical space in file directly into memory and attach that to the zerofill section. This is a performance optimisation in the loader but requires us, the linker, to properly zero-out any space between __DATA and __LINKEDIT segments in file. This is of course completely skipped if there are no zerofill sections present. --- src/link/MachO.zig | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ef180ab032..1ab0202b44 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1157,6 +1157,28 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) var ncmds: u32 = 0; try self.writeLinkeditSegmentData(&ncmds, lc_writer); + + // If the last section of __DATA segment is zerofill section, we need to ensure + // that the free space between the end of the last non-zerofill section of __DATA + // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will + // copy-paste this space into memory for quicker zerofill operation. + if (self.data_segment_cmd_index) |data_seg_id| blk: { + var physical_zerofill_start: u64 = 0; + const section_indexes = self.getSectionIndexes(data_seg_id); + for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| { + if (header.isZerofill() and header.size > 0) break; + physical_zerofill_start = header.offset + header.size; + } else break :blk; + const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?]; + const physical_zerofill_size = linkedit.fileoff - physical_zerofill_start; + if (physical_zerofill_size > 0) { + var padding = try self.base.allocator.alloc(u8, physical_zerofill_size); + defer self.base.allocator.free(padding); + mem.set(u8, padding, 0); + try self.base.file.?.pwriteAll(padding, physical_zerofill_start); + } + } + try writeDylinkerLC(&ncmds, lc_writer); try self.writeMainLC(&ncmds, lc_writer); try self.writeDylibIdLC(&ncmds, lc_writer); @@ -5690,8 +5712,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { else => unreachable, } - if (self.getSectionByName("__DATA", "__thread_vars")) |_| { - header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; + if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| { + if (self.sections.items(.header)[sect_id].size > 0) { + header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; + } } header.ncmds = ncmds;