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 {...@@ -3299,7 +3299,7 @@ fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
3299 return min_pos - start;3299 return min_pos - start;
3300}3300}
33013301
3302fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {3302pub fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3303 var start: u64 = 0;3303 var start: u64 = 0;
3304 while (self.detectAllocCollision(start, object_size)) |item_end| {3304 while (self.detectAllocCollision(start, object_size)) |item_end| {
3305 start = mem.alignForward(u64, item_end, min_alignment);3305 start = mem.alignForward(u64, item_end, min_alignment);
...@@ -3307,18 +3307,22 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {...@@ -3307,18 +3307,22 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
3307 return start;3307 return start;
3308}3308}
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
3310/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.3316/// Like File.copyRangeAll but also ensures the source region is zeroed out after copy.
3311/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.3317/// This is so that we guarantee zeroed out regions for mapping of zerofill sections by the loader.
3312fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {3318fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64) !void {
3313 const gpa = self.base.comp.gpa;3319 const gpa = self.base.comp.gpa;
3314 const file = self.base.file.?;3320 try self.copyRangeAll(old_offset, new_offset, size);
3315 const amt = try file.copyRangeAll(old_offset, file, new_offset, size);
3316 if (amt != size) return error.InputOutput;
3317 const size_u = math.cast(usize, size) orelse return error.Overflow;3321 const size_u = math.cast(usize, size) orelse return error.Overflow;
3318 const zeroes = try gpa.alloc(u8, size_u);3322 const zeroes = try gpa.alloc(u8, size_u);
3319 defer gpa.free(zeroes);3323 defer gpa.free(zeroes);
3320 @memset(zeroes, 0);3324 @memset(zeroes, 0);
3321 try file.pwriteAll(zeroes, old_offset);3325 try self.base.file.?.pwriteAll(zeroes, old_offset);
3322}3326}
33233327
3324const InitMetadataOptions = struct {3328const InitMetadataOptions = struct {
...@@ -4064,7 +4068,7 @@ fn formatSections(...@@ -4064,7 +4068,7 @@ fn formatSections(
4064 const slice = self.sections.slice();4068 const slice = self.sections.slice();
4065 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {4069 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
4066 try writer.print("sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x})\n", .{4070 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,
4068 header.@"align", header.size,4072 header.@"align", header.size,
4069 });4073 });
4070 }4074 }
src/link/MachO/relocatable.zig+92-42
...@@ -58,46 +58,20 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -58,46 +58,20 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
58 try macho_file.addAtomsToSections();58 try macho_file.addAtomsToSections();
59 try calcSectionSizes(macho_file);59 try calcSectionSizes(macho_file);
6060
61 {61 try createSegment(macho_file);
62 // For relocatable, we only ever need a single segment so create it now.62 try allocateSectionsVM(macho_file);
63 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;63 try allocateSectionsFile(macho_file);
64 try macho_file.segments.append(gpa, .{64 allocateSegment(macho_file);
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
97 macho_file.allocateAtoms();65 macho_file.allocateAtoms();
9866
99 state_log.debug("{}", .{macho_file.dumpState()});67 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);
101 try macho_file.calcSymtabSize();75 try macho_file.calcSymtabSize();
102 try writeAtoms(macho_file);76 try writeAtoms(macho_file);
103 try writeCompactUnwind(macho_file);77 try writeCompactUnwind(macho_file);
...@@ -250,8 +224,7 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {...@@ -250,8 +224,7 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
250 sect.@"align" = 3;224 sect.@"align" = 3;
251}225}
252226
253fn allocateSections(macho_file: *MachO) !u32 {227fn allocateSectionsVM(macho_file: *MachO) !void {
254 var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64);
255 var vmaddr: u64 = 0;228 var vmaddr: u64 = 0;
256 const slice = macho_file.sections.slice();229 const slice = macho_file.sections.slice();
257230
...@@ -260,20 +233,96 @@ fn allocateSections(macho_file: *MachO) !u32 {...@@ -260,20 +233,96 @@ fn allocateSections(macho_file: *MachO) !u32 {
260 vmaddr = mem.alignForward(u64, vmaddr, alignment);233 vmaddr = mem.alignForward(u64, vmaddr, alignment);
261 header.addr = vmaddr;234 header.addr = vmaddr;
262 vmaddr += header.size;235 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);
264 if (!header.isZerofill()) {308 if (!header.isZerofill()) {
265 fileoff = mem.alignForward(u32, fileoff, alignment);309 fileoff = @max(fileoff, header.offset + header.size);
266 header.offset = fileoff;
267 fileoff += @intCast(header.size);
268 }310 }
311 std.debug.print("fileoff={x},vmaddr={x}\n", .{ fileoff, vmaddr });
269 }312 }
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();
271 for (slice.items(.header)) |*header| {321 for (slice.items(.header)) |*header| {
272 if (header.nreloc == 0) continue;322 if (header.nreloc == 0) continue;
273 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));323 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
274 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);324 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
275 }325 }
276
277 return fileoff;326 return fileoff;
278}327}
279328
...@@ -511,6 +560,7 @@ const assert = std.debug.assert;...@@ -511,6 +560,7 @@ const assert = std.debug.assert;
511const eh_frame = @import("eh_frame.zig");560const eh_frame = @import("eh_frame.zig");
512const link = @import("../../link.zig");561const link = @import("../../link.zig");
513const load_commands = @import("load_commands.zig");562const load_commands = @import("load_commands.zig");
563const log = std.log.scoped(.link);
514const macho = std.macho;564const macho = std.macho;
515const math = std.math;565const math = std.math;
516const mem = std.mem;566const mem = std.mem;