authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-12 00:05:17+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:39+01:00
log3968aea8ec98277bcc5b3c26beb5592f26b1a9fd
tree570d6ad5cafcf2a0f521b6fc6cc4df0d1487542b
parent98d6d40cd64b5c52ba2ddd01fbedb5069bf0458d

macho: write to file


9 files changed, 773 insertions(+), 56 deletions(-)

src/link/MachO.zig+698-2
......@@ -540,7 +540,63 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
540540
541541 state_log.debug("{}", .{self.dumpState()});
542542
543 @panic("TODO");
543 try self.initDyldInfoSections();
544 self.writeAtoms() catch |err| switch (err) {
545 error.ResolveFailed => return error.FlushFailure,
546 else => |e| {
547 try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
548 return e;
549 },
550 };
551 try self.writeUnwindInfo();
552 try self.finalizeDyldInfoSections();
553 try self.writeSyntheticSections();
554
555 var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow;
556 off = try self.writeDyldInfoSections(off);
557 off = mem.alignForward(u32, off, @alignOf(u64));
558 off = try self.writeFunctionStarts(off);
559 off = mem.alignForward(u32, off, @alignOf(u64));
560 off = try self.writeDataInCode(self.getTextSegment().vmaddr, off);
561 try self.calcSymtabSize();
562 off = mem.alignForward(u32, off, @alignOf(u64));
563 off = try self.writeSymtab(off);
564 off = mem.alignForward(u32, off, @alignOf(u32));
565 off = try self.writeIndsymtab(off);
566 off = mem.alignForward(u32, off, @alignOf(u64));
567 off = try self.writeStrtab(off);
568
569 self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff;
570
571 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
572 // Preallocate space for the code signature.
573 // We need to do this at this stage so that we have the load commands with proper values
574 // written out to the file.
575 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
576 // where the code signature goes into.
577 var codesig = CodeSignature.init(self.getPageSize());
578 codesig.code_directory.ident = self.base.emit.sub_path;
579 if (self.entitlements) |path| try codesig.addEntitlements(gpa, path);
580 try self.writeCodeSignaturePadding(&codesig);
581 break :blk codesig;
582 } else null;
583 defer if (codesig) |*csig| csig.deinit(gpa);
584
585 self.getLinkeditSegment().vmsize = mem.alignForward(
586 u64,
587 self.getLinkeditSegment().filesize,
588 self.getPageSize(),
589 );
590
591 const ncmds, const sizeofcmds, const uuid_cmd_offset = try self.writeLoadCommands();
592 try self.writeHeader(ncmds, sizeofcmds);
593 try self.writeUuid(uuid_cmd_offset, self.requiresCodeSig());
594
595 if (codesig) |*csig| {
596 try self.writeCodeSignature(csig); // code signing always comes last
597 const emit = self.base.emit;
598 try invalidateKernelCache(emit.directory.handle, emit.sub_path);
599 }
544600}
545601
546602/// --verbose-link output
......@@ -2186,6 +2242,646 @@ fn allocateSyntheticSymbols(self: *MachO) void {
21862242 }
21872243}
21882244
2245fn initDyldInfoSections(self: *MachO) !void {
2246 const tracy = trace(@src());
2247 defer tracy.end();
2248
2249 const gpa = self.base.comp.gpa;
2250
2251 if (self.got_sect_index != null) try self.got.addDyldRelocs(self);
2252 if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self);
2253 if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self);
2254 try self.initExportTrie();
2255
2256 var nrebases: usize = 0;
2257 var nbinds: usize = 0;
2258 var nweak_binds: usize = 0;
2259 for (self.objects.items) |index| {
2260 const object = self.getFile(index).?.object;
2261 nrebases += object.num_rebase_relocs;
2262 nbinds += object.num_bind_relocs;
2263 nweak_binds += object.num_weak_bind_relocs;
2264 }
2265 try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases);
2266 try self.bind.entries.ensureUnusedCapacity(gpa, nbinds);
2267 try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds);
2268}
2269
2270fn initExportTrie(self: *MachO) !void {
2271 const tracy = trace(@src());
2272 defer tracy.end();
2273
2274 const gpa = self.base.comp.gpa;
2275 try self.export_trie.init(gpa);
2276
2277 const seg = self.getTextSegment();
2278 for (self.objects.items) |index| {
2279 for (self.getFile(index).?.getSymbols()) |sym_index| {
2280 const sym = self.getSymbol(sym_index);
2281 if (!sym.flags.@"export") continue;
2282 if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue;
2283 if (sym.getFile(self).?.getIndex() != index) continue;
2284 var flags: u64 = if (sym.flags.abs)
2285 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
2286 else if (sym.flags.tlv)
2287 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
2288 else
2289 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
2290 if (sym.flags.weak) {
2291 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
2292 self.weak_defines = true;
2293 self.binds_to_weak = true;
2294 }
2295 try self.export_trie.put(gpa, .{
2296 .name = sym.getName(self),
2297 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr,
2298 .export_flags = flags,
2299 });
2300 }
2301 }
2302
2303 if (self.mh_execute_header_index) |index| {
2304 const sym = self.getSymbol(index);
2305 try self.export_trie.put(gpa, .{
2306 .name = sym.getName(self),
2307 .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr,
2308 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2309 });
2310 }
2311}
2312
2313fn writeAtoms(self: *MachO) !void {
2314 const tracy = trace(@src());
2315 defer tracy.end();
2316
2317 const gpa = self.base.comp.gpa;
2318 const cpu_arch = self.getTarget().cpu.arch;
2319 const slice = self.sections.slice();
2320
2321 var has_resolve_error = false;
2322 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
2323 if (atoms.items.len == 0) continue;
2324 if (header.isZerofill()) continue;
2325
2326 const buffer = try gpa.alloc(u8, header.size);
2327 defer gpa.free(buffer);
2328 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
2329 @memset(buffer, padding_byte);
2330
2331 for (atoms.items) |atom_index| {
2332 const atom = self.getAtom(atom_index).?;
2333 assert(atom.flags.alive);
2334 const off = atom.value - header.addr;
2335 atom.resolveRelocs(self, buffer[off..][0..atom.size]) catch |err| switch (err) {
2336 error.ResolveFailed => has_resolve_error = true,
2337 else => |e| return e,
2338 };
2339 }
2340
2341 try self.base.file.?.pwriteAll(buffer, header.offset);
2342 }
2343
2344 for (self.thunks.items) |thunk| {
2345 const header = slice.items(.header)[thunk.out_n_sect];
2346 const offset = thunk.value - header.addr + header.offset;
2347 const buffer = try gpa.alloc(u8, thunk.size());
2348 defer gpa.free(buffer);
2349 var stream = std.io.fixedBufferStream(buffer);
2350 try thunk.write(self, stream.writer());
2351 try self.base.file.?.pwriteAll(buffer, offset);
2352 }
2353
2354 if (has_resolve_error) return error.ResolveFailed;
2355}
2356
2357fn writeUnwindInfo(self: *MachO) !void {
2358 const tracy = trace(@src());
2359 defer tracy.end();
2360
2361 const gpa = self.base.comp.gpa;
2362
2363 if (self.eh_frame_sect_index) |index| {
2364 const header = self.sections.items(.header)[index];
2365 const buffer = try gpa.alloc(u8, header.size);
2366 defer gpa.free(buffer);
2367 eh_frame.write(self, buffer);
2368 try self.base.file.?.pwriteAll(buffer, header.offset);
2369 }
2370
2371 if (self.unwind_info_sect_index) |index| {
2372 const header = self.sections.items(.header)[index];
2373 const buffer = try gpa.alloc(u8, header.size);
2374 defer gpa.free(buffer);
2375 try self.unwind_info.write(self, buffer);
2376 try self.base.file.?.pwriteAll(buffer, header.offset);
2377 }
2378}
2379
2380fn finalizeDyldInfoSections(self: *MachO) !void {
2381 const tracy = trace(@src());
2382 defer tracy.end();
2383 const gpa = self.base.comp.gpa;
2384
2385 try self.rebase.finalize(gpa);
2386 try self.bind.finalize(gpa, self);
2387 try self.weak_bind.finalize(gpa, self);
2388 try self.lazy_bind.finalize(gpa, self);
2389 try self.export_trie.finalize(gpa);
2390}
2391
2392fn writeSyntheticSections(self: *MachO) !void {
2393 const tracy = trace(@src());
2394 defer tracy.end();
2395
2396 const gpa = self.base.comp.gpa;
2397
2398 if (self.got_sect_index) |sect_id| {
2399 const header = self.sections.items(.header)[sect_id];
2400 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2401 defer buffer.deinit();
2402 try self.got.write(self, buffer.writer());
2403 assert(buffer.items.len == header.size);
2404 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2405 }
2406
2407 if (self.stubs_sect_index) |sect_id| {
2408 const header = self.sections.items(.header)[sect_id];
2409 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2410 defer buffer.deinit();
2411 try self.stubs.write(self, buffer.writer());
2412 assert(buffer.items.len == header.size);
2413 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2414 }
2415
2416 if (self.stubs_helper_sect_index) |sect_id| {
2417 const header = self.sections.items(.header)[sect_id];
2418 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2419 defer buffer.deinit();
2420 try self.stubs_helper.write(self, buffer.writer());
2421 assert(buffer.items.len == header.size);
2422 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2423 }
2424
2425 if (self.la_symbol_ptr_sect_index) |sect_id| {
2426 const header = self.sections.items(.header)[sect_id];
2427 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2428 defer buffer.deinit();
2429 try self.la_symbol_ptr.write(self, buffer.writer());
2430 assert(buffer.items.len == header.size);
2431 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2432 }
2433
2434 if (self.tlv_ptr_sect_index) |sect_id| {
2435 const header = self.sections.items(.header)[sect_id];
2436 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2437 defer buffer.deinit();
2438 try self.tlv_ptr.write(self, buffer.writer());
2439 assert(buffer.items.len == header.size);
2440 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2441 }
2442
2443 if (self.objc_stubs_sect_index) |sect_id| {
2444 const header = self.sections.items(.header)[sect_id];
2445 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
2446 defer buffer.deinit();
2447 try self.objc_stubs.write(self, buffer.writer());
2448 assert(buffer.items.len == header.size);
2449 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2450 }
2451}
2452
2453fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
2454 const tracy = trace(@src());
2455 defer tracy.end();
2456
2457 const gpa = self.base.comp.gpa;
2458 const cmd = &self.dyld_info_cmd;
2459 var needed_size: u32 = 0;
2460
2461 cmd.rebase_off = needed_size;
2462 cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64));
2463 needed_size += cmd.rebase_size;
2464
2465 cmd.bind_off = needed_size;
2466 cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64));
2467 needed_size += cmd.bind_size;
2468
2469 cmd.weak_bind_off = needed_size;
2470 cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64));
2471 needed_size += cmd.weak_bind_size;
2472
2473 cmd.lazy_bind_off = needed_size;
2474 cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64));
2475 needed_size += cmd.lazy_bind_size;
2476
2477 cmd.export_off = needed_size;
2478 cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64));
2479 needed_size += cmd.export_size;
2480
2481 const buffer = try gpa.alloc(u8, needed_size);
2482 defer gpa.free(buffer);
2483 @memset(buffer, 0);
2484
2485 var stream = std.io.fixedBufferStream(buffer);
2486 const writer = stream.writer();
2487
2488 try self.rebase.write(writer);
2489 try stream.seekTo(cmd.bind_off);
2490 try self.bind.write(writer);
2491 try stream.seekTo(cmd.weak_bind_off);
2492 try self.weak_bind.write(writer);
2493 try stream.seekTo(cmd.lazy_bind_off);
2494 try self.lazy_bind.write(writer);
2495 try stream.seekTo(cmd.export_off);
2496 try self.export_trie.write(writer);
2497
2498 cmd.rebase_off += off;
2499 cmd.bind_off += off;
2500 cmd.weak_bind_off += off;
2501 cmd.lazy_bind_off += off;
2502 cmd.export_off += off;
2503
2504 try self.base.file.?.pwriteAll(buffer, off);
2505
2506 return off + needed_size;
2507}
2508
2509fn writeFunctionStarts(self: *MachO, off: u32) !u32 {
2510 // TODO actually write it out
2511 const cmd = &self.function_starts_cmd;
2512 cmd.dataoff = off;
2513 return off;
2514}
2515
2516pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
2517 const cmd = &self.data_in_code_cmd;
2518 cmd.dataoff = off;
2519
2520 const gpa = self.base.comp.gpa;
2521 var dices = std.ArrayList(macho.data_in_code_entry).init(gpa);
2522 defer dices.deinit();
2523
2524 for (self.objects.items) |index| {
2525 const object = self.getFile(index).?.object;
2526 const in_dices = object.getDataInCode();
2527
2528 try dices.ensureUnusedCapacity(in_dices.len);
2529
2530 var next_dice: usize = 0;
2531 for (object.atoms.items) |atom_index| {
2532 if (next_dice >= in_dices.len) break;
2533 const atom = self.getAtom(atom_index) orelse continue;
2534 const start_off = atom.getInputAddress(self);
2535 const end_off = start_off + atom.size;
2536 const start_dice = next_dice;
2537
2538 if (end_off < in_dices[next_dice].offset) continue;
2539
2540 while (next_dice < in_dices.len and
2541 in_dices[next_dice].offset < end_off) : (next_dice += 1)
2542 {}
2543
2544 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
2545 dices.appendAssumeCapacity(.{
2546 .offset = @intCast(atom.value + dice.offset - start_off - base_address),
2547 .length = dice.length,
2548 .kind = dice.kind,
2549 });
2550 };
2551 }
2552 }
2553
2554 const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow;
2555 cmd.datasize = needed_size;
2556
2557 try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff);
2558
2559 return off + needed_size;
2560}
2561
2562pub fn calcSymtabSize(self: *MachO) !void {
2563 const tracy = trace(@src());
2564 defer tracy.end();
2565 const gpa = self.base.comp.gpa;
2566
2567 var nlocals: u32 = 0;
2568 var nstabs: u32 = 0;
2569 var nexports: u32 = 0;
2570 var nimports: u32 = 0;
2571 var strsize: u32 = 0;
2572
2573 var files = std.ArrayList(File.Index).init(gpa);
2574 defer files.deinit();
2575 try files.ensureTotalCapacityPrecise(self.objects.items.len + self.dylibs.items.len + 1);
2576 for (self.objects.items) |index| files.appendAssumeCapacity(index);
2577 for (self.dylibs.items) |index| files.appendAssumeCapacity(index);
2578 if (self.internal_object) |index| files.appendAssumeCapacity(index);
2579
2580 for (files.items) |index| {
2581 const file = self.getFile(index).?;
2582 const ctx = switch (file) {
2583 inline else => |x| &x.output_symtab_ctx,
2584 };
2585 ctx.ilocal = nlocals;
2586 ctx.istab = nstabs;
2587 ctx.iexport = nexports;
2588 ctx.iimport = nimports;
2589 try file.calcSymtabSize(self);
2590 nlocals += ctx.nlocals;
2591 nstabs += ctx.nstabs;
2592 nexports += ctx.nexports;
2593 nimports += ctx.nimports;
2594 strsize += ctx.strsize;
2595 }
2596
2597 for (files.items) |index| {
2598 const file = self.getFile(index).?;
2599 const ctx = switch (file) {
2600 inline else => |x| &x.output_symtab_ctx,
2601 };
2602 ctx.istab += nlocals;
2603 ctx.iexport += nlocals + nstabs;
2604 ctx.iimport += nlocals + nstabs + nexports;
2605 }
2606
2607 {
2608 const cmd = &self.symtab_cmd;
2609 cmd.nsyms = nlocals + nstabs + nexports + nimports;
2610 cmd.strsize = strsize + 1;
2611 }
2612
2613 {
2614 const cmd = &self.dysymtab_cmd;
2615 cmd.ilocalsym = 0;
2616 cmd.nlocalsym = nlocals + nstabs;
2617 cmd.iextdefsym = nlocals + nstabs;
2618 cmd.nextdefsym = nexports;
2619 cmd.iundefsym = nlocals + nstabs + nexports;
2620 cmd.nundefsym = nimports;
2621 }
2622}
2623
2624pub fn writeSymtab(self: *MachO, off: u32) !u32 {
2625 const tracy = trace(@src());
2626 defer tracy.end();
2627 const gpa = self.base.comp.gpa;
2628 const cmd = &self.symtab_cmd;
2629 cmd.symoff = off;
2630
2631 try self.symtab.resize(gpa, cmd.nsyms);
2632 try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
2633
2634 for (self.objects.items) |index| {
2635 self.getFile(index).?.writeSymtab(self);
2636 }
2637 for (self.dylibs.items) |index| {
2638 self.getFile(index).?.writeSymtab(self);
2639 }
2640 if (self.getInternalObject()) |internal| {
2641 internal.writeSymtab(self);
2642 }
2643
2644 assert(self.strtab.items.len == cmd.strsize);
2645
2646 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2647
2648 return off + cmd.nsyms * @sizeOf(macho.nlist_64);
2649}
2650
2651fn writeIndsymtab(self: *MachO, off: u32) !u32 {
2652 const gpa = self.base.comp.gpa;
2653 const cmd = &self.dysymtab_cmd;
2654 cmd.indirectsymoff = off;
2655 cmd.nindirectsyms = self.indsymtab.nsyms(self);
2656
2657 const needed_size = cmd.nindirectsyms * @sizeOf(u32);
2658 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2659 defer buffer.deinit();
2660 try self.indsymtab.write(self, buffer.writer());
2661
2662 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
2663 assert(buffer.items.len == needed_size);
2664
2665 return off + needed_size;
2666}
2667
2668pub fn writeStrtab(self: *MachO, off: u32) !u32 {
2669 const cmd = &self.symtab_cmd;
2670 cmd.stroff = off;
2671 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
2672 return off + cmd.strsize;
2673}
2674
2675fn writeLoadCommands(self: *MachO) !struct { usize, usize, usize } {
2676 const gpa = self.base.comp.gpa;
2677 const needed_size = load_commands.calcLoadCommandsSize(self, false);
2678 const buffer = try gpa.alloc(u8, needed_size);
2679 defer gpa.free(buffer);
2680
2681 var stream = std.io.fixedBufferStream(buffer);
2682 var cwriter = std.io.countingWriter(stream.writer());
2683 const writer = cwriter.writer();
2684
2685 var ncmds: usize = 0;
2686
2687 // Segment and section load commands
2688 {
2689 const slice = self.sections.slice();
2690 var sect_id: usize = 0;
2691 for (self.segments.items) |seg| {
2692 try writer.writeStruct(seg);
2693 for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| {
2694 try writer.writeStruct(header);
2695 }
2696 sect_id += seg.nsects;
2697 }
2698 ncmds += self.segments.items.len;
2699 }
2700
2701 try writer.writeStruct(self.dyld_info_cmd);
2702 ncmds += 1;
2703 try writer.writeStruct(self.function_starts_cmd);
2704 ncmds += 1;
2705 try writer.writeStruct(self.data_in_code_cmd);
2706 ncmds += 1;
2707 try writer.writeStruct(self.symtab_cmd);
2708 ncmds += 1;
2709 try writer.writeStruct(self.dysymtab_cmd);
2710 ncmds += 1;
2711 try load_commands.writeDylinkerLC(writer);
2712 ncmds += 1;
2713
2714 if (self.entry_index) |global_index| {
2715 const sym = self.getSymbol(global_index);
2716 const seg = self.getTextSegment();
2717 const entryoff: u32 = if (sym.getFile(self) == null)
2718 0
2719 else
2720 @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
2721 try writer.writeStruct(macho.entry_point_command{
2722 .entryoff = entryoff,
2723 .stacksize = self.base.stack_size,
2724 });
2725 ncmds += 1;
2726 }
2727
2728 if (self.base.isDynLib()) {
2729 try load_commands.writeDylibIdLC(self, writer);
2730 ncmds += 1;
2731 }
2732
2733 try load_commands.writeRpathLCs(self.rpath_table.keys(), writer);
2734 ncmds += self.rpath_table.keys().len;
2735
2736 try writer.writeStruct(macho.source_version_command{ .version = 0 });
2737 ncmds += 1;
2738
2739 if (self.platform.isBuildVersionCompatible()) {
2740 try load_commands.writeBuildVersionLC(self.platform, self.sdk_version, writer);
2741 ncmds += 1;
2742 } else {
2743 try load_commands.writeVersionMinLC(self.platform, self.sdk_version, writer);
2744 ncmds += 1;
2745 }
2746
2747 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + cwriter.bytes_written;
2748 try writer.writeStruct(self.uuid_cmd);
2749 ncmds += 1;
2750
2751 for (self.dylibs.items) |index| {
2752 const dylib = self.getFile(index).?.dylib;
2753 assert(dylib.isAlive(self));
2754 const dylib_id = dylib.id.?;
2755 try load_commands.writeDylibLC(.{
2756 .cmd = if (dylib.weak)
2757 .LOAD_WEAK_DYLIB
2758 else if (dylib.reexport)
2759 .REEXPORT_DYLIB
2760 else
2761 .LOAD_DYLIB,
2762 .name = dylib_id.name,
2763 .timestamp = dylib_id.timestamp,
2764 .current_version = dylib_id.current_version,
2765 .compatibility_version = dylib_id.compatibility_version,
2766 }, writer);
2767 ncmds += 1;
2768 }
2769
2770 if (self.requiresCodeSig()) {
2771 try writer.writeStruct(self.codesig_cmd);
2772 ncmds += 1;
2773 }
2774
2775 assert(cwriter.bytes_written == needed_size);
2776
2777 try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64));
2778
2779 return .{ ncmds, buffer.len, uuid_cmd_offset };
2780}
2781
2782fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
2783 var header: macho.mach_header_64 = .{};
2784 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK;
2785
2786 // TODO: if (self.options.namespace == .two_level) {
2787 header.flags |= macho.MH_TWOLEVEL;
2788 // }
2789
2790 switch (self.getTarget().cpu.arch) {
2791 .aarch64 => {
2792 header.cputype = macho.CPU_TYPE_ARM64;
2793 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
2794 },
2795 .x86_64 => {
2796 header.cputype = macho.CPU_TYPE_X86_64;
2797 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
2798 },
2799 else => {},
2800 }
2801
2802 if (self.base.isDynLib()) {
2803 header.filetype = macho.MH_DYLIB;
2804 } else {
2805 header.filetype = macho.MH_EXECUTE;
2806 header.flags |= macho.MH_PIE;
2807 }
2808
2809 const has_reexports = for (self.dylibs.items) |index| {
2810 if (self.getFile(index).?.dylib.reexport) break true;
2811 } else false;
2812 if (!has_reexports) {
2813 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
2814 }
2815
2816 if (self.has_tlv) {
2817 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2818 }
2819 if (self.binds_to_weak) {
2820 header.flags |= macho.MH_BINDS_TO_WEAK;
2821 }
2822 if (self.weak_defines) {
2823 header.flags |= macho.MH_WEAK_DEFINES;
2824 }
2825
2826 header.ncmds = @intCast(ncmds);
2827 header.sizeofcmds = @intCast(sizeofcmds);
2828
2829 log.debug("writing Mach-O header {}", .{header});
2830
2831 try self.base.file.?.pwriteAll(mem.asBytes(&header), 0);
2832}
2833
2834fn writeUuid(self: *MachO, uuid_cmd_offset: usize, has_codesig: bool) !void {
2835 const file_size = if (!has_codesig) blk: {
2836 const seg = self.getLinkeditSegment();
2837 break :blk seg.fileoff + seg.filesize;
2838 } else self.codesig_cmd.dataoff;
2839 try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid);
2840 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
2841 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
2842}
2843
2844pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
2845 const seg = self.getLinkeditSegment();
2846 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
2847 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
2848 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
2849 const needed_size = code_sig.estimateSize(offset);
2850 seg.filesize = offset + needed_size - seg.fileoff;
2851 seg.vmsize = mem.alignForward(u64, seg.filesize, self.getPageSize());
2852 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2853 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
2854 // except for code signature data.
2855 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
2856
2857 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
2858 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
2859}
2860
2861pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
2862 const seg = self.getTextSegment();
2863 const offset = self.codesig_cmd.dataoff;
2864
2865 var buffer = std.ArrayList(u8).init(self.base.comp.gpa);
2866 defer buffer.deinit();
2867 try buffer.ensureTotalCapacityPrecise(code_sig.size());
2868 try code_sig.writeAdhocSignature(self, .{
2869 .file = self.base.file.?,
2870 .exec_seg_base = seg.fileoff,
2871 .exec_seg_limit = seg.filesize,
2872 .file_size = offset,
2873 .dylib = self.base.isDynLib(),
2874 }, buffer.writer());
2875 assert(buffer.items.len == code_sig.size());
2876
2877 log.debug("writing code signature from 0x{x} to 0x{x}", .{
2878 offset,
2879 offset + buffer.items.len,
2880 });
2881
2882 try self.base.file.?.pwriteAll(buffer.items, offset);
2883}
2884
21892885fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
21902886 _ = self;
21912887 _ = atom_index;
......@@ -3194,7 +3890,7 @@ const supported_platforms = [_]SupportedPlatforms{
31943890};
31953891// zig fmt: on
31963892
3197inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
3893pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
31983894 const major = version.major;
31993895 const minor = version.minor;
32003896 const patch = version.patch;
src/link/MachO/Atom.zig+9-6
......@@ -311,13 +311,16 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
311311 try stream.seekTo(rel_offset);
312312 self.resolveRelocInner(rel, subtractor, buffer, macho_file, stream.writer()) catch |err| {
313313 switch (err) {
314 error.RelaxFail => macho_file.base.fatal(
315 "{}: {s}: 0x{x}: failed to relax relocation: in {s}",
316 .{ file.fmtPath(), name, rel.offset, @tagName(rel.type) },
317 ),
314 error.RelaxFail => {
315 try macho_file.reportParseError2(
316 file.getIndex(),
317 "{s}: 0x{x}: failed to relax relocation: in {s}",
318 .{ name, rel.offset, @tagName(rel.type) },
319 );
320 return error.ResolveFailed;
321 },
318322 else => |e| return e,
319323 }
320 return error.ResolveFailed;
321324 };
322325 }
323326}
......@@ -338,7 +341,7 @@ fn resolveRelocInner(
338341 macho_file: *MachO,
339342 writer: anytype,
340343) ResolveError!void {
341 const cpu_arch = macho_file.options.cpu_arch.?;
344 const cpu_arch = macho_file.getTarget().cpu.arch;
342345 const rel_offset = rel.offset - self.off;
343346 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
344347 const seg = macho_file.segments.items[seg_id];
src/link/MachO/CodeSignature.zig+2-2
......@@ -264,7 +264,7 @@ pub fn writeAdhocSignature(
264264 opts: WriteOpts,
265265 writer: anytype,
266266) !void {
267 const allocator = macho_file.base.allocator;
267 const allocator = macho_file.base.comp.gpa;
268268
269269 var header: macho.SuperBlob = .{
270270 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
......@@ -287,7 +287,7 @@ pub fn writeAdhocSignature(
287287 self.code_directory.inner.nCodeSlots = total_pages;
288288
289289 // Calculate hash for each page (in file) and write it to the buffer
290 var hasher = Hasher(Sha256){ .allocator = allocator, .thread_pool = macho_file.base.thread_pool };
290 var hasher = Hasher(Sha256){ .allocator = allocator, .thread_pool = macho_file.base.comp.thread_pool };
291291 try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
292292 .chunk_size = self.page_size,
293293 .max_file_size = opts.file_size,
src/link/MachO/Object.zig+4-2
......@@ -1184,7 +1184,8 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
11841184 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
11851185 }
11861186
1187 if (!macho_file.options.strip and self.hasDebugInfo()) self.calcStabsSize(macho_file);
1187 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1188 self.calcStabsSize(macho_file);
11881189}
11891190
11901191pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
......@@ -1264,7 +1265,8 @@ pub fn writeSymtab(self: Object, macho_file: *MachO) void {
12641265 sym.setOutputSym(macho_file, out_sym);
12651266 }
12661267
1267 if (!macho_file.options.strip and self.hasDebugInfo()) self.writeStabs(macho_file);
1268 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1269 self.writeStabs(macho_file);
12681270}
12691271
12701272pub fn writeStabs(self: *const Object, macho_file: *MachO) void {
src/link/MachO/Symbol.zig+8-3
......@@ -230,9 +230,14 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
230230 out.n_value = 0;
231231 out.n_desc = 0;
232232
233 const ord: u16 = if (macho_file.options.namespace == .flat)
234 @as(u8, @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP))
235 else if (symbol.getDylibOrdinal(macho_file)) |ord|
233 // TODO:
234 // const ord: u16 = if (macho_file.options.namespace == .flat)
235 // @as(u8, @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP))
236 // else if (symbol.getDylibOrdinal(macho_file)) |ord|
237 // ord
238 // else
239 // macho.BIND_SPECIAL_DYLIB_SELF;
240 const ord: u16 = if (symbol.getDylibOrdinal(macho_file)) |ord|
236241 ord
237242 else
238243 macho.BIND_SPECIAL_DYLIB_SELF;
src/link/MachO/dyld_info/bind.zig+4-4
......@@ -99,10 +99,10 @@ pub const Bind = struct {
9999 const ordinal: i16 = ord: {
100100 if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
101101 if (sym.flags.import) {
102 if (ctx.options.namespace == .flat) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
102 // TODO: if (ctx.options.namespace == .flat) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
103103 if (sym.getDylibOrdinal(ctx)) |ord| break :ord @bitCast(ord);
104104 }
105 if (ctx.options.undefined_treatment == .dynamic_lookup)
105 if (ctx.undefined_treatment == .dynamic_lookup)
106106 break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
107107 break :ord macho.BIND_SPECIAL_DYLIB_SELF;
108108 };
......@@ -359,10 +359,10 @@ pub const LazyBind = struct {
359359 const ordinal: i16 = ord: {
360360 if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
361361 if (sym.flags.import) {
362 if (ctx.options.namespace == .flat) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
362 // TODO: if (ctx.options.namespace == .flat) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
363363 if (sym.getDylibOrdinal(ctx)) |ord| break :ord @bitCast(ord);
364364 }
365 if (ctx.options.undefined_treatment == .dynamic_lookup)
365 if (ctx.undefined_treatment == .dynamic_lookup)
366366 break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
367367 break :ord macho.BIND_SPECIAL_DYLIB_SELF;
368368 };
src/link/MachO/eh_frame.zig+2-2
......@@ -374,7 +374,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
374374 defer tracy.end();
375375
376376 const sect = macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
377 const addend: i64 = switch (macho_file.options.cpu_arch.?) {
377 const addend: i64 = switch (macho_file.getTarget().cpu.arch) {
378378 .x86_64 => 4,
379379 else => 0,
380380 };
......@@ -452,7 +452,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
452452 const tracy = trace(@src());
453453 defer tracy.end();
454454
455 const cpu_arch = macho_file.options.cpu_arch.?;
455 const cpu_arch = macho_file.getTarget().cpu.arch;
456456 const sect = macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
457457 const addend: i64 = switch (cpu_arch) {
458458 .x86_64 => 4,
src/link/MachO/load_commands.zig+39-22
......@@ -7,7 +7,6 @@ const mem = std.mem;
77const Allocator = mem.Allocator;
88const Dylib = @import("Dylib.zig");
99const MachO = @import("../MachO.zig");
10const Options = @import("../MachO.zig").Options;
1110
1211pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1312
......@@ -200,17 +199,29 @@ pub fn writeDylibLC(ctx: WriteDylibLCCtx, writer: anytype) !void {
200199 }
201200}
202201
203pub fn writeDylibIdLC(options: *const Options, writer: anytype) !void {
204 assert(options.dylib);
205 const emit = options.emit;
206 const install_name = options.install_name orelse emit.sub_path;
207 const curr = options.current_version orelse Options.Version.new(1, 0, 0);
208 const compat = options.compatibility_version orelse Options.Version.new(1, 0, 0);
202pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void {
203 const comp = macho_file.base.comp;
204 const gpa = comp.gpa;
205 assert(comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic);
206 const emit = macho_file.base.emit;
207 const install_name = macho_file.install_name orelse
208 try emit.directory.join(gpa, &.{emit.sub_path});
209 defer if (macho_file.install_name == null) gpa.free(install_name);
210 const curr = comp.version orelse std.SemanticVersion{
211 .major = 1,
212 .minor = 0,
213 .patch = 0,
214 };
215 const compat = macho_file.compatibility_version orelse std.SemanticVersion{
216 .major = 1,
217 .minor = 0,
218 .patch = 0,
219 };
209220 try writeDylibLC(.{
210221 .cmd = .ID_DYLIB,
211222 .name = install_name,
212 .current_version = curr.value,
213 .compatibility_version = compat.value,
223 .current_version = @as(u32, @intCast(curr.major << 16 | curr.minor << 8 | curr.patch)),
224 .compatibility_version = @as(u32, @intCast(compat.major << 16 | compat.minor << 8 | compat.patch)),
214225 }, writer);
215226}
216227
......@@ -235,32 +246,38 @@ pub fn writeRpathLCs(rpaths: []const []const u8, writer: anytype) !void {
235246 }
236247}
237248
238pub fn writeVersionMinLC(platform: Options.Platform, sdk_version: ?Options.Version, writer: anytype) !void {
239 const cmd: macho.LC = switch (platform.platform) {
240 .MACOS => .VERSION_MIN_MACOSX,
241 .IOS, .IOSSIMULATOR => .VERSION_MIN_IPHONEOS,
242 .TVOS, .TVOSSIMULATOR => .VERSION_MIN_TVOS,
243 .WATCHOS, .WATCHOSSIMULATOR => .VERSION_MIN_WATCHOS,
249pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: anytype) !void {
250 const cmd: macho.LC = switch (platform.os_tag) {
251 .macos => .VERSION_MIN_MACOSX,
252 .ios => .VERSION_MIN_IPHONEOS,
253 .tvos => .VERSION_MIN_TVOS,
254 .watchos => .VERSION_MIN_WATCHOS,
244255 else => unreachable,
245256 };
246257 try writer.writeAll(mem.asBytes(&macho.version_min_command{
247258 .cmd = cmd,
248 .version = platform.version.value,
249 .sdk = if (sdk_version) |ver| ver.value else platform.version.value,
259 .version = platform.toAppleVersion(),
260 .sdk = if (sdk_version) |ver|
261 MachO.semanticVersionToAppleVersion(ver)
262 else
263 platform.toAppleVersion(),
250264 }));
251265}
252266
253pub fn writeBuildVersionLC(platform: Options.Platform, sdk_version: ?Options.Version, writer: anytype) !void {
267pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: anytype) !void {
254268 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
255269 try writer.writeStruct(macho.build_version_command{
256270 .cmdsize = cmdsize,
257 .platform = platform.platform,
258 .minos = platform.version.value,
259 .sdk = if (sdk_version) |ver| ver.value else platform.version.value,
271 .platform = platform.toApplePlatform(),
272 .minos = platform.toAppleVersion(),
273 .sdk = if (sdk_version) |ver|
274 MachO.semanticVersionToAppleVersion(ver)
275 else
276 platform.toAppleVersion(),
260277 .ntools = 1,
261278 });
262279 try writer.writeAll(mem.asBytes(&macho.build_tool_version{
263 .tool = @as(macho.TOOL, @enumFromInt(0x6)),
280 .tool = .ZIG,
264281 .version = 0x0,
265282 }));
266283}
src/link/MachO/uuid.zig+7-13
......@@ -4,13 +4,7 @@
44/// and we will use it too as it seems accepted by Apple OSes.
55/// TODO LLD also hashes the output filename to disambiguate between same builds with different
66/// output files. Should we also do that?
7pub fn calcUuid(
8 allocator: Allocator,
9 thread_pool: *ThreadPool,
10 file: fs.File,
11 file_size: u64,
12 out: *[Md5.digest_length]u8,
13) !void {
7pub fn calcUuid(comp: *const Compilation, file: fs.File, file_size: u64, out: *[Md5.digest_length]u8) !void {
148 const tracy = trace(@src());
159 defer tracy.end();
1610
......@@ -18,17 +12,17 @@ pub fn calcUuid(
1812 const num_chunks: usize = std.math.cast(usize, @divTrunc(file_size, chunk_size)) orelse return error.Overflow;
1913 const actual_num_chunks = if (@rem(file_size, chunk_size) > 0) num_chunks + 1 else num_chunks;
2014
21 const hashes = try allocator.alloc([Md5.digest_length]u8, actual_num_chunks);
22 defer allocator.free(hashes);
15 const hashes = try comp.gpa.alloc([Md5.digest_length]u8, actual_num_chunks);
16 defer comp.gpa.free(hashes);
2317
24 var hasher = Hasher(Md5){ .allocator = allocator, .thread_pool = thread_pool };
18 var hasher = Hasher(Md5){ .allocator = comp.gpa, .thread_pool = comp.thread_pool };
2519 try hasher.hash(file, hashes, .{
2620 .chunk_size = chunk_size,
2721 .max_file_size = file_size,
2822 });
2923
30 const final_buffer = try allocator.alloc(u8, actual_num_chunks * Md5.digest_length);
31 defer allocator.free(final_buffer);
24 const final_buffer = try comp.gpa.alloc(u8, actual_num_chunks * Md5.digest_length);
25 defer comp.gpa.free(final_buffer);
3226
3327 for (hashes, 0..) |hash, i| {
3428 @memcpy(final_buffer[i * Md5.digest_length ..][0..Md5.digest_length], &hash);
......@@ -49,7 +43,7 @@ const mem = std.mem;
4943const std = @import("std");
5044const trace = @import("../../tracy.zig").trace;
5145
52const Allocator = mem.Allocator;
46const Compilation = @import("../../Compilation.zig");
5347const Md5 = std.crypto.hash.Md5;
5448const Hasher = @import("hasher.zig").ParallelHasher;
5549const ThreadPool = std.Thread.Pool;