| ... | ... | @@ -540,7 +540,63 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 540 | 540 | |
| 541 | 541 | state_log.debug("{}", .{self.dumpState()}); |
| 542 | 542 | |
| 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 | } |
| 544 | 600 | } |
| 545 | 601 | |
| 546 | 602 | /// --verbose-link output |
| ... | ... | @@ -2186,6 +2242,646 @@ fn allocateSyntheticSymbols(self: *MachO) void { |
| 2186 | 2242 | } |
| 2187 | 2243 | } |
| 2188 | 2244 | |
| 2245 | fn 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 | |
| 2270 | fn 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 | |
| 2313 | fn 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 | |
| 2357 | fn 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 | |
| 2380 | fn 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 | |
| 2392 | fn 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 | |
| 2453 | fn 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 | |
| 2509 | fn 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 | |
| 2516 | pub 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 | |
| 2562 | pub 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 | |
| 2624 | pub 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 | |
| 2651 | fn 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 | |
| 2668 | pub 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 | |
| 2675 | fn 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 | |
| 2782 | fn 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 | |
| 2834 | fn 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 | |
| 2844 | pub 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 | |
| 2861 | pub 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 | |
| 2189 | 2885 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { |
| 2190 | 2886 | _ = self; |
| 2191 | 2887 | _ = atom_index; |
| ... | ... | @@ -3194,7 +3890,7 @@ const supported_platforms = [_]SupportedPlatforms{ |
| 3194 | 3890 | }; |
| 3195 | 3891 | // zig fmt: on |
| 3196 | 3892 | |
| 3197 | | inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { |
| 3893 | pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { |
| 3198 | 3894 | const major = version.major; |
| 3199 | 3895 | const minor = version.minor; |
| 3200 | 3896 | const patch = version.patch; |