authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-02 19:53:14+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-03 09:28:12+01:00
logc5155170b2f91ba4cba2ac356ffa749c1f30f621
tree0034aa2e6be7a434858a78062543d2e4757ecdee
parent88a4bd6cf63b44b1992ffb6f1b47c287242c07c0

macho: allocating space in .o


2 files changed, 102 insertions(+), 48 deletions(-)

src/link/MachO.zig+10-6
......@@ -3299,7 +3299,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
32993299 return min_pos - start;
33003300}
33013301
3302fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3302pub fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
33033303 var start: u64 = 0;
33043304 while (self.detectAllocCollision(start, object_size)) |item_end| {
33053305 start = mem.alignForward(u64, item_end, min_alignment);
......@@ -3307,18 +3307,22 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
33073307 return start;
33083308}
33093309
3310pub fn copyRangeAll(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
3311 const file = self.base.file.?;
3312 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3313 if (amt != size) return error.InputOutput;
3314}
3315
33103316/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.
33113317/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.
33123318fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
33133319 const gpa = self.base.comp.gpa;
3314 const file = self.base.file.?;
3315 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3316 if (amt != size) return error.InputOutput;
3320 try self.copyRangeAll(old_offset, new_offset, size);
33173321 const size_u = math.cast(usize, size) orelse return error.Overflow;
33183322 const zeroes = try gpa.alloc(u8, size_u);
33193323 defer gpa.free(zeroes);
33203324 @memset(zeroes, 0);
3321 try file.pwriteAll(zeroes, old_offset);
3325 try self.base.file.?.pwriteAll(zeroes, old_offset);
33223326}
33233327
33243328const InitMetadataOptions = struct {
......@@ -4064,7 +4068,7 @@ fn formatSections(
40644068 const slice = self.sections.slice();
40654069 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
40664070 try writer.print("sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x})\n", .{
4067 i, seg_id, header.segName(), header.sectName(), header.offset, header.addr,
4071 i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,
40684072 header.@"align", header.size,
40694073 });
40704074 }
src/link/MachO/relocatable.zig+92-42
......@@ -58,46 +58,20 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
5858 try macho_file.addAtomsToSections();
5959 try calcSectionSizes(macho_file);
6060
61 {
62 // For relocatable, we only ever need a single segment so create it now.
63 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;
64 try macho_file.segments.append(gpa, .{
65 .cmdsize = @sizeOf(macho.segment_command_64),
66 .segname = MachO.makeStaticString(""),
67 .maxprot = prot,
68 .initprot = prot,
69 });
70 const seg = &macho_file.segments.items[0];
71 seg.nsects = @intCast(macho_file.sections.items(.header).len);
72 seg.cmdsize += seg.nsects * @sizeOf(macho.section_64);
73 }
74
75 var off = try allocateSections(macho_file);
76
77 {
78 // Allocate the single segment.
79 assert(macho_file.segments.items.len == 1);
80 const seg = &macho_file.segments.items[0];
81 var vmaddr: u64 = 0;
82 var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
83 seg.vmaddr = vmaddr;
84 seg.fileoff = fileoff;
85
86 for (macho_file.sections.items(.header)) |header| {
87 vmaddr = header.addr + header.size;
88 if (!header.isZerofill()) {
89 fileoff = header.offset + header.size;
90 }
91 }
92
93 seg.vmsize = vmaddr - seg.vmaddr;
94 seg.filesize = fileoff - seg.fileoff;
95 }
96
61 try createSegment(macho_file);
62 try allocateSectionsVM(macho_file);
63 try allocateSectionsFile(macho_file);
64 allocateSegment(macho_file);
9765 macho_file.allocateAtoms();
9866
9967 state_log.debug("{}", .{macho_file.dumpState()});
10068
69 var off = off: {
70 const seg = macho_file.segments.items[0];
71 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
72 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
73 };
74 off = allocateSectionsRelocs(macho_file, off);
10175 try macho_file.calcSymtabSize();
10276 try writeAtoms(macho_file);
10377 try writeCompactUnwind(macho_file);
......@@ -250,8 +224,7 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
250224 sect.@"align" = 3;
251225}
252226
253fn allocateSections(macho_file: *MachO) !u32 {
254 var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
227fn allocateSectionsVM(macho_file: *MachO) !void {
255228 var vmaddr: u64 = 0;
256229 const slice = macho_file.sections.slice();
257230
......@@ -260,20 +233,96 @@ fn allocateSections(macho_file: *MachO) !u32 {
260233 vmaddr = mem.alignForward(u64, vmaddr, alignment);
261234 header.addr = vmaddr;
262235 vmaddr += header.size;
236 }
237}
263238
239fn allocateSectionsFile(macho_file: *MachO) !void {
240 var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
241 const slice = macho_file.sections.slice();
242
243 const last_index = for (slice.items(.header), 0..) |header, i| {
244 if (mem.indexOf(u8, header.segName(), "ZIG")) |_| break i;
245 } else slice.items(.header).len;
246
247 // TODO: I actually think for relocatable we can just use findFreeSpace
248 // all the way since there is a single segment involved anyhow.
249 for (slice.items(.header)[0..last_index]) |*header| {
250 if (header.isZerofill()) continue;
251 const alignment = try math.powi(u32, 2, header.@"align");
252 fileoff = mem.alignForward(u32, fileoff, alignment);
253 header.offset = fileoff;
254 fileoff += @intCast(header.size);
255 }
256
257 for (slice.items(.header)[last_index..]) |*header| {
258 if (header.isZerofill()) continue;
259 if (header.offset < fileoff) {
260 const existing_size = header.size;
261 header.size = 0;
262
263 // Must move the entire section.
264 const alignment = try math.powi(u32, 2, header.@"align");
265 const new_offset = macho_file.findFreeSpace(existing_size, alignment);
266
267 log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{
268 header.segName(),
269 header.sectName(),
270 new_offset,
271 new_offset + existing_size,
272 });
273
274 try macho_file.copyRangeAll(header.offset, new_offset, existing_size);
275
276 header.offset = @intCast(new_offset);
277 header.size = existing_size;
278 }
279 }
280}
281
282fn createSegment(macho_file: *MachO) !void {
283 const gpa = macho_file.base.comp.gpa;
284
285 // For relocatable, we only ever need a single segment so create it now.
286 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;
287 try macho_file.segments.append(gpa, .{
288 .cmdsize = @sizeOf(macho.segment_command_64),
289 .segname = MachO.makeStaticString(""),
290 .maxprot = prot,
291 .initprot = prot,
292 });
293 const seg = &macho_file.segments.items[0];
294 seg.nsects = @intCast(macho_file.sections.items(.header).len);
295 seg.cmdsize += seg.nsects * @sizeOf(macho.section_64);
296}
297
298fn allocateSegment(macho_file: *MachO) void {
299 // Allocate the single segment.
300 const seg = &macho_file.segments.items[0];
301 var vmaddr: u64 = 0;
302 var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
303 seg.vmaddr = vmaddr;
304 seg.fileoff = fileoff;
305
306 for (macho_file.sections.items(.header)) |header| {
307 vmaddr = @max(vmaddr, header.addr + header.size);
264308 if (!header.isZerofill()) {
265 fileoff = mem.alignForward(u32, fileoff, alignment);
266 header.offset = fileoff;
267 fileoff += @intCast(header.size);
309 fileoff = @max(fileoff, header.offset + header.size);
268310 }
311 std.debug.print("fileoff={x},vmaddr={x}\n", .{ fileoff, vmaddr });
269312 }
270313
314 seg.vmsize = vmaddr - seg.vmaddr;
315 seg.filesize = fileoff - seg.fileoff;
316}
317
318fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
319 var fileoff = off;
320 const slice = macho_file.sections.slice();
271321 for (slice.items(.header)) |*header| {
272322 if (header.nreloc == 0) continue;
273323 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
274324 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
275325 }
276
277326 return fileoff;
278327}
279328
......@@ -511,6 +560,7 @@ const assert = std.debug.assert;
511560const eh_frame = @import("eh_frame.zig");
512561const link = @import("../../link.zig");
513562const load_commands = @import("load_commands.zig");
563const log = std.log.scoped(.link);
514564const macho = std.macho;
515565const math = std.math;
516566const mem = std.mem;