| ... | ... | @@ -58,46 +58,20 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u |
| 58 | 58 | try macho_file.addAtomsToSections(); |
| 59 | 59 | try calcSectionSizes(macho_file); |
| 60 | 60 | |
| 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); |
| 97 | 65 | macho_file.allocateAtoms(); |
| 98 | 66 | |
| 99 | 67 | state_log.debug("{}", .{macho_file.dumpState()}); |
| 100 | 68 | |
| 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 | 75 | try macho_file.calcSymtabSize(); |
| 102 | 76 | try writeAtoms(macho_file); |
| 103 | 77 | try writeCompactUnwind(macho_file); |
| ... | ... | @@ -250,8 +224,7 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { |
| 250 | 224 | sect.@"align" = 3; |
| 251 | 225 | } |
| 252 | 226 | |
| 253 | | fn allocateSections(macho_file: *MachO) !u32 { |
| 254 | | var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64); |
| 227 | fn allocateSectionsVM(macho_file: *MachO) !void { |
| 255 | 228 | var vmaddr: u64 = 0; |
| 256 | 229 | const slice = macho_file.sections.slice(); |
| 257 | 230 | |
| ... | ... | @@ -260,20 +233,96 @@ fn allocateSections(macho_file: *MachO) !u32 { |
| 260 | 233 | vmaddr = mem.alignForward(u64, vmaddr, alignment); |
| 261 | 234 | header.addr = vmaddr; |
| 262 | 235 | vmaddr += header.size; |
| 236 | } |
| 237 | } |
| 263 | 238 | |
| 239 | fn 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 | |
| 282 | fn 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 | |
| 298 | fn 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 | 308 | 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); |
| 268 | 310 | } |
| 311 | std.debug.print("fileoff={x},vmaddr={x}\n", .{ fileoff, vmaddr }); |
| 269 | 312 | } |
| 270 | 313 | |
| 314 | seg.vmsize = vmaddr - seg.vmaddr; |
| 315 | seg.filesize = fileoff - seg.fileoff; |
| 316 | } |
| 317 | |
| 318 | fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 { |
| 319 | var fileoff = off; |
| 320 | const slice = macho_file.sections.slice(); |
| 271 | 321 | for (slice.items(.header)) |*header| { |
| 272 | 322 | if (header.nreloc == 0) continue; |
| 273 | 323 | header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info)); |
| 274 | 324 | fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info); |
| 275 | 325 | } |
| 276 | | |
| 277 | 326 | return fileoff; |
| 278 | 327 | } |
| 279 | 328 | |
| ... | ... | @@ -511,6 +560,7 @@ const assert = std.debug.assert; |
| 511 | 560 | const eh_frame = @import("eh_frame.zig"); |
| 512 | 561 | const link = @import("../../link.zig"); |
| 513 | 562 | const load_commands = @import("load_commands.zig"); |
| 563 | const log = std.log.scoped(.link); |
| 514 | 564 | const macho = std.macho; |
| 515 | 565 | const math = std.math; |
| 516 | 566 | const mem = std.mem; |