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.
This commit is contained in:
Jakub Konka 2022-09-09 00:01:20 +02:00
parent 0ae2ea671b
commit 8378cde743

View File

@ -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;