| ... | @@ -540,7 +540,63 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node | ... | @@ -540,7 +540,63 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 540 | | 540 | |
| 541 | state_log.debug("{}", .{self.dumpState()}); | 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 | /// --verbose-link output | 602 | /// --verbose-link output |
| ... | @@ -2186,6 +2242,646 @@ fn allocateSyntheticSymbols(self: *MachO) void { | ... | @@ -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 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { | 2885 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { |
| 2190 | _ = self; | 2886 | _ = self; |
| 2191 | _ = atom_index; | 2887 | _ = atom_index; |
| ... | @@ -3194,7 +3890,7 @@ const supported_platforms = [_]SupportedPlatforms{ | ... | @@ -3194,7 +3890,7 @@ const supported_platforms = [_]SupportedPlatforms{ |
| 3194 | }; | 3890 | }; |
| 3195 | // zig fmt: on | 3891 | // zig fmt: on |
| 3196 | | 3892 | |
| 3197 | inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { | 3893 | pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 { |
| 3198 | const major = version.major; | 3894 | const major = version.major; |
| 3199 | const minor = version.minor; | 3895 | const minor = version.minor; |
| 3200 | const patch = version.patch; | 3896 | const patch = version.patch; |