| ... | @@ -947,119 +947,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -947,119 +947,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 947 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | 947 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 948 | } | 948 | } |
| 949 | } | 949 | } |
| 950 | | | |
| 951 | // At this stage, LLD has done its job. It is time to patch the resultant | | |
| 952 | // binaries up! | | |
| 953 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); | | |
| 954 | try self.parseFromFile(out_file); | | |
| 955 | | | |
| 956 | if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) { | | |
| 957 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | | |
| 958 | const text_section = text_segment.sections.items[self.text_section_index.?]; | | |
| 959 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | | |
| 960 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); | | |
| 961 | | | |
| 962 | if (needed_size + after_last_cmd_offset > text_section.offset) { | | |
| 963 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | | |
| 964 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | | |
| 965 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | | |
| 966 | return error.NotEnoughPadding; | | |
| 967 | } | | |
| 968 | | | |
| 969 | // Calculate next available dylib ordinal. | | |
| 970 | const next_ordinal = blk: { | | |
| 971 | var ordinal: u32 = 1; | | |
| 972 | for (self.load_commands.items) |cmd| { | | |
| 973 | switch (cmd) { | | |
| 974 | .Dylib => ordinal += 1, | | |
| 975 | else => {}, | | |
| 976 | } | | |
| 977 | } | | |
| 978 | break :blk ordinal; | | |
| 979 | }; | | |
| 980 | | | |
| 981 | // Add load dylib load command | | |
| 982 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | | |
| 983 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | | |
| 984 | u64, | | |
| 985 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | | |
| 986 | @sizeOf(u64), | | |
| 987 | )); | | |
| 988 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | | |
| 989 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. | | |
| 990 | const min_version = 0x0; | | |
| 991 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | | |
| 992 | .cmd = macho.LC_LOAD_DYLIB, | | |
| 993 | .cmdsize = cmdsize, | | |
| 994 | .dylib = .{ | | |
| 995 | .name = @sizeOf(macho.dylib_command), | | |
| 996 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | | |
| 997 | .current_version = min_version, | | |
| 998 | .compatibility_version = min_version, | | |
| 999 | }, | | |
| 1000 | }); | | |
| 1001 | dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | | |
| 1002 | mem.set(u8, dylib_cmd.data, 0); | | |
| 1003 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | | |
| 1004 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | | |
| 1005 | self.header_dirty = true; | | |
| 1006 | self.load_commands_dirty = true; | | |
| 1007 | | | |
| 1008 | if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) { | | |
| 1009 | log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{}); | | |
| 1010 | log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{}); | | |
| 1011 | return error.NoSymbolTableFound; | | |
| 1012 | } | | |
| 1013 | | | |
| 1014 | // Patch dyld info | | |
| 1015 | try self.fixupBindInfo(next_ordinal); | | |
| 1016 | try self.fixupLazyBindInfo(next_ordinal); | | |
| 1017 | | | |
| 1018 | // Write updated load commands and the header | | |
| 1019 | try self.writeLoadCommands(); | | |
| 1020 | try self.writeHeader(); | | |
| 1021 | | | |
| 1022 | assert(!self.header_dirty); | | |
| 1023 | assert(!self.load_commands_dirty); | | |
| 1024 | } | | |
| 1025 | if (self.code_signature_cmd_index == null) outer: { | | |
| 1026 | if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets. | | |
| 1027 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | | |
| 1028 | const text_section = text_segment.sections.items[self.text_section_index.?]; | | |
| 1029 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | | |
| 1030 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); | | |
| 1031 | | | |
| 1032 | if (needed_size + after_last_cmd_offset > text_section.offset) { | | |
| 1033 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | | |
| 1034 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | | |
| 1035 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | | |
| 1036 | return error.NotEnoughPadding; | | |
| 1037 | } | | |
| 1038 | | | |
| 1039 | // Add code signature load command | | |
| 1040 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | | |
| 1041 | try self.load_commands.append(self.base.allocator, .{ | | |
| 1042 | .LinkeditData = .{ | | |
| 1043 | .cmd = macho.LC_CODE_SIGNATURE, | | |
| 1044 | .cmdsize = @sizeOf(macho.linkedit_data_command), | | |
| 1045 | .dataoff = 0, | | |
| 1046 | .datasize = 0, | | |
| 1047 | }, | | |
| 1048 | }); | | |
| 1049 | self.header_dirty = true; | | |
| 1050 | self.load_commands_dirty = true; | | |
| 1051 | | | |
| 1052 | // Pad out space for code signature | | |
| 1053 | try self.writeCodeSignaturePadding(); | | |
| 1054 | // Write updated load commands and the header | | |
| 1055 | try self.writeLoadCommands(); | | |
| 1056 | try self.writeHeader(); | | |
| 1057 | // Generate adhoc code signature | | |
| 1058 | try self.writeCodeSignature(); | | |
| 1059 | | | |
| 1060 | assert(!self.header_dirty); | | |
| 1061 | assert(!self.load_commands_dirty); | | |
| 1062 | } | | |
| 1063 | } | 950 | } |
| 1064 | } | 951 | } |
| 1065 | | 952 | |
| ... | @@ -3375,177 +3262,6 @@ fn writeHeader(self: *MachO) !void { | ... | @@ -3375,177 +3262,6 @@ fn writeHeader(self: *MachO) !void { |
| 3375 | self.header_dirty = false; | 3262 | self.header_dirty = false; |
| 3376 | } | 3263 | } |
| 3377 | | 3264 | |
| 3378 | /// Parse MachO contents from existing binary file. | | |
| 3379 | fn parseFromFile(self: *MachO, file: fs.File) !void { | | |
| 3380 | self.base.file = file; | | |
| 3381 | var reader = file.reader(); | | |
| 3382 | const header = try reader.readStruct(macho.mach_header_64); | | |
| 3383 | try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds); | | |
| 3384 | var i: u16 = 0; | | |
| 3385 | while (i < header.ncmds) : (i += 1) { | | |
| 3386 | const cmd = try LoadCommand.read(self.base.allocator, reader); | | |
| 3387 | switch (cmd.cmd()) { | | |
| 3388 | macho.LC_SEGMENT_64 => { | | |
| 3389 | const x = cmd.Segment; | | |
| 3390 | if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) { | | |
| 3391 | self.pagezero_segment_cmd_index = i; | | |
| 3392 | } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) { | | |
| 3393 | self.linkedit_segment_cmd_index = i; | | |
| 3394 | } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) { | | |
| 3395 | self.text_segment_cmd_index = i; | | |
| 3396 | for (x.sections.items) |sect, j| { | | |
| 3397 | if (parseAndCmpName(&sect.sectname, "__text")) { | | |
| 3398 | self.text_section_index = @intCast(u16, j); | | |
| 3399 | } | | |
| 3400 | } | | |
| 3401 | } else if (parseAndCmpName(&x.inner.segname, "__DATA")) { | | |
| 3402 | self.data_segment_cmd_index = i; | | |
| 3403 | } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) { | | |
| 3404 | self.data_const_segment_cmd_index = i; | | |
| 3405 | } | | |
| 3406 | }, | | |
| 3407 | macho.LC_DYLD_INFO_ONLY => { | | |
| 3408 | self.dyld_info_cmd_index = i; | | |
| 3409 | }, | | |
| 3410 | macho.LC_SYMTAB => { | | |
| 3411 | self.symtab_cmd_index = i; | | |
| 3412 | }, | | |
| 3413 | macho.LC_DYSYMTAB => { | | |
| 3414 | self.dysymtab_cmd_index = i; | | |
| 3415 | }, | | |
| 3416 | macho.LC_LOAD_DYLINKER => { | | |
| 3417 | self.dylinker_cmd_index = i; | | |
| 3418 | }, | | |
| 3419 | macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => { | | |
| 3420 | self.version_min_cmd_index = i; | | |
| 3421 | }, | | |
| 3422 | macho.LC_SOURCE_VERSION => { | | |
| 3423 | self.source_version_cmd_index = i; | | |
| 3424 | }, | | |
| 3425 | macho.LC_UUID => { | | |
| 3426 | self.uuid_cmd_index = i; | | |
| 3427 | }, | | |
| 3428 | macho.LC_MAIN => { | | |
| 3429 | self.main_cmd_index = i; | | |
| 3430 | }, | | |
| 3431 | macho.LC_LOAD_DYLIB => { | | |
| 3432 | const x = cmd.Dylib; | | |
| 3433 | if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) { | | |
| 3434 | self.libsystem_cmd_index = i; | | |
| 3435 | } | | |
| 3436 | }, | | |
| 3437 | macho.LC_FUNCTION_STARTS => { | | |
| 3438 | self.function_starts_cmd_index = i; | | |
| 3439 | }, | | |
| 3440 | macho.LC_DATA_IN_CODE => { | | |
| 3441 | self.data_in_code_cmd_index = i; | | |
| 3442 | }, | | |
| 3443 | macho.LC_CODE_SIGNATURE => { | | |
| 3444 | self.code_signature_cmd_index = i; | | |
| 3445 | }, | | |
| 3446 | else => { | | |
| 3447 | log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | | |
| 3448 | }, | | |
| 3449 | } | | |
| 3450 | self.load_commands.appendAssumeCapacity(cmd); | | |
| 3451 | } | | |
| 3452 | self.header = header; | | |
| 3453 | } | | |
| 3454 | | | |
| 3455 | fn parseAndCmpName(name: []const u8, needle: []const u8) bool { | | |
| 3456 | const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; | | |
| 3457 | return mem.eql(u8, name[0..len], needle); | | |
| 3458 | } | | |
| 3459 | | | |
| 3460 | fn parseSymbolTable(self: *MachO) !void { | | |
| 3461 | const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 3462 | const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | | |
| 3463 | | | |
| 3464 | var buffer = try self.base.allocator.alloc(macho.nlist_64, symtab.nsyms); | | |
| 3465 | defer self.base.allocator.free(buffer); | | |
| 3466 | const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff); | | |
| 3467 | assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len); | | |
| 3468 | | | |
| 3469 | try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym); | | |
| 3470 | try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym); | | |
| 3471 | try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym); | | |
| 3472 | | | |
| 3473 | self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]); | | |
| 3474 | self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]); | | |
| 3475 | self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]); | | |
| 3476 | } | | |
| 3477 | | | |
| 3478 | fn parseStringTable(self: *MachO) !void { | | |
| 3479 | const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 3480 | | | |
| 3481 | var buffer = try self.base.allocator.alloc(u8, symtab.strsize); | | |
| 3482 | defer self.base.allocator.free(buffer); | | |
| 3483 | const nread = try self.base.file.?.preadAll(buffer, symtab.stroff); | | |
| 3484 | assert(nread == buffer.len); | | |
| 3485 | | | |
| 3486 | try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize); | | |
| 3487 | self.string_table.appendSliceAssumeCapacity(buffer); | | |
| 3488 | } | | |
| 3489 | | | |
| 3490 | fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void { | | |
| 3491 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | | |
| 3492 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); | | |
| 3493 | defer self.base.allocator.free(buffer); | | |
| 3494 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); | | |
| 3495 | assert(nread == buffer.len); | | |
| 3496 | try self.fixupInfoCommon(buffer, dylib_ordinal); | | |
| 3497 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); | | |
| 3498 | } | | |
| 3499 | | | |
| 3500 | fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void { | | |
| 3501 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | | |
| 3502 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); | | |
| 3503 | defer self.base.allocator.free(buffer); | | |
| 3504 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); | | |
| 3505 | assert(nread == buffer.len); | | |
| 3506 | try self.fixupInfoCommon(buffer, dylib_ordinal); | | |
| 3507 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | | |
| 3508 | } | | |
| 3509 | | | |
| 3510 | fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { | | |
| 3511 | var stream = std.io.fixedBufferStream(buffer); | | |
| 3512 | var reader = stream.reader(); | | |
| 3513 | | | |
| 3514 | while (true) { | | |
| 3515 | const inst = reader.readByte() catch |err| switch (err) { | | |
| 3516 | error.EndOfStream => break, | | |
| 3517 | else => return err, | | |
| 3518 | }; | | |
| 3519 | const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; | | |
| 3520 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; | | |
| 3521 | | | |
| 3522 | switch (opcode) { | | |
| 3523 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { | | |
| 3524 | var next = try reader.readByte(); | | |
| 3525 | while (next != @as(u8, 0)) { | | |
| 3526 | next = try reader.readByte(); | | |
| 3527 | } | | |
| 3528 | }, | | |
| 3529 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { | | |
| 3530 | _ = try std.leb.readULEB128(u64, reader); | | |
| 3531 | }, | | |
| 3532 | macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { | | |
| 3533 | // Perform the fixup. | | |
| 3534 | try stream.seekBy(-1); | | |
| 3535 | var writer = stream.writer(); | | |
| 3536 | try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal)); | | |
| 3537 | }, | | |
| 3538 | macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { | | |
| 3539 | _ = try std.leb.readULEB128(u64, reader); | | |
| 3540 | }, | | |
| 3541 | macho.BIND_OPCODE_SET_ADDEND_SLEB => { | | |
| 3542 | _ = try std.leb.readILEB128(i64, reader); | | |
| 3543 | }, | | |
| 3544 | else => {}, | | |
| 3545 | } | | |
| 3546 | } | | |
| 3547 | } | | |
| 3548 | | | |
| 3549 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | 3265 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 3550 | // TODO https://github.com/ziglang/zig/issues/1284 | 3266 | // TODO https://github.com/ziglang/zig/issues/1284 |
| 3551 | return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch | 3267 | return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch |