| author | |
| committer | |
| log | f9798108f8434f277de6089502446f2544ee98b3 |
| tree | eba6945383fefa59a8858f7eec8beee3cfca7731 |
| parent | e5b476209a1215a03164890d331e2013f20882a6 |
| parent | 5533f77054acad376f2fce0d529569a7449e9949 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
macho+zld: bringing zld to stage2 MachO backend - simplicity is key!17 files changed, 5318 insertions(+), 5673 deletions(-)
CMakeLists.txt+1-5| ... | ... | @@ -581,14 +581,10 @@ set(ZIG_STAGE2_SOURCES |
| 581 | 581 | "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" |
| 582 | 582 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig" |
| 583 | 583 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" |
| 584 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" | |
| 584 | "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig" | |
| 585 | 585 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
| 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" | |
| 587 | 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 588 | 587 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 589 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" | |
| 590 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/aarch64.zig" | |
| 591 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/x86_64.zig" | |
| 592 | 588 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 593 | 589 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 594 | 590 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
src/Compilation.zig+11-6| ... | ... | @@ -894,6 +894,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 894 | 894 | |
| 895 | 895 | // Make a decision on whether to use LLD or our own linker. |
| 896 | 896 | const use_lld = options.use_lld orelse blk: { |
| 897 | if (options.target.isDarwin()) { | |
| 898 | break :blk false; | |
| 899 | } | |
| 900 | ||
| 897 | 901 | if (!build_options.have_llvm) |
| 898 | 902 | break :blk false; |
| 899 | 903 | |
| ... | ... | @@ -931,11 +935,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 931 | 935 | break :blk false; |
| 932 | 936 | }; |
| 933 | 937 | |
| 934 | const darwin_can_use_system_sdk = | |
| 935 | // comptime conditions | |
| 936 | ((build_options.have_llvm and comptime std.Target.current.isDarwin()) and | |
| 937 | // runtime conditions | |
| 938 | (use_lld and builtin.os.tag == .macos and options.target.isDarwin())); | |
| 938 | const darwin_can_use_system_sdk = blk: { | |
| 939 | if (comptime !std.Target.current.isDarwin()) break :blk false; | |
| 940 | break :blk std.builtin.os.tag == .macos and options.target.isDarwin(); | |
| 941 | }; | |
| 939 | 942 | |
| 940 | 943 | const sysroot = blk: { |
| 941 | 944 | if (options.sysroot) |sysroot| { |
| ... | ... | @@ -952,10 +955,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 952 | 955 | |
| 953 | 956 | const lto = blk: { |
| 954 | 957 | if (options.want_lto) |explicit| { |
| 955 | if (!use_lld) | |
| 958 | if (!use_lld and !options.target.isDarwin()) | |
| 956 | 959 | return error.LtoUnavailableWithoutLld; |
| 957 | 960 | break :blk explicit; |
| 958 | 961 | } else if (!use_lld) { |
| 962 | // TODO zig ld LTO support | |
| 963 | // See https://github.com/ziglang/zig/issues/8680 | |
| 959 | 964 | break :blk false; |
| 960 | 965 | } else if (options.c_source_files.len == 0) { |
| 961 | 966 | break :blk false; |
src/codegen.zig+75-41| ... | ... | @@ -2590,9 +2590,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2590 | 2590 | const got_addr = blk: { |
| 2591 | 2591 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; |
| 2592 | 2592 | const got = seg.sections.items[macho_file.got_section_index.?]; |
| 2593 | break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); | |
| 2593 | const got_index = macho_file.got_entries_map.get(.{ | |
| 2594 | .where = .local, | |
| 2595 | .where_index = func.owner_decl.link.macho.local_sym_index, | |
| 2596 | }) orelse unreachable; | |
| 2597 | break :blk got.addr + got_index * @sizeOf(u64); | |
| 2594 | 2598 | }; |
| 2595 | log.debug("got_addr = 0x{x}", .{got_addr}); | |
| 2596 | 2599 | switch (arch) { |
| 2597 | 2600 | .x86_64 => { |
| 2598 | 2601 | try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr }); |
| ... | ... | @@ -2609,37 +2612,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2609 | 2612 | } |
| 2610 | 2613 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 2611 | 2614 | const decl = func_payload.data; |
| 2612 | const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); | |
| 2613 | defer self.bin_file.allocator.free(decl_name); | |
| 2614 | const already_defined = macho_file.lazy_imports.contains(decl_name); | |
| 2615 | const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index| | |
| 2616 | @intCast(u32, index) | |
| 2617 | else | |
| 2618 | try macho_file.addExternSymbol(decl_name); | |
| 2619 | const start = self.code.items.len; | |
| 2620 | const len: usize = blk: { | |
| 2615 | const where_index = try macho_file.addExternFn(mem.spanZ(decl.name)); | |
| 2616 | const offset = blk: { | |
| 2621 | 2617 | switch (arch) { |
| 2622 | 2618 | .x86_64 => { |
| 2623 | 2619 | // callq |
| 2624 | 2620 | try self.code.ensureCapacity(self.code.items.len + 5); |
| 2625 | 2621 | self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 }); |
| 2626 | break :blk 5; | |
| 2622 | break :blk @intCast(u32, self.code.items.len) - 4; | |
| 2627 | 2623 | }, |
| 2628 | 2624 | .aarch64 => { |
| 2625 | const offset = @intCast(u32, self.code.items.len); | |
| 2629 | 2626 | // bl |
| 2630 | writeInt(u32, try self.code.addManyAsArray(4), 0); | |
| 2631 | break :blk 4; | |
| 2627 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); | |
| 2628 | break :blk offset; | |
| 2632 | 2629 | }, |
| 2633 | 2630 | else => unreachable, // unsupported architecture on MachO |
| 2634 | 2631 | } |
| 2635 | 2632 | }; |
| 2636 | try macho_file.stub_fixups.append(self.bin_file.allocator, .{ | |
| 2637 | .symbol = symbol, | |
| 2638 | .already_defined = already_defined, | |
| 2639 | .start = start, | |
| 2640 | .len = len, | |
| 2633 | // Add relocation to the decl. | |
| 2634 | try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{ | |
| 2635 | .offset = offset, | |
| 2636 | .where = .import, | |
| 2637 | .where_index = where_index, | |
| 2638 | .payload = .{ .branch = .{ | |
| 2639 | .arch = arch, | |
| 2640 | } }, | |
| 2641 | 2641 | }); |
| 2642 | // We mark the space and fix it up later. | |
| 2643 | 2642 | } else { |
| 2644 | 2643 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 2645 | 2644 | } |
| ... | ... | @@ -4144,19 +4143,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4144 | 4143 | .memory => |addr| { |
| 4145 | 4144 | if (self.bin_file.options.pie) { |
| 4146 | 4145 | // PC-relative displacement to the entry in the GOT table. |
| 4147 | // TODO we should come up with our own, backend independent relocation types | |
| 4148 | // which each backend (Elf, MachO, etc.) would then translate into an actual | |
| 4149 | // fixup when linking. | |
| 4150 | // adrp reg, pages | |
| 4151 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4152 | try macho_file.pie_fixups.append(self.bin_file.allocator, .{ | |
| 4153 | .target_addr = addr, | |
| 4154 | .offset = self.code.items.len, | |
| 4155 | .size = 4, | |
| 4156 | }); | |
| 4157 | } else { | |
| 4158 | return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{}); | |
| 4159 | } | |
| 4146 | // adrp | |
| 4147 | const offset = @intCast(u32, self.code.items.len); | |
| 4160 | 4148 | mem.writeIntLittle( |
| 4161 | 4149 | u32, |
| 4162 | 4150 | try self.code.addManyAsArray(4), |
| ... | ... | @@ -4169,6 +4157,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4169 | 4157 | .offset = Instruction.LoadStoreOffset.imm(0), |
| 4170 | 4158 | }, |
| 4171 | 4159 | }).toU32()); |
| 4160 | ||
| 4161 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4162 | // TODO this is super awkward. We are reversing the address of the GOT entry here. | |
| 4163 | // We should probably have it cached or move the reloc adding somewhere else. | |
| 4164 | const got_addr = blk: { | |
| 4165 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | |
| 4166 | const got = seg.sections.items[macho_file.got_section_index.?]; | |
| 4167 | break :blk got.addr; | |
| 4168 | }; | |
| 4169 | const where_index = blk: for (macho_file.got_entries.items) |key, id| { | |
| 4170 | if (got_addr + id * @sizeOf(u64) == addr) break :blk key.where_index; | |
| 4171 | } else unreachable; | |
| 4172 | const decl = macho_file.active_decl.?; | |
| 4173 | // Page reloc for adrp instruction. | |
| 4174 | try decl.link.macho.relocs.append(self.bin_file.allocator, .{ | |
| 4175 | .offset = offset, | |
| 4176 | .where = .local, | |
| 4177 | .where_index = where_index, | |
| 4178 | .payload = .{ .page = .{ .kind = .got } }, | |
| 4179 | }); | |
| 4180 | // Pageoff reloc for adrp instruction. | |
| 4181 | try decl.link.macho.relocs.append(self.bin_file.allocator, .{ | |
| 4182 | .offset = offset + 4, | |
| 4183 | .where = .local, | |
| 4184 | .where_index = where_index, | |
| 4185 | .payload = .{ .page_off = .{ .kind = .got } }, | |
| 4186 | }); | |
| 4187 | } else { | |
| 4188 | return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{}); | |
| 4189 | } | |
| 4172 | 4190 | } else { |
| 4173 | 4191 | // The value is in memory at a hard-coded address. |
| 4174 | 4192 | // If the type is a pointer, it means the pointer address is at this memory location. |
| ... | ... | @@ -4421,14 +4439,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4421 | 4439 | encoder.modRm_RIPDisp32(reg.low_id()); |
| 4422 | 4440 | encoder.disp32(0); |
| 4423 | 4441 | |
| 4424 | // TODO we should come up with our own, backend independent relocation types | |
| 4425 | // which each backend (Elf, MachO, etc.) would then translate into an actual | |
| 4426 | // fixup when linking. | |
| 4442 | const offset = @intCast(u32, self.code.items.len); | |
| 4443 | ||
| 4427 | 4444 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 4428 | try macho_file.pie_fixups.append(self.bin_file.allocator, .{ | |
| 4429 | .target_addr = x, | |
| 4430 | .offset = self.code.items.len - 4, | |
| 4431 | .size = 4, | |
| 4445 | // TODO this is super awkward. We are reversing the address of the GOT entry here. | |
| 4446 | // We should probably have it cached or move the reloc adding somewhere else. | |
| 4447 | const got_addr = blk: { | |
| 4448 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | |
| 4449 | const got = seg.sections.items[macho_file.got_section_index.?]; | |
| 4450 | break :blk got.addr; | |
| 4451 | }; | |
| 4452 | const where_index = blk: for (macho_file.got_entries.items) |key, id| { | |
| 4453 | if (got_addr + id * @sizeOf(u64) == x) break :blk key.where_index; | |
| 4454 | } else unreachable; | |
| 4455 | const decl = macho_file.active_decl.?; | |
| 4456 | // Load reloc for LEA instruction. | |
| 4457 | try decl.link.macho.relocs.append(self.bin_file.allocator, .{ | |
| 4458 | .offset = offset - 4, | |
| 4459 | .where = .local, | |
| 4460 | .where_index = where_index, | |
| 4461 | .payload = .{ .load = .{ .kind = .got } }, | |
| 4432 | 4462 | }); |
| 4433 | 4463 | } else { |
| 4434 | 4464 | return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{}); |
| ... | ... | @@ -4647,7 +4677,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4647 | 4677 | const got_addr = blk: { |
| 4648 | 4678 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; |
| 4649 | 4679 | const got = seg.sections.items[macho_file.got_section_index.?]; |
| 4650 | break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; | |
| 4680 | const got_index = macho_file.got_entries_map.get(.{ | |
| 4681 | .where = .local, | |
| 4682 | .where_index = decl.link.macho.local_sym_index, | |
| 4683 | }) orelse unreachable; | |
| 4684 | break :blk got.addr + got_index * ptr_bytes; | |
| 4651 | 4685 | }; |
| 4652 | 4686 | return MCValue{ .memory = got_addr }; |
| 4653 | 4687 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
src/link.zig+1-1| ... | ... | @@ -543,7 +543,7 @@ pub const File = struct { |
| 543 | 543 | } |
| 544 | 544 | } |
| 545 | 545 | |
| 546 | fn linkAsArchive(base: *File, comp: *Compilation) !void { | |
| 546 | pub fn linkAsArchive(base: *File, comp: *Compilation) !void { | |
| 547 | 547 | const tracy = trace(@src()); |
| 548 | 548 | defer tracy.end(); |
| 549 | 549 |
src/link/MachO.zig+3304-727| ... | ... | @@ -1,38 +1,43 @@ |
| 1 | 1 | const MachO = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | |
| 4 | 5 | const builtin = @import("builtin"); |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const fmt = std.fmt; |
| 8 | 8 | const fs = std.fs; |
| 9 | 9 | const log = std.log.scoped(.link); |
| 10 | 10 | const macho = std.macho; |
| 11 | const codegen = @import("../codegen.zig"); | |
| 12 | const aarch64 = @import("../codegen/aarch64.zig"); | |
| 13 | 11 | const math = std.math; |
| 14 | 12 | const mem = std.mem; |
| 15 | 13 | const meta = std.meta; |
| 16 | 14 | |
| 15 | const aarch64 = @import("../codegen/aarch64.zig"); | |
| 17 | 16 | const bind = @import("MachO/bind.zig"); |
| 18 | const trace = @import("../tracy.zig").trace; | |
| 19 | const build_options = @import("build_options"); | |
| 20 | const Module = @import("../Module.zig"); | |
| 21 | const Compilation = @import("../Compilation.zig"); | |
| 17 | const codegen = @import("../codegen.zig"); | |
| 18 | const commands = @import("MachO/commands.zig"); | |
| 22 | 19 | const link = @import("../link.zig"); |
| 23 | const File = link.File; | |
| 24 | const Cache = @import("../Cache.zig"); | |
| 20 | const llvm_backend = @import("../codegen/llvm.zig"); | |
| 25 | 21 | const target_util = @import("../target.zig"); |
| 26 | const Air = @import("../Air.zig"); | |
| 27 | const Liveness = @import("../Liveness.zig"); | |
| 22 | const trace = @import("../tracy.zig").trace; | |
| 28 | 23 | |
| 29 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); | |
| 30 | const Trie = @import("MachO/Trie.zig"); | |
| 24 | const Air = @import("../Air.zig"); | |
| 25 | const Allocator = mem.Allocator; | |
| 26 | const Archive = @import("MachO/Archive.zig"); | |
| 27 | const Cache = @import("../Cache.zig"); | |
| 31 | 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 32 | const Zld = @import("MachO/Zld.zig"); | |
| 29 | const Compilation = @import("../Compilation.zig"); | |
| 30 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); | |
| 31 | const Dylib = @import("MachO/Dylib.zig"); | |
| 32 | const File = link.File; | |
| 33 | const Object = @import("MachO/Object.zig"); | |
| 34 | const Liveness = @import("../Liveness.zig"); | |
| 33 | 35 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 34 | ||
| 35 | usingnamespace @import("MachO/commands.zig"); | |
| 36 | const LoadCommand = commands.LoadCommand; | |
| 37 | const Module = @import("../Module.zig"); | |
| 38 | const SegmentCommand = commands.SegmentCommand; | |
| 39 | pub const TextBlock = @import("MachO/TextBlock.zig"); | |
| 40 | const Trie = @import("MachO/Trie.zig"); | |
| 36 | 41 | |
| 37 | 42 | pub const base_tag: File.Tag = File.Tag.macho; |
| 38 | 43 | |
| ... | ... | @@ -48,102 +53,131 @@ d_sym: ?DebugSymbols = null, |
| 48 | 53 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 49 | 54 | page_size: u16, |
| 50 | 55 | |
| 51 | /// Mach-O header | |
| 52 | header: ?macho.mach_header_64 = null, | |
| 53 | 56 | /// We commit 0x1000 = 4096 bytes of space to the header and |
| 54 | 57 | /// the table of load commands. This should be plenty for any |
| 55 | 58 | /// potential future extensions. |
| 56 | 59 | header_pad: u16 = 0x1000, |
| 57 | 60 | |
| 58 | /// Table of all load commands | |
| 61 | /// The absolute address of the entry point. | |
| 62 | entry_addr: ?u64 = null, | |
| 63 | ||
| 64 | objects: std.ArrayListUnmanaged(*Object) = .{}, | |
| 65 | archives: std.ArrayListUnmanaged(*Archive) = .{}, | |
| 66 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 67 | ||
| 68 | next_dylib_ordinal: u16 = 1, | |
| 69 | ||
| 59 | 70 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 60 | /// __PAGEZERO segment | |
| 71 | ||
| 61 | 72 | pagezero_segment_cmd_index: ?u16 = null, |
| 62 | /// __TEXT segment | |
| 63 | 73 | text_segment_cmd_index: ?u16 = null, |
| 64 | /// __DATA_CONST segment | |
| 65 | 74 | data_const_segment_cmd_index: ?u16 = null, |
| 66 | /// __DATA segment | |
| 67 | 75 | data_segment_cmd_index: ?u16 = null, |
| 68 | /// __LINKEDIT segment | |
| 69 | 76 | linkedit_segment_cmd_index: ?u16 = null, |
| 70 | /// Dyld info | |
| 71 | 77 | dyld_info_cmd_index: ?u16 = null, |
| 72 | /// Symbol table | |
| 73 | 78 | symtab_cmd_index: ?u16 = null, |
| 74 | /// Dynamic symbol table | |
| 75 | 79 | dysymtab_cmd_index: ?u16 = null, |
| 76 | /// Path to dyld linker | |
| 77 | 80 | dylinker_cmd_index: ?u16 = null, |
| 78 | /// Path to libSystem | |
| 79 | libsystem_cmd_index: ?u16 = null, | |
| 80 | /// Data-in-code section of __LINKEDIT segment | |
| 81 | 81 | data_in_code_cmd_index: ?u16 = null, |
| 82 | /// Address to entry point function | |
| 83 | 82 | function_starts_cmd_index: ?u16 = null, |
| 84 | /// Main/entry point | |
| 85 | /// Specifies offset wrt __TEXT segment start address to the main entry point | |
| 86 | /// of the binary. | |
| 87 | 83 | main_cmd_index: ?u16 = null, |
| 88 | /// Minimum OS version | |
| 84 | dylib_id_cmd_index: ?u16 = null, | |
| 89 | 85 | version_min_cmd_index: ?u16 = null, |
| 90 | /// Source version | |
| 91 | 86 | source_version_cmd_index: ?u16 = null, |
| 92 | /// UUID load command | |
| 93 | 87 | uuid_cmd_index: ?u16 = null, |
| 94 | /// Code signature | |
| 95 | 88 | code_signature_cmd_index: ?u16 = null, |
| 89 | /// Path to libSystem | |
| 90 | /// TODO this is obsolete, remove it. | |
| 91 | libsystem_cmd_index: ?u16 = null, | |
| 96 | 92 | |
| 97 | /// Index into __TEXT,__text section. | |
| 93 | // __TEXT segment sections | |
| 98 | 94 | text_section_index: ?u16 = null, |
| 99 | /// Index into __TEXT,__stubs section. | |
| 100 | 95 | stubs_section_index: ?u16 = null, |
| 101 | /// Index into __TEXT,__stub_helper section. | |
| 102 | 96 | stub_helper_section_index: ?u16 = null, |
| 103 | /// Index into __DATA_CONST,__got section. | |
| 97 | text_const_section_index: ?u16 = null, | |
| 98 | cstring_section_index: ?u16 = null, | |
| 99 | ustring_section_index: ?u16 = null, | |
| 100 | gcc_except_tab_section_index: ?u16 = null, | |
| 101 | unwind_info_section_index: ?u16 = null, | |
| 102 | eh_frame_section_index: ?u16 = null, | |
| 103 | ||
| 104 | objc_methlist_section_index: ?u16 = null, | |
| 105 | objc_methname_section_index: ?u16 = null, | |
| 106 | objc_methtype_section_index: ?u16 = null, | |
| 107 | objc_classname_section_index: ?u16 = null, | |
| 108 | ||
| 109 | // __DATA_CONST segment sections | |
| 104 | 110 | got_section_index: ?u16 = null, |
| 105 | /// Index into __DATA,__la_symbol_ptr section. | |
| 111 | mod_init_func_section_index: ?u16 = null, | |
| 112 | mod_term_func_section_index: ?u16 = null, | |
| 113 | data_const_section_index: ?u16 = null, | |
| 114 | ||
| 115 | objc_cfstring_section_index: ?u16 = null, | |
| 116 | objc_classlist_section_index: ?u16 = null, | |
| 117 | objc_imageinfo_section_index: ?u16 = null, | |
| 118 | ||
| 119 | // __DATA segment sections | |
| 120 | tlv_section_index: ?u16 = null, | |
| 121 | tlv_data_section_index: ?u16 = null, | |
| 122 | tlv_bss_section_index: ?u16 = null, | |
| 106 | 123 | la_symbol_ptr_section_index: ?u16 = null, |
| 107 | /// Index into __DATA,__data section. | |
| 108 | 124 | data_section_index: ?u16 = null, |
| 109 | /// The absolute address of the entry point. | |
| 110 | entry_addr: ?u64 = null, | |
| 125 | bss_section_index: ?u16 = null, | |
| 126 | common_section_index: ?u16 = null, | |
| 127 | ||
| 128 | objc_const_section_index: ?u16 = null, | |
| 129 | objc_selrefs_section_index: ?u16 = null, | |
| 130 | objc_classrefs_section_index: ?u16 = null, | |
| 131 | objc_data_section_index: ?u16 = null, | |
| 111 | 132 | |
| 112 | /// Table of all local symbols | |
| 113 | /// Internally references string table for names (which are optional). | |
| 114 | 133 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 115 | /// Table of all global symbols | |
| 116 | 134 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 117 | /// Table of all extern nonlazy symbols, indexed by name. | |
| 118 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 119 | /// Table of all extern lazy symbols, indexed by name. | |
| 120 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 135 | imports: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 136 | undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 137 | tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 138 | symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{}, | |
| 121 | 139 | |
| 122 | 140 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 123 | 141 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 124 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 125 | 142 | |
| 126 | 143 | stub_helper_stubs_start_off: ?u64 = null, |
| 127 | 144 | |
| 128 | /// Table of symbol names aka the string table. | |
| 129 | string_table: std.ArrayListUnmanaged(u8) = .{}, | |
| 130 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, | |
| 145 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 146 | strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, | |
| 147 | ||
| 148 | got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{}, | |
| 149 | got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{}, | |
| 150 | ||
| 151 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 131 | 152 | |
| 132 | /// Table of GOT entries. | |
| 133 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, | |
| 153 | stubs: std.ArrayListUnmanaged(u32) = .{}, | |
| 154 | stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{}, | |
| 134 | 155 | |
| 135 | 156 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 136 | 157 | |
| 137 | offset_table_count_dirty: bool = false, | |
| 138 | header_dirty: bool = false, | |
| 158 | got_entries_count_dirty: bool = false, | |
| 139 | 159 | load_commands_dirty: bool = false, |
| 140 | 160 | rebase_info_dirty: bool = false, |
| 141 | 161 | binding_info_dirty: bool = false, |
| 142 | 162 | lazy_binding_info_dirty: bool = false, |
| 143 | 163 | export_info_dirty: bool = false, |
| 144 | string_table_dirty: bool = false, | |
| 145 | 164 | |
| 146 | string_table_needs_relocation: bool = false, | |
| 165 | strtab_dirty: bool = false, | |
| 166 | strtab_needs_relocation: bool = false, | |
| 167 | ||
| 168 | has_dices: bool = false, | |
| 169 | has_stabs: bool = false, | |
| 170 | ||
| 171 | section_ordinals: std.ArrayListUnmanaged(MatchingSection) = .{}, | |
| 172 | section_to_ordinal: std.AutoHashMapUnmanaged(MatchingSection, u8) = .{}, | |
| 173 | ||
| 174 | pending_updates: std.ArrayListUnmanaged(struct { | |
| 175 | kind: enum { | |
| 176 | got, | |
| 177 | stub, | |
| 178 | }, | |
| 179 | index: u32, | |
| 180 | }) = .{}, | |
| 147 | 181 | |
| 148 | 182 | /// A list of text blocks that have surplus capacity. This list can have false |
| 149 | 183 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | ... | @@ -165,47 +199,73 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 165 | 199 | /// Pointer to the last allocated text block |
| 166 | 200 | last_text_block: ?*TextBlock = null, |
| 167 | 201 | |
| 168 | /// A list of all PIE fixups required for this run of the linker. | |
| 169 | /// Warning, this is currently NOT thread-safe. See the TODO below. | |
| 170 | /// TODO Move this list inside `updateDecl` where it should be allocated | |
| 171 | /// prior to calling `generateSymbol`, and then immediately deallocated | |
| 172 | /// rather than sitting in the global scope. | |
| 173 | /// TODO We should also rewrite this using generic relocations common to all | |
| 174 | /// backends. | |
| 175 | pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, | |
| 176 | ||
| 177 | /// A list of all stub (extern decls) fixups required for this run of the linker. | |
| 178 | /// Warning, this is currently NOT thread-safe. See the TODO below. | |
| 179 | /// TODO Move this list inside `updateDecl` where it should be allocated | |
| 180 | /// prior to calling `generateSymbol`, and then immediately deallocated | |
| 181 | /// rather than sitting in the global scope. | |
| 182 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, | |
| 183 | ||
| 184 | pub const GOTEntry = struct { | |
| 185 | /// GOT entry can either be a local pointer or an extern (nonlazy) import. | |
| 186 | kind: enum { | |
| 187 | Local, | |
| 188 | Extern, | |
| 189 | }, | |
| 202 | /// List of TextBlocks that are owned directly by the linker. | |
| 203 | /// Currently these are only TextBlocks that are the result of linking | |
| 204 | /// object files. TextBlock which take part in incremental linking are | |
| 205 | /// at present owned by Module.Decl. | |
| 206 | /// TODO consolidate this. | |
| 207 | managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{}, | |
| 190 | 208 | |
| 191 | /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports. | |
| 192 | /// TODO I'm more and more inclined to just manage a single, max two symbol tables | |
| 193 | /// rather than 4 as we currently do, but I'll follow up in the future PR. | |
| 194 | symbol: u32, | |
| 209 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, | |
| 195 | 210 | |
| 196 | /// Index of this entry in the GOT. | |
| 197 | index: u32, | |
| 211 | /// Table of Decls that are currently alive. | |
| 212 | /// We store them here so that we can properly dispose of any allocated | |
| 213 | /// memory within the TextBlock in the incremental linker. | |
| 214 | /// TODO consolidate this. | |
| 215 | decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, | |
| 216 | ||
| 217 | /// Currently active Module.Decl. | |
| 218 | /// TODO this might not be necessary if we figure out how to pass Module.Decl instance | |
| 219 | /// to codegen.genSetReg() or alterntively move PIE displacement for MCValue{ .memory = x } | |
| 220 | /// somewhere else in the codegen. | |
| 221 | active_decl: ?*Module.Decl = null, | |
| 222 | ||
| 223 | const StringIndexContext = struct { | |
| 224 | strtab: *std.ArrayListUnmanaged(u8), | |
| 225 | ||
| 226 | pub fn eql(_: StringIndexContext, a: u32, b: u32) bool { | |
| 227 | return a == b; | |
| 228 | } | |
| 229 | ||
| 230 | pub fn hash(self: StringIndexContext, x: u32) u64 { | |
| 231 | const x_slice = mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr) + x); | |
| 232 | return std.hash_map.hashString(x_slice); | |
| 233 | } | |
| 198 | 234 | }; |
| 199 | 235 | |
| 200 | pub const Import = struct { | |
| 201 | /// MachO symbol table entry. | |
| 202 | symbol: macho.nlist_64, | |
| 236 | pub const StringSliceAdapter = struct { | |
| 237 | strtab: *std.ArrayListUnmanaged(u8), | |
| 238 | ||
| 239 | pub fn eql(self: StringSliceAdapter, a_slice: []const u8, b: u32) bool { | |
| 240 | const b_slice = mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr) + b); | |
| 241 | return mem.eql(u8, a_slice, b_slice); | |
| 242 | } | |
| 203 | 243 | |
| 204 | /// Id of the dynamic library where the specified entries can be found. | |
| 205 | dylib_ordinal: i64, | |
| 244 | pub fn hash(self: StringSliceAdapter, adapted_key: []const u8) u64 { | |
| 245 | _ = self; | |
| 246 | return std.hash_map.hashString(adapted_key); | |
| 247 | } | |
| 248 | }; | |
| 206 | 249 | |
| 207 | /// Index of this import within the import list. | |
| 208 | index: u32, | |
| 250 | const SymbolWithLoc = struct { | |
| 251 | // Table where the symbol can be found. | |
| 252 | where: enum { | |
| 253 | global, | |
| 254 | import, | |
| 255 | undef, | |
| 256 | tentative, | |
| 257 | }, | |
| 258 | where_index: u32, | |
| 259 | local_sym_index: u32 = 0, | |
| 260 | file: u16 = 0, | |
| 261 | }; | |
| 262 | ||
| 263 | pub const GotIndirectionKey = struct { | |
| 264 | where: enum { | |
| 265 | local, | |
| 266 | import, | |
| 267 | }, | |
| 268 | where_index: u32, | |
| 209 | 269 | }; |
| 210 | 270 | |
| 211 | 271 | pub const PIEFixup = struct { |
| ... | ... | @@ -219,19 +279,6 @@ pub const PIEFixup = struct { |
| 219 | 279 | size: usize, |
| 220 | 280 | }; |
| 221 | 281 | |
| 222 | pub const StubFixup = struct { | |
| 223 | /// Id of extern (lazy) symbol. | |
| 224 | symbol: u32, | |
| 225 | /// Signals whether the symbol has already been declared before. If so, | |
| 226 | /// then there is no need to rewrite the stub entry and related. | |
| 227 | already_defined: bool, | |
| 228 | /// Where in the byte stream we should perform the fixup. | |
| 229 | start: usize, | |
| 230 | /// The length of the byte stream. For x86_64, this will be | |
| 231 | /// variable. For aarch64, it will be fixed at 4 bytes. | |
| 232 | len: usize, | |
| 233 | }; | |
| 234 | ||
| 235 | 282 | /// When allocating, the ideal_capacity is calculated by |
| 236 | 283 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 237 | 284 | const ideal_factor = 2; |
| ... | ... | @@ -254,75 +301,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. |
| 254 | 301 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 255 | 302 | /// (plus extra for reserved capacity). |
| 256 | 303 | const minimum_text_block_size = 64; |
| 257 | const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 258 | ||
| 259 | pub const TextBlock = struct { | |
| 260 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 261 | /// The vaddr and size are found here directly. | |
| 262 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 263 | /// the symbol references, and adding that to the file offset of the section. | |
| 264 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 265 | /// offset table entry. | |
| 266 | local_sym_index: u32, | |
| 267 | /// Index into offset table | |
| 268 | /// This field is undefined for symbols with size = 0. | |
| 269 | offset_table_index: u32, | |
| 270 | /// Size of this text block | |
| 271 | /// Unlike in Elf, we need to store the size of this symbol as part of | |
| 272 | /// the TextBlock since macho.nlist_64 lacks this information. | |
| 273 | size: u64, | |
| 274 | /// Points to the previous and next neighbours | |
| 275 | prev: ?*TextBlock, | |
| 276 | next: ?*TextBlock, | |
| 277 | ||
| 278 | /// Previous/next linked list pointers. | |
| 279 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | |
| 280 | dbg_info_prev: ?*TextBlock, | |
| 281 | dbg_info_next: ?*TextBlock, | |
| 282 | /// Offset into .debug_info pointing to the tag for this Decl. | |
| 283 | dbg_info_off: u32, | |
| 284 | /// Size of the .debug_info tag for this Decl, not including padding. | |
| 285 | dbg_info_len: u32, | |
| 286 | ||
| 287 | pub const empty = TextBlock{ | |
| 288 | .local_sym_index = 0, | |
| 289 | .offset_table_index = undefined, | |
| 290 | .size = 0, | |
| 291 | .prev = null, | |
| 292 | .next = null, | |
| 293 | .dbg_info_prev = null, | |
| 294 | .dbg_info_next = null, | |
| 295 | .dbg_info_off = undefined, | |
| 296 | .dbg_info_len = undefined, | |
| 297 | }; | |
| 298 | ||
| 299 | /// Returns how much room there is to grow in virtual address space. | |
| 300 | /// File offset relocation happens transparently, so it is not included in | |
| 301 | /// this calculation. | |
| 302 | fn capacity(self: TextBlock, macho_file: MachO) u64 { | |
| 303 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 304 | if (self.next) |next| { | |
| 305 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 306 | return next_sym.n_value - self_sym.n_value; | |
| 307 | } else { | |
| 308 | // We are the last block. | |
| 309 | // The capacity is limited only by virtual address space. | |
| 310 | return std.math.maxInt(u64) - self_sym.n_value; | |
| 311 | } | |
| 312 | } | |
| 313 | ||
| 314 | fn freeListEligible(self: TextBlock, macho_file: MachO) bool { | |
| 315 | // No need to keep a free list node for the last block. | |
| 316 | const next = self.next orelse return false; | |
| 317 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 318 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 319 | const cap = next_sym.n_value - self_sym.n_value; | |
| 320 | const ideal_cap = padToIdeal(self.size); | |
| 321 | if (cap <= ideal_cap) return false; | |
| 322 | const surplus = cap - ideal_cap; | |
| 323 | return surplus >= min_text_capacity; | |
| 324 | } | |
| 325 | }; | |
| 304 | pub const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 326 | 305 | |
| 327 | 306 | pub const Export = struct { |
| 328 | 307 | sym_index: ?u32 = null, |
| ... | ... | @@ -374,6 +353,10 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 374 | 353 | |
| 375 | 354 | self.base.file = file; |
| 376 | 355 | |
| 356 | if (options.output_mode == .Lib and options.link_mode == .Static) { | |
| 357 | return self; | |
| 358 | } | |
| 359 | ||
| 377 | 360 | if (!options.strip and options.module != null) { |
| 378 | 361 | // Create dSYM bundle. |
| 379 | 362 | const dir = options.module.?.zig_cache_artifact_directory; |
| ... | ... | @@ -409,12 +392,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 409 | 392 | .n_value = 0, |
| 410 | 393 | }); |
| 411 | 394 | |
| 412 | switch (options.output_mode) { | |
| 413 | .Exe => {}, | |
| 414 | .Obj => {}, | |
| 415 | .Lib => return error.TODOImplementWritingLibFiles, | |
| 416 | } | |
| 417 | ||
| 418 | 395 | try self.populateMissingMetadata(); |
| 419 | 396 | try self.writeLocalSymbol(0); |
| 420 | 397 | |
| ... | ... | @@ -428,6 +405,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 428 | 405 | |
| 429 | 406 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 430 | 407 | const self = try gpa.create(MachO); |
| 408 | ||
| 431 | 409 | self.* = .{ |
| 432 | 410 | .base = .{ |
| 433 | 411 | .tag = .macho, |
| ... | ... | @@ -437,11 +415,22 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 437 | 415 | }, |
| 438 | 416 | .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, |
| 439 | 417 | }; |
| 418 | ||
| 440 | 419 | return self; |
| 441 | 420 | } |
| 442 | 421 | |
| 443 | 422 | pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 444 | if (build_options.have_llvm and self.base.options.use_lld) { | |
| 423 | if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) { | |
| 424 | if (build_options.have_llvm) { | |
| 425 | return self.base.linkAsArchive(comp); | |
| 426 | } else { | |
| 427 | log.err("TODO: non-LLVM archiver for MachO object files", .{}); | |
| 428 | return error.TODOImplementWritingStaticLibFiles; | |
| 429 | } | |
| 430 | } | |
| 431 | ||
| 432 | const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; | |
| 433 | if (use_stage1) { | |
| 445 | 434 | return self.linkWithZld(comp); |
| 446 | 435 | } else { |
| 447 | 436 | switch (self.base.options.effectiveOutputMode()) { |
| ... | ... | @@ -471,9 +460,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 471 | 460 | self.load_commands_dirty = true; |
| 472 | 461 | } |
| 473 | 462 | try self.writeRebaseInfoTable(); |
| 474 | try self.writeBindingInfoTable(); | |
| 475 | try self.writeLazyBindingInfoTable(); | |
| 476 | try self.writeExportTrie(); | |
| 463 | try self.writeBindInfoTable(); | |
| 464 | try self.writeLazyBindInfoTable(); | |
| 465 | try self.writeExportInfo(); | |
| 477 | 466 | try self.writeAllGlobalAndUndefSymbols(); |
| 478 | 467 | try self.writeIndirectSymbolTable(); |
| 479 | 468 | try self.writeStringTable(); |
| ... | ... | @@ -508,15 +497,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 508 | 497 | self.error_flags.no_entry_point_found = false; |
| 509 | 498 | } |
| 510 | 499 | |
| 511 | assert(!self.offset_table_count_dirty); | |
| 512 | assert(!self.header_dirty); | |
| 500 | assert(!self.got_entries_count_dirty); | |
| 513 | 501 | assert(!self.load_commands_dirty); |
| 514 | 502 | assert(!self.rebase_info_dirty); |
| 515 | 503 | assert(!self.binding_info_dirty); |
| 516 | 504 | assert(!self.lazy_binding_info_dirty); |
| 517 | 505 | assert(!self.export_info_dirty); |
| 518 | assert(!self.string_table_dirty); | |
| 519 | assert(!self.string_table_needs_relocation); | |
| 506 | assert(!self.strtab_dirty); | |
| 507 | assert(!self.strtab_needs_relocation); | |
| 520 | 508 | |
| 521 | 509 | if (target.cpu.arch == .aarch64) { |
| 522 | 510 | switch (output_mode) { |
| ... | ... | @@ -639,7 +627,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 639 | 627 | const is_lib = self.base.options.output_mode == .Lib; |
| 640 | 628 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; |
| 641 | 629 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 642 | const target = self.base.options.target; | |
| 643 | 630 | const stack_size = self.base.options.stack_size_override orelse 0; |
| 644 | 631 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| 645 | 632 | |
| ... | ... | @@ -738,14 +725,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 738 | 725 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); |
| 739 | 726 | } |
| 740 | 727 | } else { |
| 741 | var zld = Zld.init(self.base.allocator); | |
| 742 | defer { | |
| 743 | zld.closeFiles(); | |
| 744 | zld.deinit(); | |
| 745 | } | |
| 746 | zld.target = target; | |
| 747 | zld.stack_size = stack_size; | |
| 748 | ||
| 749 | 728 | // Positional arguments to the linker such as object files and static archives. |
| 750 | 729 | var positionals = std.ArrayList([]const u8).init(arena); |
| 751 | 730 | |
| ... | ... | @@ -888,23 +867,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 888 | 867 | rpaths.appendAssumeCapacity(key.*); |
| 889 | 868 | } |
| 890 | 869 | |
| 891 | const output: Zld.Output = output: { | |
| 892 | if (is_dyn_lib) { | |
| 893 | const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{ | |
| 894 | self.base.options.emit.?.sub_path, | |
| 895 | }); | |
| 896 | break :output .{ | |
| 897 | .tag = .dylib, | |
| 898 | .path = full_out_path, | |
| 899 | .install_name = install_name, | |
| 900 | }; | |
| 901 | } | |
| 902 | break :output .{ | |
| 903 | .tag = .exe, | |
| 904 | .path = full_out_path, | |
| 905 | }; | |
| 906 | }; | |
| 907 | ||
| 908 | 870 | if (self.base.options.verbose_link) { |
| 909 | 871 | var argv = std.ArrayList([]const u8).init(arena); |
| 910 | 872 | |
| ... | ... | @@ -918,8 +880,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 918 | 880 | if (is_dyn_lib) { |
| 919 | 881 | try argv.append("-dylib"); |
| 920 | 882 | |
| 883 | const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{ | |
| 884 | self.base.options.emit.?.sub_path, | |
| 885 | }); | |
| 921 | 886 | try argv.append("-install_name"); |
| 922 | try argv.append(output.install_name.?); | |
| 887 | try argv.append(install_name); | |
| 923 | 888 | } |
| 924 | 889 | |
| 925 | 890 | if (self.base.options.sysroot) |syslibroot| { |
| ... | ... | @@ -935,7 +900,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 935 | 900 | try argv.appendSlice(positionals.items); |
| 936 | 901 | |
| 937 | 902 | try argv.append("-o"); |
| 938 | try argv.append(output.path); | |
| 903 | try argv.append(full_out_path); | |
| 939 | 904 | |
| 940 | 905 | if (native_libsystem_available) { |
| 941 | 906 | try argv.append("-lSystem"); |
| ... | ... | @@ -953,11 +918,56 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 953 | 918 | Compilation.dump_argv(argv.items); |
| 954 | 919 | } |
| 955 | 920 | |
| 956 | try zld.link(positionals.items, output, .{ | |
| 957 | .syslibroot = self.base.options.sysroot, | |
| 958 | .libs = libs.items, | |
| 959 | .rpaths = rpaths.items, | |
| 921 | self.base.file = try fs.cwd().createFile(full_out_path, .{ | |
| 922 | .truncate = true, | |
| 923 | .read = true, | |
| 924 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | |
| 925 | }); | |
| 926 | self.page_size = switch (self.base.options.target.cpu.arch) { | |
| 927 | .aarch64 => 0x4000, | |
| 928 | .x86_64 => 0x1000, | |
| 929 | else => unreachable, | |
| 930 | }; | |
| 931 | ||
| 932 | // Initialize section ordinals with null ordinal pointing at | |
| 933 | // PAGEZERO segment. | |
| 934 | try self.section_ordinals.append(self.base.allocator, .{ | |
| 935 | .seg = 0, | |
| 936 | .sect = 0, | |
| 960 | 937 | }); |
| 938 | ||
| 939 | try self.populateMetadata(); | |
| 940 | try self.parseInputFiles(positionals.items, self.base.options.sysroot); | |
| 941 | try self.parseLibs(libs.items, self.base.options.sysroot); | |
| 942 | try self.resolveSymbols(); | |
| 943 | try self.parseTextBlocks(); | |
| 944 | ||
| 945 | { | |
| 946 | // Add dyld_stub_binder as the final GOT entry. | |
| 947 | const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ | |
| 948 | .strtab = &self.strtab, | |
| 949 | }) orelse unreachable; | |
| 950 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 951 | const got_index = @intCast(u32, self.got_entries.items.len); | |
| 952 | const got_entry = GotIndirectionKey{ | |
| 953 | .where = .import, | |
| 954 | .where_index = resolv.where_index, | |
| 955 | }; | |
| 956 | try self.got_entries.append(self.base.allocator, got_entry); | |
| 957 | try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); | |
| 958 | } | |
| 959 | ||
| 960 | try self.sortSections(); | |
| 961 | try self.addRpaths(rpaths.items); | |
| 962 | try self.addDataInCodeLC(); | |
| 963 | try self.addCodeSignatureLC(); | |
| 964 | try self.allocateTextSegment(); | |
| 965 | try self.allocateDataConstSegment(); | |
| 966 | try self.allocateDataSegment(); | |
| 967 | self.allocateLinkeditSegment(); | |
| 968 | try self.allocateTextBlocks(); | |
| 969 | self.printSymtabAndTextBlock(); | |
| 970 | try self.flushZld(); | |
| 961 | 971 | } |
| 962 | 972 | |
| 963 | 973 | if (!self.base.options.disable_lld_caching) { |
| ... | ... | @@ -976,162 +986,2513 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 976 | 986 | } |
| 977 | 987 | } |
| 978 | 988 | |
| 979 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | |
| 980 | return switch (arch) { | |
| 981 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", | |
| 982 | .thumb, .arm => "arm", | |
| 983 | .thumbeb, .armeb => "armeb", | |
| 984 | .powerpc => "ppc", | |
| 985 | .powerpc64 => "ppc64", | |
| 986 | .powerpc64le => "ppc64le", | |
| 987 | else => @tagName(arch), | |
| 988 | }; | |
| 989 | } | |
| 989 | fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 990 | const arch = self.base.options.target.cpu.arch; | |
| 991 | for (files) |file_name| { | |
| 992 | const full_path = full_path: { | |
| 993 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 994 | const path = try std.fs.realpath(file_name, &buffer); | |
| 995 | break :full_path try self.base.allocator.dupe(u8, path); | |
| 996 | }; | |
| 990 | 997 | |
| 991 | pub fn deinit(self: *MachO) void { | |
| 992 | if (build_options.have_llvm) { | |
| 993 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 994 | } | |
| 995 | if (self.d_sym) |*ds| { | |
| 996 | ds.deinit(self.base.allocator); | |
| 997 | } | |
| 998 | for (self.lazy_imports.keys()) |*key| { | |
| 999 | self.base.allocator.free(key.*); | |
| 1000 | } | |
| 1001 | self.lazy_imports.deinit(self.base.allocator); | |
| 1002 | for (self.nonlazy_imports.keys()) |*key| { | |
| 1003 | self.base.allocator.free(key.*); | |
| 1004 | } | |
| 1005 | self.nonlazy_imports.deinit(self.base.allocator); | |
| 1006 | self.pie_fixups.deinit(self.base.allocator); | |
| 1007 | self.stub_fixups.deinit(self.base.allocator); | |
| 1008 | self.text_block_free_list.deinit(self.base.allocator); | |
| 1009 | self.offset_table.deinit(self.base.allocator); | |
| 1010 | self.offset_table_free_list.deinit(self.base.allocator); | |
| 1011 | { | |
| 1012 | var it = self.string_table_directory.keyIterator(); | |
| 1013 | while (it.next()) |key| { | |
| 1014 | self.base.allocator.free(key.*); | |
| 998 | if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| { | |
| 999 | try self.objects.append(self.base.allocator, object); | |
| 1000 | continue; | |
| 1015 | 1001 | } |
| 1016 | } | |
| 1017 | self.string_table_directory.deinit(self.base.allocator); | |
| 1018 | self.string_table.deinit(self.base.allocator); | |
| 1019 | self.globals.deinit(self.base.allocator); | |
| 1020 | self.globals_free_list.deinit(self.base.allocator); | |
| 1021 | self.locals.deinit(self.base.allocator); | |
| 1022 | self.locals_free_list.deinit(self.base.allocator); | |
| 1023 | for (self.load_commands.items) |*lc| { | |
| 1024 | lc.deinit(self.base.allocator); | |
| 1025 | } | |
| 1026 | self.load_commands.deinit(self.base.allocator); | |
| 1027 | } | |
| 1028 | 1002 | |
| 1029 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { | |
| 1030 | var already_have_free_list_node = false; | |
| 1031 | { | |
| 1032 | var i: usize = 0; | |
| 1033 | // TODO turn text_block_free_list into a hash map | |
| 1034 | while (i < self.text_block_free_list.items.len) { | |
| 1035 | if (self.text_block_free_list.items[i] == text_block) { | |
| 1036 | _ = self.text_block_free_list.swapRemove(i); | |
| 1037 | continue; | |
| 1038 | } | |
| 1039 | if (self.text_block_free_list.items[i] == text_block.prev) { | |
| 1040 | already_have_free_list_node = true; | |
| 1041 | } | |
| 1042 | i += 1; | |
| 1003 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| { | |
| 1004 | try self.archives.append(self.base.allocator, archive); | |
| 1005 | continue; | |
| 1043 | 1006 | } |
| 1044 | } | |
| 1045 | // TODO process free list for dbg info just like we do above for vaddrs | |
| 1046 | 1007 | |
| 1047 | if (self.last_text_block == text_block) { | |
| 1048 | // TODO shrink the __text section size here | |
| 1049 | self.last_text_block = text_block.prev; | |
| 1050 | } | |
| 1051 | if (self.d_sym) |*ds| { | |
| 1052 | if (ds.dbg_info_decl_first == text_block) { | |
| 1053 | ds.dbg_info_decl_first = text_block.dbg_info_next; | |
| 1054 | } | |
| 1055 | if (ds.dbg_info_decl_last == text_block) { | |
| 1056 | // TODO shrink the .debug_info section size here | |
| 1057 | ds.dbg_info_decl_last = text_block.dbg_info_prev; | |
| 1008 | if (try Dylib.createAndParseFromPath(self.base.allocator, arch, full_path, .{ | |
| 1009 | .syslibroot = syslibroot, | |
| 1010 | })) |dylibs| { | |
| 1011 | defer self.base.allocator.free(dylibs); | |
| 1012 | try self.dylibs.appendSlice(self.base.allocator, dylibs); | |
| 1013 | continue; | |
| 1058 | 1014 | } |
| 1015 | ||
| 1016 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | |
| 1059 | 1017 | } |
| 1018 | } | |
| 1060 | 1019 | |
| 1061 | if (text_block.prev) |prev| { | |
| 1062 | prev.next = text_block.next; | |
| 1020 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 1021 | const arch = self.base.options.target.cpu.arch; | |
| 1022 | for (libs) |lib| { | |
| 1023 | if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{ | |
| 1024 | .syslibroot = syslibroot, | |
| 1025 | })) |dylibs| { | |
| 1026 | defer self.base.allocator.free(dylibs); | |
| 1027 | try self.dylibs.appendSlice(self.base.allocator, dylibs); | |
| 1028 | continue; | |
| 1029 | } | |
| 1063 | 1030 | |
| 1064 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { | |
| 1065 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore | |
| 1066 | // the OOM here. | |
| 1067 | self.text_block_free_list.append(self.base.allocator, prev) catch {}; | |
| 1031 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| { | |
| 1032 | try self.archives.append(self.base.allocator, archive); | |
| 1033 | continue; | |
| 1068 | 1034 | } |
| 1069 | } else { | |
| 1070 | text_block.prev = null; | |
| 1071 | } | |
| 1072 | 1035 | |
| 1073 | if (text_block.next) |next| { | |
| 1074 | next.prev = text_block.prev; | |
| 1075 | } else { | |
| 1076 | text_block.next = null; | |
| 1036 | log.warn("unknown filetype for a library: '{s}'", .{lib}); | |
| 1077 | 1037 | } |
| 1038 | } | |
| 1078 | 1039 | |
| 1079 | if (text_block.dbg_info_prev) |prev| { | |
| 1080 | prev.dbg_info_next = text_block.dbg_info_next; | |
| 1040 | pub const MatchingSection = struct { | |
| 1041 | seg: u16, | |
| 1042 | sect: u16, | |
| 1043 | }; | |
| 1081 | 1044 | |
| 1082 | // TODO the free list logic like we do for text blocks above | |
| 1083 | } else { | |
| 1084 | text_block.dbg_info_prev = null; | |
| 1085 | } | |
| 1045 | pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection { | |
| 1046 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1047 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1048 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1049 | const segname = commands.segmentName(sect); | |
| 1050 | const sectname = commands.sectionName(sect); | |
| 1051 | ||
| 1052 | const res: ?MatchingSection = blk: { | |
| 1053 | switch (commands.sectionType(sect)) { | |
| 1054 | macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { | |
| 1055 | if (self.text_const_section_index == null) { | |
| 1056 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1057 | try text_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1058 | } | |
| 1086 | 1059 | |
| 1087 | if (text_block.dbg_info_next) |next| { | |
| 1088 | next.dbg_info_prev = text_block.dbg_info_prev; | |
| 1089 | } else { | |
| 1090 | text_block.dbg_info_next = null; | |
| 1091 | } | |
| 1092 | } | |
| 1060 | break :blk .{ | |
| 1061 | .seg = self.text_segment_cmd_index.?, | |
| 1062 | .sect = self.text_const_section_index.?, | |
| 1063 | }; | |
| 1064 | }, | |
| 1065 | macho.S_CSTRING_LITERALS => { | |
| 1066 | if (mem.eql(u8, sectname, "__objc_methname")) { | |
| 1067 | // TODO it seems the common values within the sections in objects are deduplicated/merged | |
| 1068 | // on merging the sections' contents. | |
| 1069 | if (self.objc_methname_section_index == null) { | |
| 1070 | self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1071 | try text_seg.addSection(self.base.allocator, "__objc_methname", .{ | |
| 1072 | .flags = macho.S_CSTRING_LITERALS, | |
| 1073 | }); | |
| 1074 | } | |
| 1075 | ||
| 1076 | break :blk .{ | |
| 1077 | .seg = self.text_segment_cmd_index.?, | |
| 1078 | .sect = self.objc_methname_section_index.?, | |
| 1079 | }; | |
| 1080 | } else if (mem.eql(u8, sectname, "__objc_methtype")) { | |
| 1081 | if (self.objc_methtype_section_index == null) { | |
| 1082 | self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1083 | try text_seg.addSection(self.base.allocator, "__objc_methtype", .{ | |
| 1084 | .flags = macho.S_CSTRING_LITERALS, | |
| 1085 | }); | |
| 1086 | } | |
| 1087 | ||
| 1088 | break :blk .{ | |
| 1089 | .seg = self.text_segment_cmd_index.?, | |
| 1090 | .sect = self.objc_methtype_section_index.?, | |
| 1091 | }; | |
| 1092 | } else if (mem.eql(u8, sectname, "__objc_classname")) { | |
| 1093 | if (self.objc_classname_section_index == null) { | |
| 1094 | self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1095 | try text_seg.addSection(self.base.allocator, "__objc_classname", .{}); | |
| 1096 | } | |
| 1097 | ||
| 1098 | break :blk .{ | |
| 1099 | .seg = self.text_segment_cmd_index.?, | |
| 1100 | .sect = self.objc_classname_section_index.?, | |
| 1101 | }; | |
| 1102 | } | |
| 1093 | 1103 | |
| 1094 | fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void { | |
| 1095 | _ = self; | |
| 1096 | _ = text_block; | |
| 1097 | _ = new_block_size; | |
| 1098 | // TODO check the new capacity, and if it crosses the size threshold into a big enough | |
| 1099 | // capacity, insert a free list node for it. | |
| 1100 | } | |
| 1104 | if (self.cstring_section_index == null) { | |
| 1105 | self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1106 | try text_seg.addSection(self.base.allocator, "__cstring", .{ | |
| 1107 | .flags = macho.S_CSTRING_LITERALS, | |
| 1108 | }); | |
| 1109 | } | |
| 1101 | 1110 | |
| 1102 | fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | |
| 1103 | const sym = self.locals.items[text_block.local_sym_index]; | |
| 1104 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; | |
| 1105 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); | |
| 1106 | if (!need_realloc) return sym.n_value; | |
| 1107 | return self.allocateTextBlock(text_block, new_block_size, alignment); | |
| 1108 | } | |
| 1111 | break :blk .{ | |
| 1112 | .seg = self.text_segment_cmd_index.?, | |
| 1113 | .sect = self.cstring_section_index.?, | |
| 1114 | }; | |
| 1115 | }, | |
| 1116 | macho.S_LITERAL_POINTERS => { | |
| 1117 | if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { | |
| 1118 | if (self.objc_selrefs_section_index == null) { | |
| 1119 | self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1120 | try data_seg.addSection(self.base.allocator, "__objc_selrefs", .{ | |
| 1121 | .flags = macho.S_LITERAL_POINTERS, | |
| 1122 | }); | |
| 1123 | } | |
| 1124 | ||
| 1125 | break :blk .{ | |
| 1126 | .seg = self.data_segment_cmd_index.?, | |
| 1127 | .sect = self.objc_selrefs_section_index.?, | |
| 1128 | }; | |
| 1129 | } | |
| 1109 | 1130 | |
| 1110 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | |
| 1111 | if (decl.link.macho.local_sym_index != 0) return; | |
| 1131 | // TODO investigate | |
| 1132 | break :blk null; | |
| 1133 | }, | |
| 1134 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 1135 | if (self.mod_init_func_section_index == null) { | |
| 1136 | self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1137 | try data_const_seg.addSection(self.base.allocator, "__mod_init_func", .{ | |
| 1138 | .flags = macho.S_MOD_INIT_FUNC_POINTERS, | |
| 1139 | }); | |
| 1140 | } | |
| 1141 | ||
| 1142 | break :blk .{ | |
| 1143 | .seg = self.data_const_segment_cmd_index.?, | |
| 1144 | .sect = self.mod_init_func_section_index.?, | |
| 1145 | }; | |
| 1146 | }, | |
| 1147 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 1148 | if (self.mod_term_func_section_index == null) { | |
| 1149 | self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1150 | try data_const_seg.addSection(self.base.allocator, "__mod_term_func", .{ | |
| 1151 | .flags = macho.S_MOD_TERM_FUNC_POINTERS, | |
| 1152 | }); | |
| 1153 | } | |
| 1112 | 1154 | |
| 1113 | try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1); | |
| 1114 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | |
| 1155 | break :blk .{ | |
| 1156 | .seg = self.data_const_segment_cmd_index.?, | |
| 1157 | .sect = self.mod_term_func_section_index.?, | |
| 1158 | }; | |
| 1159 | }, | |
| 1160 | macho.S_ZEROFILL => { | |
| 1161 | if (mem.eql(u8, sectname, "__common")) { | |
| 1162 | if (self.common_section_index == null) { | |
| 1163 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1164 | try data_seg.addSection(self.base.allocator, "__common", .{ | |
| 1165 | .flags = macho.S_ZEROFILL, | |
| 1166 | }); | |
| 1167 | } | |
| 1168 | ||
| 1169 | break :blk .{ | |
| 1170 | .seg = self.data_segment_cmd_index.?, | |
| 1171 | .sect = self.common_section_index.?, | |
| 1172 | }; | |
| 1173 | } else { | |
| 1174 | if (self.bss_section_index == null) { | |
| 1175 | self.bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1176 | try data_seg.addSection(self.base.allocator, "__bss", .{ | |
| 1177 | .flags = macho.S_ZEROFILL, | |
| 1178 | }); | |
| 1179 | } | |
| 1180 | ||
| 1181 | break :blk .{ | |
| 1182 | .seg = self.data_segment_cmd_index.?, | |
| 1183 | .sect = self.bss_section_index.?, | |
| 1184 | }; | |
| 1185 | } | |
| 1186 | }, | |
| 1187 | macho.S_THREAD_LOCAL_VARIABLES => { | |
| 1188 | if (self.tlv_section_index == null) { | |
| 1189 | self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1190 | try data_seg.addSection(self.base.allocator, "__thread_vars", .{ | |
| 1191 | .flags = macho.S_THREAD_LOCAL_VARIABLES, | |
| 1192 | }); | |
| 1193 | } | |
| 1115 | 1194 | |
| 1116 | if (self.locals_free_list.popOrNull()) |i| { | |
| 1117 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); | |
| 1118 | decl.link.macho.local_sym_index = i; | |
| 1119 | } else { | |
| 1120 | log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name }); | |
| 1121 | decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len); | |
| 1122 | _ = self.locals.addOneAssumeCapacity(); | |
| 1123 | } | |
| 1195 | break :blk .{ | |
| 1196 | .seg = self.data_segment_cmd_index.?, | |
| 1197 | .sect = self.tlv_section_index.?, | |
| 1198 | }; | |
| 1199 | }, | |
| 1200 | macho.S_THREAD_LOCAL_REGULAR => { | |
| 1201 | if (self.tlv_data_section_index == null) { | |
| 1202 | self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1203 | try data_seg.addSection(self.base.allocator, "__thread_data", .{ | |
| 1204 | .flags = macho.S_THREAD_LOCAL_REGULAR, | |
| 1205 | }); | |
| 1206 | } | |
| 1124 | 1207 | |
| 1125 | if (self.offset_table_free_list.popOrNull()) |i| { | |
| 1126 | log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name }); | |
| 1127 | decl.link.macho.offset_table_index = i; | |
| 1128 | } else { | |
| 1129 | log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name }); | |
| 1130 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); | |
| 1131 | _ = self.offset_table.addOneAssumeCapacity(); | |
| 1132 | self.offset_table_count_dirty = true; | |
| 1133 | self.rebase_info_dirty = true; | |
| 1134 | } | |
| 1208 | break :blk .{ | |
| 1209 | .seg = self.data_segment_cmd_index.?, | |
| 1210 | .sect = self.tlv_data_section_index.?, | |
| 1211 | }; | |
| 1212 | }, | |
| 1213 | macho.S_THREAD_LOCAL_ZEROFILL => { | |
| 1214 | if (self.tlv_bss_section_index == null) { | |
| 1215 | self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1216 | try data_seg.addSection(self.base.allocator, "__thread_bss", .{ | |
| 1217 | .flags = macho.S_THREAD_LOCAL_ZEROFILL, | |
| 1218 | }); | |
| 1219 | } | |
| 1220 | ||
| 1221 | break :blk .{ | |
| 1222 | .seg = self.data_segment_cmd_index.?, | |
| 1223 | .sect = self.tlv_bss_section_index.?, | |
| 1224 | }; | |
| 1225 | }, | |
| 1226 | macho.S_COALESCED => { | |
| 1227 | if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { | |
| 1228 | // TODO I believe __eh_frame is currently part of __unwind_info section | |
| 1229 | // in the latest ld64 output. | |
| 1230 | if (self.eh_frame_section_index == null) { | |
| 1231 | self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1232 | try text_seg.addSection(self.base.allocator, "__eh_frame", .{}); | |
| 1233 | } | |
| 1234 | ||
| 1235 | break :blk .{ | |
| 1236 | .seg = self.text_segment_cmd_index.?, | |
| 1237 | .sect = self.eh_frame_section_index.?, | |
| 1238 | }; | |
| 1239 | } | |
| 1240 | ||
| 1241 | // TODO audit this: is this the right mapping? | |
| 1242 | if (self.data_const_section_index == null) { | |
| 1243 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1244 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1245 | } | |
| 1246 | ||
| 1247 | break :blk .{ | |
| 1248 | .seg = self.data_const_segment_cmd_index.?, | |
| 1249 | .sect = self.data_const_section_index.?, | |
| 1250 | }; | |
| 1251 | }, | |
| 1252 | macho.S_REGULAR => { | |
| 1253 | if (commands.sectionIsCode(sect)) { | |
| 1254 | if (self.text_section_index == null) { | |
| 1255 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1256 | try text_seg.addSection(self.base.allocator, "__text", .{ | |
| 1257 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1258 | }); | |
| 1259 | } | |
| 1260 | ||
| 1261 | break :blk .{ | |
| 1262 | .seg = self.text_segment_cmd_index.?, | |
| 1263 | .sect = self.text_section_index.?, | |
| 1264 | }; | |
| 1265 | } | |
| 1266 | if (commands.sectionIsDebug(sect)) { | |
| 1267 | // TODO debug attributes | |
| 1268 | if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { | |
| 1269 | log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ | |
| 1270 | sect.flags, segname, sectname, | |
| 1271 | }); | |
| 1272 | } | |
| 1273 | break :blk null; | |
| 1274 | } | |
| 1275 | ||
| 1276 | if (mem.eql(u8, segname, "__TEXT")) { | |
| 1277 | if (mem.eql(u8, sectname, "__ustring")) { | |
| 1278 | if (self.ustring_section_index == null) { | |
| 1279 | self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1280 | try text_seg.addSection(self.base.allocator, "__ustring", .{}); | |
| 1281 | } | |
| 1282 | ||
| 1283 | break :blk .{ | |
| 1284 | .seg = self.text_segment_cmd_index.?, | |
| 1285 | .sect = self.ustring_section_index.?, | |
| 1286 | }; | |
| 1287 | } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { | |
| 1288 | if (self.gcc_except_tab_section_index == null) { | |
| 1289 | self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1290 | try text_seg.addSection(self.base.allocator, "__gcc_except_tab", .{}); | |
| 1291 | } | |
| 1292 | ||
| 1293 | break :blk .{ | |
| 1294 | .seg = self.text_segment_cmd_index.?, | |
| 1295 | .sect = self.gcc_except_tab_section_index.?, | |
| 1296 | }; | |
| 1297 | } else if (mem.eql(u8, sectname, "__objc_methlist")) { | |
| 1298 | if (self.objc_methlist_section_index == null) { | |
| 1299 | self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1300 | try text_seg.addSection(self.base.allocator, "__objc_methlist", .{}); | |
| 1301 | } | |
| 1302 | ||
| 1303 | break :blk .{ | |
| 1304 | .seg = self.text_segment_cmd_index.?, | |
| 1305 | .sect = self.objc_methlist_section_index.?, | |
| 1306 | }; | |
| 1307 | } else if (mem.eql(u8, sectname, "__rodata") or | |
| 1308 | mem.eql(u8, sectname, "__typelink") or | |
| 1309 | mem.eql(u8, sectname, "__itablink") or | |
| 1310 | mem.eql(u8, sectname, "__gosymtab") or | |
| 1311 | mem.eql(u8, sectname, "__gopclntab")) | |
| 1312 | { | |
| 1313 | if (self.data_const_section_index == null) { | |
| 1314 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1315 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1316 | } | |
| 1317 | ||
| 1318 | break :blk .{ | |
| 1319 | .seg = self.data_const_segment_cmd_index.?, | |
| 1320 | .sect = self.data_const_section_index.?, | |
| 1321 | }; | |
| 1322 | } else { | |
| 1323 | if (self.text_const_section_index == null) { | |
| 1324 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1325 | try text_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1326 | } | |
| 1327 | ||
| 1328 | break :blk .{ | |
| 1329 | .seg = self.text_segment_cmd_index.?, | |
| 1330 | .sect = self.text_const_section_index.?, | |
| 1331 | }; | |
| 1332 | } | |
| 1333 | } | |
| 1334 | ||
| 1335 | if (mem.eql(u8, segname, "__DATA_CONST")) { | |
| 1336 | if (self.data_const_section_index == null) { | |
| 1337 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1338 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1339 | } | |
| 1340 | ||
| 1341 | break :blk .{ | |
| 1342 | .seg = self.data_const_segment_cmd_index.?, | |
| 1343 | .sect = self.data_const_section_index.?, | |
| 1344 | }; | |
| 1345 | } | |
| 1346 | ||
| 1347 | if (mem.eql(u8, segname, "__DATA")) { | |
| 1348 | if (mem.eql(u8, sectname, "__const")) { | |
| 1349 | if (self.data_const_section_index == null) { | |
| 1350 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1351 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1352 | } | |
| 1353 | ||
| 1354 | break :blk .{ | |
| 1355 | .seg = self.data_const_segment_cmd_index.?, | |
| 1356 | .sect = self.data_const_section_index.?, | |
| 1357 | }; | |
| 1358 | } else if (mem.eql(u8, sectname, "__cfstring")) { | |
| 1359 | if (self.objc_cfstring_section_index == null) { | |
| 1360 | self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1361 | try data_const_seg.addSection(self.base.allocator, "__cfstring", .{}); | |
| 1362 | } | |
| 1363 | ||
| 1364 | break :blk .{ | |
| 1365 | .seg = self.data_const_segment_cmd_index.?, | |
| 1366 | .sect = self.objc_cfstring_section_index.?, | |
| 1367 | }; | |
| 1368 | } else if (mem.eql(u8, sectname, "__objc_classlist")) { | |
| 1369 | if (self.objc_classlist_section_index == null) { | |
| 1370 | self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1371 | try data_const_seg.addSection(self.base.allocator, "__objc_classlist", .{}); | |
| 1372 | } | |
| 1373 | ||
| 1374 | break :blk .{ | |
| 1375 | .seg = self.data_const_segment_cmd_index.?, | |
| 1376 | .sect = self.objc_classlist_section_index.?, | |
| 1377 | }; | |
| 1378 | } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { | |
| 1379 | if (self.objc_imageinfo_section_index == null) { | |
| 1380 | self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1381 | try data_const_seg.addSection(self.base.allocator, "__objc_imageinfo", .{}); | |
| 1382 | } | |
| 1383 | ||
| 1384 | break :blk .{ | |
| 1385 | .seg = self.data_const_segment_cmd_index.?, | |
| 1386 | .sect = self.objc_imageinfo_section_index.?, | |
| 1387 | }; | |
| 1388 | } else if (mem.eql(u8, sectname, "__objc_const")) { | |
| 1389 | if (self.objc_const_section_index == null) { | |
| 1390 | self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1391 | try data_seg.addSection(self.base.allocator, "__objc_const", .{}); | |
| 1392 | } | |
| 1393 | ||
| 1394 | break :blk .{ | |
| 1395 | .seg = self.data_segment_cmd_index.?, | |
| 1396 | .sect = self.objc_const_section_index.?, | |
| 1397 | }; | |
| 1398 | } else if (mem.eql(u8, sectname, "__objc_classrefs")) { | |
| 1399 | if (self.objc_classrefs_section_index == null) { | |
| 1400 | self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1401 | try data_seg.addSection(self.base.allocator, "__objc_classrefs", .{}); | |
| 1402 | } | |
| 1403 | ||
| 1404 | break :blk .{ | |
| 1405 | .seg = self.data_segment_cmd_index.?, | |
| 1406 | .sect = self.objc_classrefs_section_index.?, | |
| 1407 | }; | |
| 1408 | } else if (mem.eql(u8, sectname, "__objc_data")) { | |
| 1409 | if (self.objc_data_section_index == null) { | |
| 1410 | self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1411 | try data_seg.addSection(self.base.allocator, "__objc_data", .{}); | |
| 1412 | } | |
| 1413 | ||
| 1414 | break :blk .{ | |
| 1415 | .seg = self.data_segment_cmd_index.?, | |
| 1416 | .sect = self.objc_data_section_index.?, | |
| 1417 | }; | |
| 1418 | } else { | |
| 1419 | if (self.data_section_index == null) { | |
| 1420 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1421 | try data_seg.addSection(self.base.allocator, "__data", .{}); | |
| 1422 | } | |
| 1423 | ||
| 1424 | break :blk .{ | |
| 1425 | .seg = self.data_segment_cmd_index.?, | |
| 1426 | .sect = self.data_section_index.?, | |
| 1427 | }; | |
| 1428 | } | |
| 1429 | } | |
| 1430 | ||
| 1431 | if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { | |
| 1432 | log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ | |
| 1433 | sect.flags, segname, sectname, | |
| 1434 | }); | |
| 1435 | } | |
| 1436 | ||
| 1437 | break :blk null; | |
| 1438 | }, | |
| 1439 | else => break :blk null, | |
| 1440 | } | |
| 1441 | }; | |
| 1442 | ||
| 1443 | if (res) |match| { | |
| 1444 | try self.createSectionOrdinal(match); | |
| 1445 | } | |
| 1446 | ||
| 1447 | return res; | |
| 1448 | } | |
| 1449 | ||
| 1450 | fn sortSections(self: *MachO) !void { | |
| 1451 | var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1452 | defer text_index_mapping.deinit(); | |
| 1453 | var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1454 | defer data_const_index_mapping.deinit(); | |
| 1455 | var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1456 | defer data_index_mapping.deinit(); | |
| 1457 | ||
| 1458 | { | |
| 1459 | // __TEXT segment | |
| 1460 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1461 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1462 | defer self.base.allocator.free(sections); | |
| 1463 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1464 | ||
| 1465 | const indices = &[_]*?u16{ | |
| 1466 | &self.text_section_index, | |
| 1467 | &self.stubs_section_index, | |
| 1468 | &self.stub_helper_section_index, | |
| 1469 | &self.gcc_except_tab_section_index, | |
| 1470 | &self.cstring_section_index, | |
| 1471 | &self.ustring_section_index, | |
| 1472 | &self.text_const_section_index, | |
| 1473 | &self.objc_methname_section_index, | |
| 1474 | &self.objc_methtype_section_index, | |
| 1475 | &self.objc_classname_section_index, | |
| 1476 | &self.eh_frame_section_index, | |
| 1477 | }; | |
| 1478 | for (indices) |maybe_index| { | |
| 1479 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1480 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1481 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1482 | try text_index_mapping.putNoClobber(index, idx); | |
| 1483 | break :blk idx; | |
| 1484 | } else continue; | |
| 1485 | maybe_index.* = new_index; | |
| 1486 | } | |
| 1487 | } | |
| 1488 | ||
| 1489 | { | |
| 1490 | // __DATA_CONST segment | |
| 1491 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1492 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1493 | defer self.base.allocator.free(sections); | |
| 1494 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1495 | ||
| 1496 | const indices = &[_]*?u16{ | |
| 1497 | &self.got_section_index, | |
| 1498 | &self.mod_init_func_section_index, | |
| 1499 | &self.mod_term_func_section_index, | |
| 1500 | &self.data_const_section_index, | |
| 1501 | &self.objc_cfstring_section_index, | |
| 1502 | &self.objc_classlist_section_index, | |
| 1503 | &self.objc_imageinfo_section_index, | |
| 1504 | }; | |
| 1505 | for (indices) |maybe_index| { | |
| 1506 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1507 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1508 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1509 | try data_const_index_mapping.putNoClobber(index, idx); | |
| 1510 | break :blk idx; | |
| 1511 | } else continue; | |
| 1512 | maybe_index.* = new_index; | |
| 1513 | } | |
| 1514 | } | |
| 1515 | ||
| 1516 | { | |
| 1517 | // __DATA segment | |
| 1518 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1519 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1520 | defer self.base.allocator.free(sections); | |
| 1521 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1522 | ||
| 1523 | // __DATA segment | |
| 1524 | const indices = &[_]*?u16{ | |
| 1525 | &self.la_symbol_ptr_section_index, | |
| 1526 | &self.objc_const_section_index, | |
| 1527 | &self.objc_selrefs_section_index, | |
| 1528 | &self.objc_classrefs_section_index, | |
| 1529 | &self.objc_data_section_index, | |
| 1530 | &self.data_section_index, | |
| 1531 | &self.tlv_section_index, | |
| 1532 | &self.tlv_data_section_index, | |
| 1533 | &self.tlv_bss_section_index, | |
| 1534 | &self.bss_section_index, | |
| 1535 | &self.common_section_index, | |
| 1536 | }; | |
| 1537 | for (indices) |maybe_index| { | |
| 1538 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1539 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1540 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1541 | try data_index_mapping.putNoClobber(index, idx); | |
| 1542 | break :blk idx; | |
| 1543 | } else continue; | |
| 1544 | maybe_index.* = new_index; | |
| 1545 | } | |
| 1546 | } | |
| 1547 | ||
| 1548 | { | |
| 1549 | var transient: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}; | |
| 1550 | try transient.ensureCapacity(self.base.allocator, self.blocks.count()); | |
| 1551 | ||
| 1552 | var it = self.blocks.iterator(); | |
| 1553 | while (it.next()) |entry| { | |
| 1554 | const old = entry.key_ptr.*; | |
| 1555 | const sect = if (old.seg == self.text_segment_cmd_index.?) | |
| 1556 | text_index_mapping.get(old.sect).? | |
| 1557 | else if (old.seg == self.data_const_segment_cmd_index.?) | |
| 1558 | data_const_index_mapping.get(old.sect).? | |
| 1559 | else | |
| 1560 | data_index_mapping.get(old.sect).?; | |
| 1561 | transient.putAssumeCapacityNoClobber(.{ | |
| 1562 | .seg = old.seg, | |
| 1563 | .sect = sect, | |
| 1564 | }, entry.value_ptr.*); | |
| 1565 | } | |
| 1566 | ||
| 1567 | self.blocks.clearAndFree(self.base.allocator); | |
| 1568 | self.blocks.deinit(self.base.allocator); | |
| 1569 | self.blocks = transient; | |
| 1570 | } | |
| 1571 | ||
| 1572 | { | |
| 1573 | // Create new section ordinals. | |
| 1574 | self.section_ordinals.clearRetainingCapacity(); | |
| 1575 | self.section_to_ordinal.clearRetainingCapacity(); | |
| 1576 | // First ordinal is always null | |
| 1577 | self.section_ordinals.appendAssumeCapacity(.{ | |
| 1578 | .seg = 0, | |
| 1579 | .sect = 0, | |
| 1580 | }); | |
| 1581 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1582 | for (text_seg.sections.items) |_, sect_id| { | |
| 1583 | try self.createSectionOrdinal(.{ | |
| 1584 | .seg = self.text_segment_cmd_index.?, | |
| 1585 | .sect = @intCast(u16, sect_id), | |
| 1586 | }); | |
| 1587 | } | |
| 1588 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1589 | for (data_const_seg.sections.items) |_, sect_id| { | |
| 1590 | try self.createSectionOrdinal(.{ | |
| 1591 | .seg = self.data_const_segment_cmd_index.?, | |
| 1592 | .sect = @intCast(u16, sect_id), | |
| 1593 | }); | |
| 1594 | } | |
| 1595 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1596 | for (data_seg.sections.items) |_, sect_id| { | |
| 1597 | try self.createSectionOrdinal(.{ | |
| 1598 | .seg = self.data_segment_cmd_index.?, | |
| 1599 | .sect = @intCast(u16, sect_id), | |
| 1600 | }); | |
| 1601 | } | |
| 1602 | } | |
| 1603 | } | |
| 1604 | ||
| 1605 | fn allocateTextSegment(self: *MachO) !void { | |
| 1606 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1607 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 1608 | ||
| 1609 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; | |
| 1610 | seg.inner.fileoff = 0; | |
| 1611 | seg.inner.vmaddr = base_vmaddr; | |
| 1612 | ||
| 1613 | // Set stubs and stub_helper sizes | |
| 1614 | const stubs = &seg.sections.items[self.stubs_section_index.?]; | |
| 1615 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; | |
| 1616 | stubs.size += nstubs * stubs.reserved2; | |
| 1617 | ||
| 1618 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { | |
| 1619 | .x86_64 => 10, | |
| 1620 | .aarch64 => 3 * @sizeOf(u32), | |
| 1621 | else => unreachable, | |
| 1622 | }; | |
| 1623 | stub_helper.size += nstubs * stub_size; | |
| 1624 | ||
| 1625 | var sizeofcmds: u64 = 0; | |
| 1626 | for (self.load_commands.items) |lc| { | |
| 1627 | sizeofcmds += lc.cmdsize(); | |
| 1628 | } | |
| 1629 | ||
| 1630 | try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); | |
| 1631 | ||
| 1632 | // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. | |
| 1633 | var min_alignment: u32 = 0; | |
| 1634 | for (seg.sections.items) |sect| { | |
| 1635 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 1636 | min_alignment = math.max(min_alignment, alignment); | |
| 1637 | } | |
| 1638 | ||
| 1639 | assert(min_alignment > 0); | |
| 1640 | const last_sect_idx = seg.sections.items.len - 1; | |
| 1641 | const last_sect = seg.sections.items[last_sect_idx]; | |
| 1642 | const shift: u32 = blk: { | |
| 1643 | const diff = seg.inner.filesize - last_sect.offset - last_sect.size; | |
| 1644 | const factor = @divTrunc(diff, min_alignment); | |
| 1645 | break :blk @intCast(u32, factor * min_alignment); | |
| 1646 | }; | |
| 1647 | ||
| 1648 | if (shift > 0) { | |
| 1649 | for (seg.sections.items) |*sect| { | |
| 1650 | sect.offset += shift; | |
| 1651 | sect.addr += shift; | |
| 1652 | } | |
| 1653 | } | |
| 1654 | } | |
| 1655 | ||
| 1656 | fn allocateDataConstSegment(self: *MachO) !void { | |
| 1657 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1658 | const nentries = @intCast(u32, self.got_entries.items.len); | |
| 1659 | ||
| 1660 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1661 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; | |
| 1662 | seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; | |
| 1663 | ||
| 1664 | // Set got size | |
| 1665 | const got = &seg.sections.items[self.got_section_index.?]; | |
| 1666 | got.size += nentries * @sizeOf(u64); | |
| 1667 | ||
| 1668 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); | |
| 1669 | } | |
| 1670 | ||
| 1671 | fn allocateDataSegment(self: *MachO) !void { | |
| 1672 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1673 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 1674 | ||
| 1675 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1676 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; | |
| 1677 | seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; | |
| 1678 | ||
| 1679 | // Set la_symbol_ptr and data size | |
| 1680 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1681 | const data = &seg.sections.items[self.data_section_index.?]; | |
| 1682 | la_symbol_ptr.size += nstubs * @sizeOf(u64); | |
| 1683 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder | |
| 1684 | ||
| 1685 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); | |
| 1686 | } | |
| 1687 | ||
| 1688 | fn allocateLinkeditSegment(self: *MachO) void { | |
| 1689 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 1690 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1691 | seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; | |
| 1692 | seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; | |
| 1693 | } | |
| 1694 | ||
| 1695 | fn allocateSegment(self: *MachO, index: u16, offset: u64) !void { | |
| 1696 | const seg = &self.load_commands.items[index].Segment; | |
| 1697 | ||
| 1698 | // Allocate the sections according to their alignment at the beginning of the segment. | |
| 1699 | var start: u64 = offset; | |
| 1700 | for (seg.sections.items) |*sect| { | |
| 1701 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 1702 | const start_aligned = mem.alignForwardGeneric(u64, start, alignment); | |
| 1703 | const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment); | |
| 1704 | sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned); | |
| 1705 | sect.addr = seg.inner.vmaddr + start_aligned; | |
| 1706 | start = end_aligned; | |
| 1707 | } | |
| 1708 | ||
| 1709 | const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size); | |
| 1710 | seg.inner.filesize = seg_size_aligned; | |
| 1711 | seg.inner.vmsize = seg_size_aligned; | |
| 1712 | } | |
| 1713 | ||
| 1714 | fn allocateTextBlocks(self: *MachO) !void { | |
| 1715 | var it = self.blocks.iterator(); | |
| 1716 | while (it.next()) |entry| { | |
| 1717 | const match = entry.key_ptr.*; | |
| 1718 | var block: *TextBlock = entry.value_ptr.*; | |
| 1719 | ||
| 1720 | // Find the first block | |
| 1721 | while (block.prev) |prev| { | |
| 1722 | block = prev; | |
| 1723 | } | |
| 1724 | ||
| 1725 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1726 | const sect = seg.sections.items[match.sect]; | |
| 1727 | ||
| 1728 | var base_addr: u64 = sect.addr; | |
| 1729 | const n_sect = self.section_to_ordinal.get(match) orelse unreachable; | |
| 1730 | ||
| 1731 | log.debug(" within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); | |
| 1732 | log.debug(" {}", .{sect}); | |
| 1733 | ||
| 1734 | while (true) { | |
| 1735 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1736 | base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment); | |
| 1737 | ||
| 1738 | const sym = &self.locals.items[block.local_sym_index]; | |
| 1739 | sym.n_value = base_addr; | |
| 1740 | sym.n_sect = n_sect; | |
| 1741 | ||
| 1742 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1743 | self.getString(sym.n_strx), | |
| 1744 | base_addr, | |
| 1745 | base_addr + block.size, | |
| 1746 | block.size, | |
| 1747 | block.alignment, | |
| 1748 | }); | |
| 1749 | ||
| 1750 | // Update each alias (if any) | |
| 1751 | for (block.aliases.items) |index| { | |
| 1752 | const alias_sym = &self.locals.items[index]; | |
| 1753 | alias_sym.n_value = base_addr; | |
| 1754 | alias_sym.n_sect = n_sect; | |
| 1755 | } | |
| 1756 | ||
| 1757 | // Update each symbol contained within the TextBlock | |
| 1758 | for (block.contained.items) |sym_at_off| { | |
| 1759 | const contained_sym = &self.locals.items[sym_at_off.local_sym_index]; | |
| 1760 | contained_sym.n_value = base_addr + sym_at_off.offset; | |
| 1761 | contained_sym.n_sect = n_sect; | |
| 1762 | } | |
| 1763 | ||
| 1764 | base_addr += block.size; | |
| 1765 | ||
| 1766 | if (block.next) |next| { | |
| 1767 | block = next; | |
| 1768 | } else break; | |
| 1769 | } | |
| 1770 | } | |
| 1771 | ||
| 1772 | // Update globals | |
| 1773 | { | |
| 1774 | var sym_it = self.symbol_resolver.valueIterator(); | |
| 1775 | while (sym_it.next()) |resolv| { | |
| 1776 | if (resolv.where != .global) continue; | |
| 1777 | ||
| 1778 | assert(resolv.local_sym_index != 0); | |
| 1779 | const local_sym = self.locals.items[resolv.local_sym_index]; | |
| 1780 | const sym = &self.globals.items[resolv.where_index]; | |
| 1781 | sym.n_value = local_sym.n_value; | |
| 1782 | sym.n_sect = local_sym.n_sect; | |
| 1783 | } | |
| 1784 | } | |
| 1785 | } | |
| 1786 | ||
| 1787 | fn writeTextBlocks(self: *MachO) !void { | |
| 1788 | var it = self.blocks.iterator(); | |
| 1789 | while (it.next()) |entry| { | |
| 1790 | const match = entry.key_ptr.*; | |
| 1791 | var block: *TextBlock = entry.value_ptr.*; | |
| 1792 | ||
| 1793 | while (block.prev) |prev| { | |
| 1794 | block = prev; | |
| 1795 | } | |
| 1796 | ||
| 1797 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1798 | const sect = seg.sections.items[match.sect]; | |
| 1799 | const sect_type = commands.sectionType(sect); | |
| 1800 | ||
| 1801 | log.debug(" for section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); | |
| 1802 | log.debug(" {}", .{sect}); | |
| 1803 | ||
| 1804 | var code = try self.base.allocator.alloc(u8, sect.size); | |
| 1805 | defer self.base.allocator.free(code); | |
| 1806 | ||
| 1807 | if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) { | |
| 1808 | mem.set(u8, code, 0); | |
| 1809 | } else { | |
| 1810 | var base_off: u64 = 0; | |
| 1811 | ||
| 1812 | while (true) { | |
| 1813 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1814 | const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment); | |
| 1815 | ||
| 1816 | const sym = self.locals.items[block.local_sym_index]; | |
| 1817 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1818 | self.getString(sym.n_strx), | |
| 1819 | aligned_base_off, | |
| 1820 | aligned_base_off + block.size, | |
| 1821 | block.size, | |
| 1822 | block.alignment, | |
| 1823 | }); | |
| 1824 | ||
| 1825 | try block.resolveRelocs(self); | |
| 1826 | mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items); | |
| 1827 | ||
| 1828 | // TODO NOP for machine code instead of just zeroing out | |
| 1829 | const padding_len = aligned_base_off - base_off; | |
| 1830 | mem.set(u8, code[base_off..][0..padding_len], 0); | |
| 1831 | ||
| 1832 | base_off = aligned_base_off + block.size; | |
| 1833 | ||
| 1834 | if (block.next) |next| { | |
| 1835 | block = next; | |
| 1836 | } else break; | |
| 1837 | } | |
| 1838 | ||
| 1839 | mem.set(u8, code[base_off..], 0); | |
| 1840 | } | |
| 1841 | ||
| 1842 | try self.base.file.?.pwriteAll(code, sect.offset); | |
| 1843 | } | |
| 1844 | } | |
| 1845 | ||
| 1846 | fn writeStubHelperCommon(self: *MachO) !void { | |
| 1847 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1848 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1849 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1850 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 1851 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1852 | const data = &data_segment.sections.items[self.data_section_index.?]; | |
| 1853 | ||
| 1854 | self.stub_helper_stubs_start_off = blk: { | |
| 1855 | switch (self.base.options.target.cpu.arch) { | |
| 1856 | .x86_64 => { | |
| 1857 | const code_size = 15; | |
| 1858 | var code: [code_size]u8 = undefined; | |
| 1859 | // lea %r11, [rip + disp] | |
| 1860 | code[0] = 0x4c; | |
| 1861 | code[1] = 0x8d; | |
| 1862 | code[2] = 0x1d; | |
| 1863 | { | |
| 1864 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1865 | const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); | |
| 1866 | mem.writeIntLittle(u32, code[3..7], displacement); | |
| 1867 | } | |
| 1868 | // push %r11 | |
| 1869 | code[7] = 0x41; | |
| 1870 | code[8] = 0x53; | |
| 1871 | // jmp [rip + disp] | |
| 1872 | code[9] = 0xff; | |
| 1873 | code[10] = 0x25; | |
| 1874 | { | |
| 1875 | const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ | |
| 1876 | .strtab = &self.strtab, | |
| 1877 | }) orelse unreachable; | |
| 1878 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 1879 | const got_index = self.got_entries_map.get(.{ | |
| 1880 | .where = .import, | |
| 1881 | .where_index = resolv.where_index, | |
| 1882 | }) orelse unreachable; | |
| 1883 | const addr = got.addr + got_index * @sizeOf(u64); | |
| 1884 | const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); | |
| 1885 | mem.writeIntLittle(u32, code[11..], displacement); | |
| 1886 | } | |
| 1887 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1888 | break :blk stub_helper.offset + code_size; | |
| 1889 | }, | |
| 1890 | .aarch64 => { | |
| 1891 | var code: [6 * @sizeOf(u32)]u8 = undefined; | |
| 1892 | data_blk_outer: { | |
| 1893 | const this_addr = stub_helper.addr; | |
| 1894 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1895 | data_blk: { | |
| 1896 | const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk; | |
| 1897 | // adr x17, disp | |
| 1898 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1899 | // nop | |
| 1900 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1901 | break :data_blk_outer; | |
| 1902 | } | |
| 1903 | data_blk: { | |
| 1904 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1905 | const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk; | |
| 1906 | // nop | |
| 1907 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1908 | // adr x17, disp | |
| 1909 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1910 | break :data_blk_outer; | |
| 1911 | } | |
| 1912 | // Jump is too big, replace adr with adrp and add. | |
| 1913 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1914 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1915 | const pages = @intCast(i21, target_page - this_page); | |
| 1916 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); | |
| 1917 | const narrowed = @truncate(u12, target_addr); | |
| 1918 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); | |
| 1919 | } | |
| 1920 | // stp x16, x17, [sp, #-16]! | |
| 1921 | code[8] = 0xf0; | |
| 1922 | code[9] = 0x47; | |
| 1923 | code[10] = 0xbf; | |
| 1924 | code[11] = 0xa9; | |
| 1925 | binder_blk_outer: { | |
| 1926 | const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ | |
| 1927 | .strtab = &self.strtab, | |
| 1928 | }) orelse unreachable; | |
| 1929 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 1930 | const got_index = self.got_entries_map.get(.{ | |
| 1931 | .where = .import, | |
| 1932 | .where_index = resolv.where_index, | |
| 1933 | }) orelse unreachable; | |
| 1934 | const this_addr = stub_helper.addr + 3 * @sizeOf(u32); | |
| 1935 | const target_addr = got.addr + got_index * @sizeOf(u64); | |
| 1936 | binder_blk: { | |
| 1937 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk; | |
| 1938 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1939 | // ldr x16, label | |
| 1940 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ | |
| 1941 | .literal = literal, | |
| 1942 | }).toU32()); | |
| 1943 | // nop | |
| 1944 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); | |
| 1945 | break :binder_blk_outer; | |
| 1946 | } | |
| 1947 | binder_blk: { | |
| 1948 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1949 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk; | |
| 1950 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1951 | // Pad with nop to please division. | |
| 1952 | // nop | |
| 1953 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); | |
| 1954 | // ldr x16, label | |
| 1955 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1956 | .literal = literal, | |
| 1957 | }).toU32()); | |
| 1958 | break :binder_blk_outer; | |
| 1959 | } | |
| 1960 | // Use adrp followed by ldr(immediate). | |
| 1961 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1962 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1963 | const pages = @intCast(i21, target_page - this_page); | |
| 1964 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1965 | const narrowed = @truncate(u12, target_addr); | |
| 1966 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1967 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1968 | .register = .{ | |
| 1969 | .rn = .x16, | |
| 1970 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1971 | }, | |
| 1972 | }).toU32()); | |
| 1973 | } | |
| 1974 | // br x16 | |
| 1975 | code[20] = 0x00; | |
| 1976 | code[21] = 0x02; | |
| 1977 | code[22] = 0x1f; | |
| 1978 | code[23] = 0xd6; | |
| 1979 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1980 | break :blk stub_helper.offset + 6 * @sizeOf(u32); | |
| 1981 | }, | |
| 1982 | else => unreachable, | |
| 1983 | } | |
| 1984 | }; | |
| 1985 | ||
| 1986 | for (self.stubs.items) |_, i| { | |
| 1987 | const index = @intCast(u32, i); | |
| 1988 | // TODO weak bound pointers | |
| 1989 | try self.writeLazySymbolPointer(index); | |
| 1990 | try self.writeStub(index); | |
| 1991 | try self.writeStubInStubHelper(index); | |
| 1992 | } | |
| 1993 | } | |
| 1994 | ||
| 1995 | fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { | |
| 1996 | const object = self.objects.items[object_id]; | |
| 1997 | ||
| 1998 | log.debug("resolving symbols in '{s}'", .{object.name}); | |
| 1999 | ||
| 2000 | for (object.symtab.items) |sym, id| { | |
| 2001 | const sym_id = @intCast(u32, id); | |
| 2002 | const sym_name = object.getString(sym.n_strx); | |
| 2003 | ||
| 2004 | if (symbolIsStab(sym)) { | |
| 2005 | log.err("unhandled symbol type: stab", .{}); | |
| 2006 | log.err(" symbol '{s}'", .{sym_name}); | |
| 2007 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 2008 | return error.UnhandledSymbolType; | |
| 2009 | } | |
| 2010 | ||
| 2011 | if (symbolIsIndr(sym)) { | |
| 2012 | log.err("unhandled symbol type: indirect", .{}); | |
| 2013 | log.err(" symbol '{s}'", .{sym_name}); | |
| 2014 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 2015 | return error.UnhandledSymbolType; | |
| 2016 | } | |
| 2017 | ||
| 2018 | if (symbolIsAbs(sym)) { | |
| 2019 | log.err("unhandled symbol type: absolute", .{}); | |
| 2020 | log.err(" symbol '{s}'", .{sym_name}); | |
| 2021 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 2022 | return error.UnhandledSymbolType; | |
| 2023 | } | |
| 2024 | ||
| 2025 | const n_strx = try self.makeString(sym_name); | |
| 2026 | if (symbolIsSect(sym)) { | |
| 2027 | // Defined symbol regardless of scope lands in the locals symbol table. | |
| 2028 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 2029 | try self.locals.append(self.base.allocator, .{ | |
| 2030 | .n_strx = n_strx, | |
| 2031 | .n_type = macho.N_SECT, | |
| 2032 | .n_sect = 0, | |
| 2033 | .n_desc = 0, | |
| 2034 | .n_value = sym.n_value, | |
| 2035 | }); | |
| 2036 | try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index); | |
| 2037 | try object.reverse_symbol_mapping.putNoClobber(self.base.allocator, local_sym_index, sym_id); | |
| 2038 | ||
| 2039 | // If the symbol's scope is not local aka translation unit, then we need work out | |
| 2040 | // if we should save the symbol as a global, or potentially flag the error. | |
| 2041 | if (!symbolIsExt(sym)) continue; | |
| 2042 | ||
| 2043 | const local = self.locals.items[local_sym_index]; | |
| 2044 | const resolv = self.symbol_resolver.getPtr(n_strx) orelse { | |
| 2045 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2046 | try self.globals.append(self.base.allocator, .{ | |
| 2047 | .n_strx = n_strx, | |
| 2048 | .n_type = sym.n_type, | |
| 2049 | .n_sect = 0, | |
| 2050 | .n_desc = sym.n_desc, | |
| 2051 | .n_value = sym.n_value, | |
| 2052 | }); | |
| 2053 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 2054 | .where = .global, | |
| 2055 | .where_index = global_sym_index, | |
| 2056 | .local_sym_index = local_sym_index, | |
| 2057 | .file = object_id, | |
| 2058 | }); | |
| 2059 | continue; | |
| 2060 | }; | |
| 2061 | ||
| 2062 | switch (resolv.where) { | |
| 2063 | .import => unreachable, | |
| 2064 | .global => { | |
| 2065 | const global = &self.globals.items[resolv.where_index]; | |
| 2066 | ||
| 2067 | if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and | |
| 2068 | !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) | |
| 2069 | { | |
| 2070 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | |
| 2071 | log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 2072 | log.err(" next definition in '{s}'", .{object.name.?}); | |
| 2073 | return error.MultipleSymbolDefinitions; | |
| 2074 | } | |
| 2075 | ||
| 2076 | if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it. | |
| 2077 | ||
| 2078 | // Otherwise, update the resolver and the global symbol. | |
| 2079 | global.n_type = sym.n_type; | |
| 2080 | resolv.local_sym_index = local_sym_index; | |
| 2081 | resolv.file = object_id; | |
| 2082 | ||
| 2083 | continue; | |
| 2084 | }, | |
| 2085 | .undef => { | |
| 2086 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2087 | undef.* = .{ | |
| 2088 | .n_strx = 0, | |
| 2089 | .n_type = macho.N_UNDF, | |
| 2090 | .n_sect = 0, | |
| 2091 | .n_desc = 0, | |
| 2092 | .n_value = 0, | |
| 2093 | }; | |
| 2094 | }, | |
| 2095 | .tentative => { | |
| 2096 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 2097 | tentative.* = .{ | |
| 2098 | .n_strx = 0, | |
| 2099 | .n_type = macho.N_UNDF, | |
| 2100 | .n_sect = 0, | |
| 2101 | .n_desc = 0, | |
| 2102 | .n_value = 0, | |
| 2103 | }; | |
| 2104 | }, | |
| 2105 | } | |
| 2106 | ||
| 2107 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2108 | try self.globals.append(self.base.allocator, .{ | |
| 2109 | .n_strx = local.n_strx, | |
| 2110 | .n_type = sym.n_type, | |
| 2111 | .n_sect = 0, | |
| 2112 | .n_desc = sym.n_desc, | |
| 2113 | .n_value = sym.n_value, | |
| 2114 | }); | |
| 2115 | resolv.* = .{ | |
| 2116 | .where = .global, | |
| 2117 | .where_index = global_sym_index, | |
| 2118 | .local_sym_index = local_sym_index, | |
| 2119 | .file = object_id, | |
| 2120 | }; | |
| 2121 | } else if (symbolIsTentative(sym)) { | |
| 2122 | // Symbol is a tentative definition. | |
| 2123 | const resolv = self.symbol_resolver.getPtr(n_strx) orelse { | |
| 2124 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 2125 | try self.tentatives.append(self.base.allocator, .{ | |
| 2126 | .n_strx = try self.makeString(sym_name), | |
| 2127 | .n_type = sym.n_type, | |
| 2128 | .n_sect = 0, | |
| 2129 | .n_desc = sym.n_desc, | |
| 2130 | .n_value = sym.n_value, | |
| 2131 | }); | |
| 2132 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 2133 | .where = .tentative, | |
| 2134 | .where_index = tent_sym_index, | |
| 2135 | .file = object_id, | |
| 2136 | }); | |
| 2137 | continue; | |
| 2138 | }; | |
| 2139 | ||
| 2140 | switch (resolv.where) { | |
| 2141 | .import => unreachable, | |
| 2142 | .global => {}, | |
| 2143 | .undef => { | |
| 2144 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2145 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 2146 | try self.tentatives.append(self.base.allocator, .{ | |
| 2147 | .n_strx = undef.n_strx, | |
| 2148 | .n_type = sym.n_type, | |
| 2149 | .n_sect = 0, | |
| 2150 | .n_desc = sym.n_desc, | |
| 2151 | .n_value = sym.n_value, | |
| 2152 | }); | |
| 2153 | resolv.* = .{ | |
| 2154 | .where = .tentative, | |
| 2155 | .where_index = tent_sym_index, | |
| 2156 | .file = object_id, | |
| 2157 | }; | |
| 2158 | undef.* = .{ | |
| 2159 | .n_strx = 0, | |
| 2160 | .n_type = macho.N_UNDF, | |
| 2161 | .n_sect = 0, | |
| 2162 | .n_desc = 0, | |
| 2163 | .n_value = 0, | |
| 2164 | }; | |
| 2165 | }, | |
| 2166 | .tentative => { | |
| 2167 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 2168 | if (tentative.n_value >= sym.n_value) continue; | |
| 2169 | ||
| 2170 | tentative.n_desc = sym.n_desc; | |
| 2171 | tentative.n_value = sym.n_value; | |
| 2172 | resolv.file = object_id; | |
| 2173 | }, | |
| 2174 | } | |
| 2175 | } else { | |
| 2176 | // Symbol is undefined. | |
| 2177 | if (self.symbol_resolver.contains(n_strx)) continue; | |
| 2178 | ||
| 2179 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 2180 | try self.undefs.append(self.base.allocator, .{ | |
| 2181 | .n_strx = try self.makeString(sym_name), | |
| 2182 | .n_type = macho.N_UNDF, | |
| 2183 | .n_sect = 0, | |
| 2184 | .n_desc = 0, | |
| 2185 | .n_value = 0, | |
| 2186 | }); | |
| 2187 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 2188 | .where = .undef, | |
| 2189 | .where_index = undef_sym_index, | |
| 2190 | .file = object_id, | |
| 2191 | }); | |
| 2192 | } | |
| 2193 | } | |
| 2194 | } | |
| 2195 | ||
| 2196 | fn resolveSymbols(self: *MachO) !void { | |
| 2197 | // TODO mimicking insertion of null symbol from incremental linker. | |
| 2198 | // This will need to moved. | |
| 2199 | try self.locals.append(self.base.allocator, .{ | |
| 2200 | .n_strx = 0, | |
| 2201 | .n_type = macho.N_UNDF, | |
| 2202 | .n_sect = 0, | |
| 2203 | .n_desc = 0, | |
| 2204 | .n_value = 0, | |
| 2205 | }); | |
| 2206 | try self.strtab.append(self.base.allocator, 0); | |
| 2207 | ||
| 2208 | // First pass, resolve symbols in provided objects. | |
| 2209 | for (self.objects.items) |_, object_id| { | |
| 2210 | try self.resolveSymbolsInObject(@intCast(u16, object_id)); | |
| 2211 | } | |
| 2212 | ||
| 2213 | // Second pass, resolve symbols in static libraries. | |
| 2214 | var next_sym: usize = 0; | |
| 2215 | loop: while (true) : (next_sym += 1) { | |
| 2216 | if (next_sym == self.undefs.items.len) break; | |
| 2217 | ||
| 2218 | const sym = self.undefs.items[next_sym]; | |
| 2219 | if (symbolIsNull(sym)) continue; | |
| 2220 | ||
| 2221 | const sym_name = self.getString(sym.n_strx); | |
| 2222 | ||
| 2223 | for (self.archives.items) |archive| { | |
| 2224 | // Check if the entry exists in a static archive. | |
| 2225 | const offsets = archive.toc.get(sym_name) orelse { | |
| 2226 | // No hit. | |
| 2227 | continue; | |
| 2228 | }; | |
| 2229 | assert(offsets.items.len > 0); | |
| 2230 | ||
| 2231 | const object = try archive.parseObject(offsets.items[0]); | |
| 2232 | const object_id = @intCast(u16, self.objects.items.len); | |
| 2233 | try self.objects.append(self.base.allocator, object); | |
| 2234 | try self.resolveSymbolsInObject(object_id); | |
| 2235 | ||
| 2236 | continue :loop; | |
| 2237 | } | |
| 2238 | } | |
| 2239 | ||
| 2240 | // Convert any tentative definition into a regular symbol and allocate | |
| 2241 | // text blocks for each tentative defintion. | |
| 2242 | for (self.tentatives.items) |sym| { | |
| 2243 | if (symbolIsNull(sym)) continue; | |
| 2244 | ||
| 2245 | const match: MatchingSection = blk: { | |
| 2246 | if (self.common_section_index == null) { | |
| 2247 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2248 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2249 | try data_seg.addSection(self.base.allocator, "__common", .{ | |
| 2250 | .flags = macho.S_ZEROFILL, | |
| 2251 | }); | |
| 2252 | } | |
| 2253 | break :blk .{ | |
| 2254 | .seg = self.data_segment_cmd_index.?, | |
| 2255 | .sect = self.common_section_index.?, | |
| 2256 | }; | |
| 2257 | }; | |
| 2258 | try self.createSectionOrdinal(match); | |
| 2259 | ||
| 2260 | const size = sym.n_value; | |
| 2261 | const code = try self.base.allocator.alloc(u8, size); | |
| 2262 | defer self.base.allocator.free(code); | |
| 2263 | mem.set(u8, code, 0); | |
| 2264 | const alignment = (sym.n_desc >> 8) & 0x0f; | |
| 2265 | ||
| 2266 | const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable; | |
| 2267 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 2268 | var nlist = macho.nlist_64{ | |
| 2269 | .n_strx = sym.n_strx, | |
| 2270 | .n_type = macho.N_SECT, | |
| 2271 | .n_sect = self.section_to_ordinal.get(match) orelse unreachable, | |
| 2272 | .n_desc = 0, | |
| 2273 | .n_value = 0, | |
| 2274 | }; | |
| 2275 | try self.locals.append(self.base.allocator, nlist); | |
| 2276 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2277 | nlist.n_type |= macho.N_EXT; | |
| 2278 | try self.globals.append(self.base.allocator, nlist); | |
| 2279 | resolv.* = .{ | |
| 2280 | .where = .global, | |
| 2281 | .where_index = global_sym_index, | |
| 2282 | .local_sym_index = local_sym_index, | |
| 2283 | }; | |
| 2284 | ||
| 2285 | const block = try self.base.allocator.create(TextBlock); | |
| 2286 | block.* = TextBlock.empty; | |
| 2287 | block.local_sym_index = local_sym_index; | |
| 2288 | block.size = size; | |
| 2289 | block.alignment = alignment; | |
| 2290 | try self.managed_blocks.append(self.base.allocator, block); | |
| 2291 | ||
| 2292 | try block.code.appendSlice(self.base.allocator, code); | |
| 2293 | ||
| 2294 | // Update target section's metadata | |
| 2295 | // TODO should we update segment's size here too? | |
| 2296 | // How does it tie with incremental space allocs? | |
| 2297 | const tseg = &self.load_commands.items[match.seg].Segment; | |
| 2298 | const tsect = &tseg.sections.items[match.sect]; | |
| 2299 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 2300 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 2301 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 2302 | tsect.size = new_size; | |
| 2303 | tsect.@"align" = new_alignment; | |
| 2304 | ||
| 2305 | if (self.blocks.getPtr(match)) |last| { | |
| 2306 | last.*.next = block; | |
| 2307 | block.prev = last.*; | |
| 2308 | last.* = block; | |
| 2309 | } else { | |
| 2310 | try self.blocks.putNoClobber(self.base.allocator, match, block); | |
| 2311 | } | |
| 2312 | } | |
| 2313 | ||
| 2314 | // Third pass, resolve symbols in dynamic libraries. | |
| 2315 | { | |
| 2316 | // Put dyld_stub_binder as an undefined special symbol. | |
| 2317 | const n_strx = try self.makeString("dyld_stub_binder"); | |
| 2318 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 2319 | try self.undefs.append(self.base.allocator, .{ | |
| 2320 | .n_strx = n_strx, | |
| 2321 | .n_type = macho.N_UNDF, | |
| 2322 | .n_sect = 0, | |
| 2323 | .n_desc = 0, | |
| 2324 | .n_value = 0, | |
| 2325 | }); | |
| 2326 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 2327 | .where = .undef, | |
| 2328 | .where_index = undef_sym_index, | |
| 2329 | }); | |
| 2330 | } | |
| 2331 | ||
| 2332 | var referenced = std.AutoHashMap(*Dylib, void).init(self.base.allocator); | |
| 2333 | defer referenced.deinit(); | |
| 2334 | ||
| 2335 | loop: for (self.undefs.items) |sym| { | |
| 2336 | if (symbolIsNull(sym)) continue; | |
| 2337 | ||
| 2338 | const sym_name = self.getString(sym.n_strx); | |
| 2339 | for (self.dylibs.items) |dylib| { | |
| 2340 | if (!dylib.symbols.contains(sym_name)) continue; | |
| 2341 | ||
| 2342 | if (!referenced.contains(dylib)) { | |
| 2343 | // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. | |
| 2344 | dylib.ordinal = self.next_dylib_ordinal; | |
| 2345 | const dylib_id = dylib.id orelse unreachable; | |
| 2346 | var dylib_cmd = try commands.createLoadDylibCommand( | |
| 2347 | self.base.allocator, | |
| 2348 | dylib_id.name, | |
| 2349 | dylib_id.timestamp, | |
| 2350 | dylib_id.current_version, | |
| 2351 | dylib_id.compatibility_version, | |
| 2352 | ); | |
| 2353 | errdefer dylib_cmd.deinit(self.base.allocator); | |
| 2354 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | |
| 2355 | self.next_dylib_ordinal += 1; | |
| 2356 | try referenced.putNoClobber(dylib, {}); | |
| 2357 | } | |
| 2358 | ||
| 2359 | const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable; | |
| 2360 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2361 | const import_sym_index = @intCast(u32, self.imports.items.len); | |
| 2362 | try self.imports.append(self.base.allocator, .{ | |
| 2363 | .n_strx = undef.n_strx, | |
| 2364 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 2365 | .n_sect = 0, | |
| 2366 | .n_desc = packDylibOrdinal(dylib.ordinal.?), | |
| 2367 | .n_value = 0, | |
| 2368 | }); | |
| 2369 | resolv.* = .{ | |
| 2370 | .where = .import, | |
| 2371 | .where_index = import_sym_index, | |
| 2372 | }; | |
| 2373 | undef.* = .{ | |
| 2374 | .n_strx = 0, | |
| 2375 | .n_type = macho.N_UNDF, | |
| 2376 | .n_sect = 0, | |
| 2377 | .n_desc = 0, | |
| 2378 | .n_value = 0, | |
| 2379 | }; | |
| 2380 | ||
| 2381 | continue :loop; | |
| 2382 | } | |
| 2383 | } | |
| 2384 | ||
| 2385 | // Fourth pass, handle synthetic symbols and flag any undefined references. | |
| 2386 | if (self.strtab_dir.getAdapted(@as([]const u8, "___dso_handle"), StringSliceAdapter{ | |
| 2387 | .strtab = &self.strtab, | |
| 2388 | })) |n_strx| blk: { | |
| 2389 | const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk; | |
| 2390 | if (resolv.where != .undef) break :blk; | |
| 2391 | ||
| 2392 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2393 | const match: MatchingSection = .{ | |
| 2394 | .seg = self.text_segment_cmd_index.?, | |
| 2395 | .sect = self.text_section_index.?, | |
| 2396 | }; | |
| 2397 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 2398 | var nlist = macho.nlist_64{ | |
| 2399 | .n_strx = undef.n_strx, | |
| 2400 | .n_type = macho.N_SECT, | |
| 2401 | .n_sect = self.section_to_ordinal.get(match) orelse unreachable, | |
| 2402 | .n_desc = 0, | |
| 2403 | .n_value = 0, | |
| 2404 | }; | |
| 2405 | try self.locals.append(self.base.allocator, nlist); | |
| 2406 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2407 | nlist.n_type |= macho.N_EXT; | |
| 2408 | nlist.n_desc = macho.N_WEAK_DEF; | |
| 2409 | try self.globals.append(self.base.allocator, nlist); | |
| 2410 | ||
| 2411 | undef.* = .{ | |
| 2412 | .n_strx = 0, | |
| 2413 | .n_type = macho.N_UNDF, | |
| 2414 | .n_sect = 0, | |
| 2415 | .n_desc = 0, | |
| 2416 | .n_value = 0, | |
| 2417 | }; | |
| 2418 | resolv.* = .{ | |
| 2419 | .where = .global, | |
| 2420 | .where_index = global_sym_index, | |
| 2421 | .local_sym_index = local_sym_index, | |
| 2422 | }; | |
| 2423 | ||
| 2424 | // We create an empty atom for this symbol. | |
| 2425 | // TODO perhaps we should special-case special symbols? Create a separate | |
| 2426 | // linked list of atoms? | |
| 2427 | const block = try self.base.allocator.create(TextBlock); | |
| 2428 | block.* = TextBlock.empty; | |
| 2429 | block.local_sym_index = local_sym_index; | |
| 2430 | block.size = 0; | |
| 2431 | block.alignment = 0; | |
| 2432 | try self.managed_blocks.append(self.base.allocator, block); | |
| 2433 | ||
| 2434 | if (self.blocks.getPtr(match)) |last| { | |
| 2435 | last.*.next = block; | |
| 2436 | block.prev = last.*; | |
| 2437 | last.* = block; | |
| 2438 | } else { | |
| 2439 | try self.blocks.putNoClobber(self.base.allocator, match, block); | |
| 2440 | } | |
| 2441 | } | |
| 2442 | ||
| 2443 | var has_undefined = false; | |
| 2444 | for (self.undefs.items) |sym| { | |
| 2445 | if (symbolIsNull(sym)) continue; | |
| 2446 | ||
| 2447 | const sym_name = self.getString(sym.n_strx); | |
| 2448 | const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable; | |
| 2449 | ||
| 2450 | log.err("undefined reference to symbol '{s}'", .{sym_name}); | |
| 2451 | log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 2452 | has_undefined = true; | |
| 2453 | } | |
| 2454 | ||
| 2455 | if (has_undefined) return error.UndefinedSymbolReference; | |
| 2456 | } | |
| 2457 | ||
| 2458 | fn parseTextBlocks(self: *MachO) !void { | |
| 2459 | for (self.objects.items) |object| { | |
| 2460 | try object.parseTextBlocks(self); | |
| 2461 | } | |
| 2462 | } | |
| 2463 | ||
| 2464 | fn populateMetadata(self: *MachO) !void { | |
| 2465 | if (self.pagezero_segment_cmd_index == null) { | |
| 2466 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2467 | try self.load_commands.append(self.base.allocator, .{ | |
| 2468 | .Segment = SegmentCommand.empty("__PAGEZERO", .{ | |
| 2469 | .vmsize = 0x100000000, // size always set to 4GB | |
| 2470 | }), | |
| 2471 | }); | |
| 2472 | } | |
| 2473 | ||
| 2474 | if (self.text_segment_cmd_index == null) { | |
| 2475 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2476 | try self.load_commands.append(self.base.allocator, .{ | |
| 2477 | .Segment = SegmentCommand.empty("__TEXT", .{ | |
| 2478 | .vmaddr = 0x100000000, // always starts at 4GB | |
| 2479 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 2480 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 2481 | }), | |
| 2482 | }); | |
| 2483 | } | |
| 2484 | ||
| 2485 | if (self.text_section_index == null) { | |
| 2486 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2487 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2488 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2489 | .x86_64 => 0, | |
| 2490 | .aarch64 => 2, | |
| 2491 | else => unreachable, // unhandled architecture type | |
| 2492 | }; | |
| 2493 | try text_seg.addSection(self.base.allocator, "__text", .{ | |
| 2494 | .@"align" = alignment, | |
| 2495 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2496 | }); | |
| 2497 | try self.createSectionOrdinal(.{ | |
| 2498 | .seg = self.text_segment_cmd_index.?, | |
| 2499 | .sect = self.text_section_index.?, | |
| 2500 | }); | |
| 2501 | } | |
| 2502 | ||
| 2503 | if (self.stubs_section_index == null) { | |
| 2504 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2505 | self.stubs_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2506 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2507 | .x86_64 => 0, | |
| 2508 | .aarch64 => 2, | |
| 2509 | else => unreachable, // unhandled architecture type | |
| 2510 | }; | |
| 2511 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { | |
| 2512 | .x86_64 => 6, | |
| 2513 | .aarch64 => 3 * @sizeOf(u32), | |
| 2514 | else => unreachable, // unhandled architecture type | |
| 2515 | }; | |
| 2516 | try text_seg.addSection(self.base.allocator, "__stubs", .{ | |
| 2517 | .@"align" = alignment, | |
| 2518 | .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2519 | .reserved2 = stub_size, | |
| 2520 | }); | |
| 2521 | try self.createSectionOrdinal(.{ | |
| 2522 | .seg = self.text_segment_cmd_index.?, | |
| 2523 | .sect = self.stubs_section_index.?, | |
| 2524 | }); | |
| 2525 | } | |
| 2526 | ||
| 2527 | if (self.stub_helper_section_index == null) { | |
| 2528 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2529 | self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2530 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2531 | .x86_64 => 0, | |
| 2532 | .aarch64 => 2, | |
| 2533 | else => unreachable, // unhandled architecture type | |
| 2534 | }; | |
| 2535 | const stub_helper_size: u6 = switch (self.base.options.target.cpu.arch) { | |
| 2536 | .x86_64 => 15, | |
| 2537 | .aarch64 => 6 * @sizeOf(u32), | |
| 2538 | else => unreachable, | |
| 2539 | }; | |
| 2540 | try text_seg.addSection(self.base.allocator, "__stub_helper", .{ | |
| 2541 | .size = stub_helper_size, | |
| 2542 | .@"align" = alignment, | |
| 2543 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2544 | }); | |
| 2545 | try self.createSectionOrdinal(.{ | |
| 2546 | .seg = self.text_segment_cmd_index.?, | |
| 2547 | .sect = self.stub_helper_section_index.?, | |
| 2548 | }); | |
| 2549 | } | |
| 2550 | ||
| 2551 | if (self.data_const_segment_cmd_index == null) { | |
| 2552 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2553 | try self.load_commands.append(self.base.allocator, .{ | |
| 2554 | .Segment = SegmentCommand.empty("__DATA_CONST", .{ | |
| 2555 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2556 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2557 | }), | |
| 2558 | }); | |
| 2559 | } | |
| 2560 | ||
| 2561 | if (self.got_section_index == null) { | |
| 2562 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2563 | self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 2564 | try data_const_seg.addSection(self.base.allocator, "__got", .{ | |
| 2565 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2566 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, | |
| 2567 | }); | |
| 2568 | try self.createSectionOrdinal(.{ | |
| 2569 | .seg = self.data_const_segment_cmd_index.?, | |
| 2570 | .sect = self.got_section_index.?, | |
| 2571 | }); | |
| 2572 | } | |
| 2573 | ||
| 2574 | if (self.data_segment_cmd_index == null) { | |
| 2575 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2576 | try self.load_commands.append(self.base.allocator, .{ | |
| 2577 | .Segment = SegmentCommand.empty("__DATA", .{ | |
| 2578 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2579 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2580 | }), | |
| 2581 | }); | |
| 2582 | } | |
| 2583 | ||
| 2584 | if (self.la_symbol_ptr_section_index == null) { | |
| 2585 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2586 | self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2587 | try data_seg.addSection(self.base.allocator, "__la_symbol_ptr", .{ | |
| 2588 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2589 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | |
| 2590 | }); | |
| 2591 | try self.createSectionOrdinal(.{ | |
| 2592 | .seg = self.data_segment_cmd_index.?, | |
| 2593 | .sect = self.la_symbol_ptr_section_index.?, | |
| 2594 | }); | |
| 2595 | } | |
| 2596 | ||
| 2597 | if (self.data_section_index == null) { | |
| 2598 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2599 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2600 | try data_seg.addSection(self.base.allocator, "__data", .{ | |
| 2601 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2602 | }); | |
| 2603 | try self.createSectionOrdinal(.{ | |
| 2604 | .seg = self.data_segment_cmd_index.?, | |
| 2605 | .sect = self.data_section_index.?, | |
| 2606 | }); | |
| 2607 | } | |
| 2608 | ||
| 2609 | if (self.linkedit_segment_cmd_index == null) { | |
| 2610 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2611 | try self.load_commands.append(self.base.allocator, .{ | |
| 2612 | .Segment = SegmentCommand.empty("__LINKEDIT", .{ | |
| 2613 | .maxprot = macho.VM_PROT_READ, | |
| 2614 | .initprot = macho.VM_PROT_READ, | |
| 2615 | }), | |
| 2616 | }); | |
| 2617 | } | |
| 2618 | ||
| 2619 | if (self.dyld_info_cmd_index == null) { | |
| 2620 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2621 | try self.load_commands.append(self.base.allocator, .{ | |
| 2622 | .DyldInfoOnly = .{ | |
| 2623 | .cmd = macho.LC_DYLD_INFO_ONLY, | |
| 2624 | .cmdsize = @sizeOf(macho.dyld_info_command), | |
| 2625 | .rebase_off = 0, | |
| 2626 | .rebase_size = 0, | |
| 2627 | .bind_off = 0, | |
| 2628 | .bind_size = 0, | |
| 2629 | .weak_bind_off = 0, | |
| 2630 | .weak_bind_size = 0, | |
| 2631 | .lazy_bind_off = 0, | |
| 2632 | .lazy_bind_size = 0, | |
| 2633 | .export_off = 0, | |
| 2634 | .export_size = 0, | |
| 2635 | }, | |
| 2636 | }); | |
| 2637 | } | |
| 2638 | ||
| 2639 | if (self.symtab_cmd_index == null) { | |
| 2640 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2641 | try self.load_commands.append(self.base.allocator, .{ | |
| 2642 | .Symtab = .{ | |
| 2643 | .cmd = macho.LC_SYMTAB, | |
| 2644 | .cmdsize = @sizeOf(macho.symtab_command), | |
| 2645 | .symoff = 0, | |
| 2646 | .nsyms = 0, | |
| 2647 | .stroff = 0, | |
| 2648 | .strsize = 0, | |
| 2649 | }, | |
| 2650 | }); | |
| 2651 | } | |
| 2652 | ||
| 2653 | if (self.dysymtab_cmd_index == null) { | |
| 2654 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2655 | try self.load_commands.append(self.base.allocator, .{ | |
| 2656 | .Dysymtab = .{ | |
| 2657 | .cmd = macho.LC_DYSYMTAB, | |
| 2658 | .cmdsize = @sizeOf(macho.dysymtab_command), | |
| 2659 | .ilocalsym = 0, | |
| 2660 | .nlocalsym = 0, | |
| 2661 | .iextdefsym = 0, | |
| 2662 | .nextdefsym = 0, | |
| 2663 | .iundefsym = 0, | |
| 2664 | .nundefsym = 0, | |
| 2665 | .tocoff = 0, | |
| 2666 | .ntoc = 0, | |
| 2667 | .modtaboff = 0, | |
| 2668 | .nmodtab = 0, | |
| 2669 | .extrefsymoff = 0, | |
| 2670 | .nextrefsyms = 0, | |
| 2671 | .indirectsymoff = 0, | |
| 2672 | .nindirectsyms = 0, | |
| 2673 | .extreloff = 0, | |
| 2674 | .nextrel = 0, | |
| 2675 | .locreloff = 0, | |
| 2676 | .nlocrel = 0, | |
| 2677 | }, | |
| 2678 | }); | |
| 2679 | } | |
| 2680 | ||
| 2681 | if (self.dylinker_cmd_index == null) { | |
| 2682 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2683 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2684 | u64, | |
| 2685 | @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), | |
| 2686 | @sizeOf(u64), | |
| 2687 | )); | |
| 2688 | var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{ | |
| 2689 | .cmd = macho.LC_LOAD_DYLINKER, | |
| 2690 | .cmdsize = cmdsize, | |
| 2691 | .name = @sizeOf(macho.dylinker_command), | |
| 2692 | }); | |
| 2693 | dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); | |
| 2694 | mem.set(u8, dylinker_cmd.data, 0); | |
| 2695 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); | |
| 2696 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); | |
| 2697 | } | |
| 2698 | ||
| 2699 | if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) { | |
| 2700 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2701 | try self.load_commands.append(self.base.allocator, .{ | |
| 2702 | .Main = .{ | |
| 2703 | .cmd = macho.LC_MAIN, | |
| 2704 | .cmdsize = @sizeOf(macho.entry_point_command), | |
| 2705 | .entryoff = 0x0, | |
| 2706 | .stacksize = 0, | |
| 2707 | }, | |
| 2708 | }); | |
| 2709 | } | |
| 2710 | ||
| 2711 | if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) { | |
| 2712 | self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2713 | const install_name = try std.fmt.allocPrint(self.base.allocator, "@rpath/{s}", .{ | |
| 2714 | self.base.options.emit.?.sub_path, | |
| 2715 | }); | |
| 2716 | defer self.base.allocator.free(install_name); | |
| 2717 | var dylib_cmd = try commands.createLoadDylibCommand( | |
| 2718 | self.base.allocator, | |
| 2719 | install_name, | |
| 2720 | 2, | |
| 2721 | 0x10000, // TODO forward user-provided versions | |
| 2722 | 0x10000, | |
| 2723 | ); | |
| 2724 | errdefer dylib_cmd.deinit(self.base.allocator); | |
| 2725 | dylib_cmd.inner.cmd = macho.LC_ID_DYLIB; | |
| 2726 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | |
| 2727 | } | |
| 2728 | ||
| 2729 | if (self.version_min_cmd_index == null) { | |
| 2730 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2731 | const cmd: u32 = switch (self.base.options.target.os.tag) { | |
| 2732 | .macos => macho.LC_VERSION_MIN_MACOSX, | |
| 2733 | .ios => macho.LC_VERSION_MIN_IPHONEOS, | |
| 2734 | .tvos => macho.LC_VERSION_MIN_TVOS, | |
| 2735 | .watchos => macho.LC_VERSION_MIN_WATCHOS, | |
| 2736 | else => unreachable, // wrong OS | |
| 2737 | }; | |
| 2738 | const ver = self.base.options.target.os.version_range.semver.min; | |
| 2739 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; | |
| 2740 | try self.load_commands.append(self.base.allocator, .{ | |
| 2741 | .VersionMin = .{ | |
| 2742 | .cmd = cmd, | |
| 2743 | .cmdsize = @sizeOf(macho.version_min_command), | |
| 2744 | .version = version, | |
| 2745 | .sdk = version, | |
| 2746 | }, | |
| 2747 | }); | |
| 2748 | } | |
| 2749 | ||
| 2750 | if (self.source_version_cmd_index == null) { | |
| 2751 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2752 | try self.load_commands.append(self.base.allocator, .{ | |
| 2753 | .SourceVersion = .{ | |
| 2754 | .cmd = macho.LC_SOURCE_VERSION, | |
| 2755 | .cmdsize = @sizeOf(macho.source_version_command), | |
| 2756 | .version = 0x0, | |
| 2757 | }, | |
| 2758 | }); | |
| 2759 | } | |
| 2760 | ||
| 2761 | if (self.uuid_cmd_index == null) { | |
| 2762 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2763 | var uuid_cmd: macho.uuid_command = .{ | |
| 2764 | .cmd = macho.LC_UUID, | |
| 2765 | .cmdsize = @sizeOf(macho.uuid_command), | |
| 2766 | .uuid = undefined, | |
| 2767 | }; | |
| 2768 | std.crypto.random.bytes(&uuid_cmd.uuid); | |
| 2769 | try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); | |
| 2770 | } | |
| 2771 | } | |
| 2772 | ||
| 2773 | fn addDataInCodeLC(self: *MachO) !void { | |
| 2774 | if (self.data_in_code_cmd_index == null) { | |
| 2775 | self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2776 | try self.load_commands.append(self.base.allocator, .{ | |
| 2777 | .LinkeditData = .{ | |
| 2778 | .cmd = macho.LC_DATA_IN_CODE, | |
| 2779 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2780 | .dataoff = 0, | |
| 2781 | .datasize = 0, | |
| 2782 | }, | |
| 2783 | }); | |
| 2784 | } | |
| 2785 | } | |
| 2786 | ||
| 2787 | fn addCodeSignatureLC(self: *MachO) !void { | |
| 2788 | if (self.code_signature_cmd_index == null and self.base.options.target.cpu.arch == .aarch64) { | |
| 2789 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2790 | try self.load_commands.append(self.base.allocator, .{ | |
| 2791 | .LinkeditData = .{ | |
| 2792 | .cmd = macho.LC_CODE_SIGNATURE, | |
| 2793 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2794 | .dataoff = 0, | |
| 2795 | .datasize = 0, | |
| 2796 | }, | |
| 2797 | }); | |
| 2798 | } | |
| 2799 | } | |
| 2800 | ||
| 2801 | fn addRpaths(self: *MachO, rpaths: []const []const u8) !void { | |
| 2802 | for (rpaths) |rpath| { | |
| 2803 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2804 | u64, | |
| 2805 | @sizeOf(macho.rpath_command) + rpath.len + 1, | |
| 2806 | @sizeOf(u64), | |
| 2807 | )); | |
| 2808 | var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{ | |
| 2809 | .cmd = macho.LC_RPATH, | |
| 2810 | .cmdsize = cmdsize, | |
| 2811 | .path = @sizeOf(macho.rpath_command), | |
| 2812 | }); | |
| 2813 | rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); | |
| 2814 | mem.set(u8, rpath_cmd.data, 0); | |
| 2815 | mem.copy(u8, rpath_cmd.data, rpath); | |
| 2816 | try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd }); | |
| 2817 | } | |
| 2818 | } | |
| 2819 | ||
| 2820 | fn flushZld(self: *MachO) !void { | |
| 2821 | self.load_commands_dirty = true; | |
| 2822 | try self.writeTextBlocks(); | |
| 2823 | try self.writeStubHelperCommon(); | |
| 2824 | ||
| 2825 | if (self.common_section_index) |index| { | |
| 2826 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2827 | const sect = &seg.sections.items[index]; | |
| 2828 | sect.offset = 0; | |
| 2829 | } | |
| 2830 | ||
| 2831 | if (self.bss_section_index) |index| { | |
| 2832 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2833 | const sect = &seg.sections.items[index]; | |
| 2834 | sect.offset = 0; | |
| 2835 | } | |
| 2836 | ||
| 2837 | if (self.tlv_bss_section_index) |index| { | |
| 2838 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2839 | const sect = &seg.sections.items[index]; | |
| 2840 | sect.offset = 0; | |
| 2841 | } | |
| 2842 | ||
| 2843 | try self.writeGotEntries(); | |
| 2844 | try self.setEntryPoint(); | |
| 2845 | try self.writeRebaseInfoTableZld(); | |
| 2846 | try self.writeBindInfoTableZld(); | |
| 2847 | try self.writeLazyBindInfoTableZld(); | |
| 2848 | try self.writeExportInfoZld(); | |
| 2849 | try self.writeDices(); | |
| 2850 | ||
| 2851 | { | |
| 2852 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2853 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2854 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2855 | } | |
| 2856 | ||
| 2857 | try self.writeSymbolTable(); | |
| 2858 | try self.writeStringTableZld(); | |
| 2859 | ||
| 2860 | { | |
| 2861 | // Seal __LINKEDIT size | |
| 2862 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2863 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); | |
| 2864 | } | |
| 2865 | ||
| 2866 | if (self.base.options.target.cpu.arch == .aarch64) { | |
| 2867 | try self.writeCodeSignaturePadding(); | |
| 2868 | } | |
| 2869 | ||
| 2870 | try self.writeLoadCommands(); | |
| 2871 | try self.writeHeader(); | |
| 2872 | ||
| 2873 | if (self.base.options.target.cpu.arch == .aarch64) { | |
| 2874 | try self.writeCodeSignature(); | |
| 2875 | } | |
| 2876 | ||
| 2877 | // if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { | |
| 2878 | // const out_path = self.output.?.path; | |
| 2879 | // try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); | |
| 2880 | // } | |
| 2881 | } | |
| 2882 | ||
| 2883 | fn writeGotEntries(self: *MachO) !void { | |
| 2884 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2885 | const sect = seg.sections.items[self.got_section_index.?]; | |
| 2886 | ||
| 2887 | var buffer = try self.base.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); | |
| 2888 | defer self.base.allocator.free(buffer); | |
| 2889 | ||
| 2890 | var stream = std.io.fixedBufferStream(buffer); | |
| 2891 | var writer = stream.writer(); | |
| 2892 | ||
| 2893 | for (self.got_entries.items) |key| { | |
| 2894 | const address: u64 = switch (key.where) { | |
| 2895 | .local => self.locals.items[key.where_index].n_value, | |
| 2896 | .import => 0, | |
| 2897 | }; | |
| 2898 | try writer.writeIntLittle(u64, address); | |
| 2899 | } | |
| 2900 | ||
| 2901 | log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); | |
| 2902 | ||
| 2903 | try self.base.file.?.pwriteAll(buffer, sect.offset); | |
| 2904 | } | |
| 2905 | ||
| 2906 | fn setEntryPoint(self: *MachO) !void { | |
| 2907 | if (self.base.options.output_mode != .Exe) return; | |
| 2908 | ||
| 2909 | // TODO we should respect the -entry flag passed in by the user to set a custom | |
| 2910 | // entrypoint. For now, assume default of `_main`. | |
| 2911 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2912 | const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "_main"), StringSliceAdapter{ | |
| 2913 | .strtab = &self.strtab, | |
| 2914 | }) orelse { | |
| 2915 | log.err("'_main' export not found", .{}); | |
| 2916 | return error.MissingMainEntrypoint; | |
| 2917 | }; | |
| 2918 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 2919 | assert(resolv.where == .global); | |
| 2920 | const sym = self.globals.items[resolv.where_index]; | |
| 2921 | const ec = &self.load_commands.items[self.main_cmd_index.?].Main; | |
| 2922 | ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr); | |
| 2923 | ec.stacksize = self.base.options.stack_size_override orelse 0; | |
| 2924 | } | |
| 2925 | ||
| 2926 | fn writeRebaseInfoTableZld(self: *MachO) !void { | |
| 2927 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 2928 | defer pointers.deinit(); | |
| 2929 | ||
| 2930 | { | |
| 2931 | var it = self.blocks.iterator(); | |
| 2932 | while (it.next()) |entry| { | |
| 2933 | const match = entry.key_ptr.*; | |
| 2934 | var block: *TextBlock = entry.value_ptr.*; | |
| 2935 | ||
| 2936 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 2937 | ||
| 2938 | const seg = self.load_commands.items[match.seg].Segment; | |
| 2939 | ||
| 2940 | while (true) { | |
| 2941 | const sym = self.locals.items[block.local_sym_index]; | |
| 2942 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 2943 | ||
| 2944 | for (block.rebases.items) |offset| { | |
| 2945 | try pointers.append(.{ | |
| 2946 | .offset = base_offset + offset, | |
| 2947 | .segment_id = match.seg, | |
| 2948 | }); | |
| 2949 | } | |
| 2950 | ||
| 2951 | if (block.prev) |prev| { | |
| 2952 | block = prev; | |
| 2953 | } else break; | |
| 2954 | } | |
| 2955 | } | |
| 2956 | } | |
| 2957 | ||
| 2958 | if (self.got_section_index) |idx| { | |
| 2959 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2960 | const sect = seg.sections.items[idx]; | |
| 2961 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2962 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2963 | ||
| 2964 | for (self.got_entries.items) |entry, i| { | |
| 2965 | if (entry.where == .import) continue; | |
| 2966 | ||
| 2967 | try pointers.append(.{ | |
| 2968 | .offset = base_offset + i * @sizeOf(u64), | |
| 2969 | .segment_id = segment_id, | |
| 2970 | }); | |
| 2971 | } | |
| 2972 | } | |
| 2973 | ||
| 2974 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2975 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2976 | const sect = seg.sections.items[idx]; | |
| 2977 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2978 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2979 | ||
| 2980 | try pointers.ensureUnusedCapacity(self.stubs.items.len); | |
| 2981 | for (self.stubs.items) |_, i| { | |
| 2982 | pointers.appendAssumeCapacity(.{ | |
| 2983 | .offset = base_offset + i * @sizeOf(u64), | |
| 2984 | .segment_id = segment_id, | |
| 2985 | }); | |
| 2986 | } | |
| 2987 | } | |
| 2988 | ||
| 2989 | std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp); | |
| 2990 | ||
| 2991 | const size = try bind.rebaseInfoSize(pointers.items); | |
| 2992 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); | |
| 2993 | defer self.base.allocator.free(buffer); | |
| 2994 | ||
| 2995 | var stream = std.io.fixedBufferStream(buffer); | |
| 2996 | try bind.writeRebaseInfo(pointers.items, stream.writer()); | |
| 2997 | ||
| 2998 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2999 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 3000 | dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); | |
| 3001 | dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64))); | |
| 3002 | seg.inner.filesize += dyld_info.rebase_size; | |
| 3003 | ||
| 3004 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); | |
| 3005 | ||
| 3006 | try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); | |
| 3007 | } | |
| 3008 | ||
| 3009 | fn writeBindInfoTableZld(self: *MachO) !void { | |
| 3010 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 3011 | defer pointers.deinit(); | |
| 3012 | ||
| 3013 | if (self.got_section_index) |idx| { | |
| 3014 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 3015 | const sect = seg.sections.items[idx]; | |
| 3016 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 3017 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 3018 | ||
| 3019 | for (self.got_entries.items) |entry, i| { | |
| 3020 | if (entry.where == .local) continue; | |
| 3021 | ||
| 3022 | const sym = self.imports.items[entry.where_index]; | |
| 3023 | try pointers.append(.{ | |
| 3024 | .offset = base_offset + i * @sizeOf(u64), | |
| 3025 | .segment_id = segment_id, | |
| 3026 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 3027 | .name = self.getString(sym.n_strx), | |
| 3028 | }); | |
| 3029 | } | |
| 3030 | } | |
| 3031 | ||
| 3032 | { | |
| 3033 | var it = self.blocks.iterator(); | |
| 3034 | while (it.next()) |entry| { | |
| 3035 | const match = entry.key_ptr.*; | |
| 3036 | var block: *TextBlock = entry.value_ptr.*; | |
| 3037 | ||
| 3038 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 3039 | ||
| 3040 | const seg = self.load_commands.items[match.seg].Segment; | |
| 3041 | ||
| 3042 | while (true) { | |
| 3043 | const sym = self.locals.items[block.local_sym_index]; | |
| 3044 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 3045 | ||
| 3046 | for (block.bindings.items) |binding| { | |
| 3047 | const bind_sym = self.imports.items[binding.local_sym_index]; | |
| 3048 | try pointers.append(.{ | |
| 3049 | .offset = binding.offset + base_offset, | |
| 3050 | .segment_id = match.seg, | |
| 3051 | .dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc), | |
| 3052 | .name = self.getString(bind_sym.n_strx), | |
| 3053 | }); | |
| 3054 | } | |
| 3055 | ||
| 3056 | if (block.prev) |prev| { | |
| 3057 | block = prev; | |
| 3058 | } else break; | |
| 3059 | } | |
| 3060 | } | |
| 3061 | } | |
| 3062 | ||
| 3063 | const size = try bind.bindInfoSize(pointers.items); | |
| 3064 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); | |
| 3065 | defer self.base.allocator.free(buffer); | |
| 3066 | ||
| 3067 | var stream = std.io.fixedBufferStream(buffer); | |
| 3068 | try bind.writeBindInfo(pointers.items, stream.writer()); | |
| 3069 | ||
| 3070 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3071 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 3072 | dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 3073 | dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 3074 | seg.inner.filesize += dyld_info.bind_size; | |
| 3075 | ||
| 3076 | log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); | |
| 3077 | ||
| 3078 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); | |
| 3079 | } | |
| 3080 | ||
| 3081 | fn writeLazyBindInfoTableZld(self: *MachO) !void { | |
| 3082 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 3083 | defer pointers.deinit(); | |
| 3084 | ||
| 3085 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 3086 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 3087 | const sect = seg.sections.items[idx]; | |
| 3088 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 3089 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 3090 | ||
| 3091 | try pointers.ensureUnusedCapacity(self.stubs.items.len); | |
| 3092 | ||
| 3093 | for (self.stubs.items) |import_id, i| { | |
| 3094 | const sym = self.imports.items[import_id]; | |
| 3095 | pointers.appendAssumeCapacity(.{ | |
| 3096 | .offset = base_offset + i * @sizeOf(u64), | |
| 3097 | .segment_id = segment_id, | |
| 3098 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 3099 | .name = self.getString(sym.n_strx), | |
| 3100 | }); | |
| 3101 | } | |
| 3102 | } | |
| 3103 | ||
| 3104 | const size = try bind.lazyBindInfoSize(pointers.items); | |
| 3105 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); | |
| 3106 | defer self.base.allocator.free(buffer); | |
| 3107 | ||
| 3108 | var stream = std.io.fixedBufferStream(buffer); | |
| 3109 | try bind.writeLazyBindInfo(pointers.items, stream.writer()); | |
| 3110 | ||
| 3111 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3112 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 3113 | dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 3114 | dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 3115 | seg.inner.filesize += dyld_info.lazy_bind_size; | |
| 3116 | ||
| 3117 | log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); | |
| 3118 | ||
| 3119 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | |
| 3120 | try self.populateLazyBindOffsetsInStubHelper(buffer); | |
| 3121 | } | |
| 3122 | ||
| 3123 | fn writeExportInfoZld(self: *MachO) !void { | |
| 3124 | var trie = Trie.init(self.base.allocator); | |
| 3125 | defer trie.deinit(); | |
| 3126 | ||
| 3127 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 3128 | const base_address = text_segment.inner.vmaddr; | |
| 3129 | ||
| 3130 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. | |
| 3131 | log.debug("writing export trie", .{}); | |
| 3132 | ||
| 3133 | for (self.globals.items) |sym| { | |
| 3134 | const sym_name = self.getString(sym.n_strx); | |
| 3135 | log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value }); | |
| 3136 | ||
| 3137 | try trie.put(.{ | |
| 3138 | .name = sym_name, | |
| 3139 | .vmaddr_offset = sym.n_value - base_address, | |
| 3140 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, | |
| 3141 | }); | |
| 3142 | } | |
| 3143 | ||
| 3144 | try trie.finalize(); | |
| 3145 | ||
| 3146 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size)); | |
| 3147 | defer self.base.allocator.free(buffer); | |
| 3148 | ||
| 3149 | var stream = std.io.fixedBufferStream(buffer); | |
| 3150 | const nwritten = try trie.write(stream.writer()); | |
| 3151 | assert(nwritten == trie.size); | |
| 3152 | ||
| 3153 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3154 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 3155 | dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 3156 | dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 3157 | seg.inner.filesize += dyld_info.export_size; | |
| 3158 | ||
| 3159 | log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); | |
| 3160 | ||
| 3161 | try self.base.file.?.pwriteAll(buffer, dyld_info.export_off); | |
| 3162 | } | |
| 3163 | ||
| 3164 | fn writeSymbolTable(self: *MachO) !void { | |
| 3165 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3166 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 3167 | ||
| 3168 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); | |
| 3169 | defer locals.deinit(); | |
| 3170 | try locals.appendSlice(self.locals.items); | |
| 3171 | ||
| 3172 | if (self.has_stabs) { | |
| 3173 | for (self.objects.items) |object| { | |
| 3174 | if (object.debug_info == null) continue; | |
| 3175 | ||
| 3176 | // Open scope | |
| 3177 | try locals.ensureUnusedCapacity(3); | |
| 3178 | locals.appendAssumeCapacity(.{ | |
| 3179 | .n_strx = try self.makeString(object.tu_comp_dir.?), | |
| 3180 | .n_type = macho.N_SO, | |
| 3181 | .n_sect = 0, | |
| 3182 | .n_desc = 0, | |
| 3183 | .n_value = 0, | |
| 3184 | }); | |
| 3185 | locals.appendAssumeCapacity(.{ | |
| 3186 | .n_strx = try self.makeString(object.tu_name.?), | |
| 3187 | .n_type = macho.N_SO, | |
| 3188 | .n_sect = 0, | |
| 3189 | .n_desc = 0, | |
| 3190 | .n_value = 0, | |
| 3191 | }); | |
| 3192 | locals.appendAssumeCapacity(.{ | |
| 3193 | .n_strx = try self.makeString(object.name.?), | |
| 3194 | .n_type = macho.N_OSO, | |
| 3195 | .n_sect = 0, | |
| 3196 | .n_desc = 1, | |
| 3197 | .n_value = object.mtime orelse 0, | |
| 3198 | }); | |
| 3199 | ||
| 3200 | for (object.text_blocks.items) |block| { | |
| 3201 | if (block.stab) |stab| { | |
| 3202 | const nlists = try stab.asNlists(block.local_sym_index, self); | |
| 3203 | defer self.base.allocator.free(nlists); | |
| 3204 | try locals.appendSlice(nlists); | |
| 3205 | } else { | |
| 3206 | for (block.contained.items) |sym_at_off| { | |
| 3207 | const stab = sym_at_off.stab orelse continue; | |
| 3208 | const nlists = try stab.asNlists(sym_at_off.local_sym_index, self); | |
| 3209 | defer self.base.allocator.free(nlists); | |
| 3210 | try locals.appendSlice(nlists); | |
| 3211 | } | |
| 3212 | } | |
| 3213 | } | |
| 3214 | ||
| 3215 | // Close scope | |
| 3216 | try locals.append(.{ | |
| 3217 | .n_strx = 0, | |
| 3218 | .n_type = macho.N_SO, | |
| 3219 | .n_sect = 0, | |
| 3220 | .n_desc = 0, | |
| 3221 | .n_value = 0, | |
| 3222 | }); | |
| 3223 | } | |
| 3224 | } | |
| 3225 | ||
| 3226 | const nlocals = locals.items.len; | |
| 3227 | const nexports = self.globals.items.len; | |
| 3228 | const nundefs = self.imports.items.len; | |
| 3229 | ||
| 3230 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | |
| 3231 | const locals_size = nlocals * @sizeOf(macho.nlist_64); | |
| 3232 | log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); | |
| 3233 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); | |
| 3234 | ||
| 3235 | const exports_off = locals_off + locals_size; | |
| 3236 | const exports_size = nexports * @sizeOf(macho.nlist_64); | |
| 3237 | log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); | |
| 3238 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off); | |
| 3239 | ||
| 3240 | const undefs_off = exports_off + exports_size; | |
| 3241 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); | |
| 3242 | log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); | |
| 3243 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off); | |
| 3244 | ||
| 3245 | symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs); | |
| 3246 | seg.inner.filesize += locals_size + exports_size + undefs_size; | |
| 3247 | ||
| 3248 | // Update dynamic symbol table. | |
| 3249 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | |
| 3250 | dysymtab.nlocalsym += @intCast(u32, nlocals); | |
| 3251 | dysymtab.iextdefsym = dysymtab.nlocalsym; | |
| 3252 | dysymtab.nextdefsym = @intCast(u32, nexports); | |
| 3253 | dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; | |
| 3254 | dysymtab.nundefsym = @intCast(u32, nundefs); | |
| 3255 | ||
| 3256 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 3257 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; | |
| 3258 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 3259 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 3260 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 3261 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 3262 | ||
| 3263 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 3264 | const ngot_entries = @intCast(u32, self.got_entries.items.len); | |
| 3265 | ||
| 3266 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 3267 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; | |
| 3268 | ||
| 3269 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | |
| 3270 | seg.inner.filesize += needed_size; | |
| 3271 | ||
| 3272 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ | |
| 3273 | dysymtab.indirectsymoff, | |
| 3274 | dysymtab.indirectsymoff + needed_size, | |
| 3275 | }); | |
| 3276 | ||
| 3277 | var buf = try self.base.allocator.alloc(u8, needed_size); | |
| 3278 | defer self.base.allocator.free(buf); | |
| 3279 | ||
| 3280 | var stream = std.io.fixedBufferStream(buf); | |
| 3281 | var writer = stream.writer(); | |
| 3282 | ||
| 3283 | stubs.reserved1 = 0; | |
| 3284 | for (self.stubs.items) |id| { | |
| 3285 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 3286 | } | |
| 3287 | ||
| 3288 | got.reserved1 = nstubs; | |
| 3289 | for (self.got_entries.items) |entry| { | |
| 3290 | switch (entry.where) { | |
| 3291 | .import => { | |
| 3292 | try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index); | |
| 3293 | }, | |
| 3294 | .local => { | |
| 3295 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 3296 | }, | |
| 3297 | } | |
| 3298 | } | |
| 3299 | ||
| 3300 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; | |
| 3301 | for (self.stubs.items) |id| { | |
| 3302 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 3303 | } | |
| 3304 | ||
| 3305 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | |
| 3306 | } | |
| 3307 | ||
| 3308 | pub fn deinit(self: *MachO) void { | |
| 3309 | if (build_options.have_llvm) { | |
| 3310 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 3311 | } | |
| 3312 | ||
| 3313 | if (self.d_sym) |*ds| { | |
| 3314 | ds.deinit(self.base.allocator); | |
| 3315 | } | |
| 3316 | ||
| 3317 | self.section_ordinals.deinit(self.base.allocator); | |
| 3318 | self.section_to_ordinal.deinit(self.base.allocator); | |
| 3319 | self.pending_updates.deinit(self.base.allocator); | |
| 3320 | self.got_entries.deinit(self.base.allocator); | |
| 3321 | self.got_entries_map.deinit(self.base.allocator); | |
| 3322 | self.got_entries_free_list.deinit(self.base.allocator); | |
| 3323 | self.stubs.deinit(self.base.allocator); | |
| 3324 | self.stubs_map.deinit(self.base.allocator); | |
| 3325 | self.strtab_dir.deinit(self.base.allocator); | |
| 3326 | self.strtab.deinit(self.base.allocator); | |
| 3327 | self.undefs.deinit(self.base.allocator); | |
| 3328 | self.tentatives.deinit(self.base.allocator); | |
| 3329 | self.imports.deinit(self.base.allocator); | |
| 3330 | self.globals.deinit(self.base.allocator); | |
| 3331 | self.globals_free_list.deinit(self.base.allocator); | |
| 3332 | self.locals.deinit(self.base.allocator); | |
| 3333 | self.locals_free_list.deinit(self.base.allocator); | |
| 3334 | self.symbol_resolver.deinit(self.base.allocator); | |
| 3335 | ||
| 3336 | for (self.objects.items) |object| { | |
| 3337 | object.deinit(); | |
| 3338 | self.base.allocator.destroy(object); | |
| 3339 | } | |
| 3340 | self.objects.deinit(self.base.allocator); | |
| 3341 | ||
| 3342 | for (self.archives.items) |archive| { | |
| 3343 | archive.deinit(); | |
| 3344 | self.base.allocator.destroy(archive); | |
| 3345 | } | |
| 3346 | self.archives.deinit(self.base.allocator); | |
| 3347 | ||
| 3348 | for (self.dylibs.items) |dylib| { | |
| 3349 | dylib.deinit(); | |
| 3350 | self.base.allocator.destroy(dylib); | |
| 3351 | } | |
| 3352 | self.dylibs.deinit(self.base.allocator); | |
| 3353 | ||
| 3354 | for (self.load_commands.items) |*lc| { | |
| 3355 | lc.deinit(self.base.allocator); | |
| 3356 | } | |
| 3357 | self.load_commands.deinit(self.base.allocator); | |
| 3358 | ||
| 3359 | for (self.managed_blocks.items) |block| { | |
| 3360 | block.deinit(self.base.allocator); | |
| 3361 | self.base.allocator.destroy(block); | |
| 3362 | } | |
| 3363 | self.managed_blocks.deinit(self.base.allocator); | |
| 3364 | self.blocks.deinit(self.base.allocator); | |
| 3365 | self.text_block_free_list.deinit(self.base.allocator); | |
| 3366 | ||
| 3367 | for (self.decls.keys()) |decl| { | |
| 3368 | decl.link.macho.deinit(self.base.allocator); | |
| 3369 | } | |
| 3370 | self.decls.deinit(self.base.allocator); | |
| 3371 | } | |
| 3372 | ||
| 3373 | pub fn closeFiles(self: MachO) void { | |
| 3374 | for (self.objects.items) |object| { | |
| 3375 | object.closeFile(); | |
| 3376 | } | |
| 3377 | for (self.archives.items) |archive| { | |
| 3378 | archive.closeFile(); | |
| 3379 | } | |
| 3380 | } | |
| 3381 | ||
| 3382 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { | |
| 3383 | log.debug("freeTextBlock {*}", .{text_block}); | |
| 3384 | text_block.deinit(self.base.allocator); | |
| 3385 | ||
| 3386 | var already_have_free_list_node = false; | |
| 3387 | { | |
| 3388 | var i: usize = 0; | |
| 3389 | // TODO turn text_block_free_list into a hash map | |
| 3390 | while (i < self.text_block_free_list.items.len) { | |
| 3391 | if (self.text_block_free_list.items[i] == text_block) { | |
| 3392 | _ = self.text_block_free_list.swapRemove(i); | |
| 3393 | continue; | |
| 3394 | } | |
| 3395 | if (self.text_block_free_list.items[i] == text_block.prev) { | |
| 3396 | already_have_free_list_node = true; | |
| 3397 | } | |
| 3398 | i += 1; | |
| 3399 | } | |
| 3400 | } | |
| 3401 | // TODO process free list for dbg info just like we do above for vaddrs | |
| 3402 | ||
| 3403 | if (self.last_text_block == text_block) { | |
| 3404 | // TODO shrink the __text section size here | |
| 3405 | self.last_text_block = text_block.prev; | |
| 3406 | } | |
| 3407 | if (self.d_sym) |*ds| { | |
| 3408 | if (ds.dbg_info_decl_first == text_block) { | |
| 3409 | ds.dbg_info_decl_first = text_block.dbg_info_next; | |
| 3410 | } | |
| 3411 | if (ds.dbg_info_decl_last == text_block) { | |
| 3412 | // TODO shrink the .debug_info section size here | |
| 3413 | ds.dbg_info_decl_last = text_block.dbg_info_prev; | |
| 3414 | } | |
| 3415 | } | |
| 3416 | ||
| 3417 | if (text_block.prev) |prev| { | |
| 3418 | prev.next = text_block.next; | |
| 3419 | ||
| 3420 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { | |
| 3421 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore | |
| 3422 | // the OOM here. | |
| 3423 | self.text_block_free_list.append(self.base.allocator, prev) catch {}; | |
| 3424 | } | |
| 3425 | } else { | |
| 3426 | text_block.prev = null; | |
| 3427 | } | |
| 3428 | ||
| 3429 | if (text_block.next) |next| { | |
| 3430 | next.prev = text_block.prev; | |
| 3431 | } else { | |
| 3432 | text_block.next = null; | |
| 3433 | } | |
| 3434 | ||
| 3435 | if (text_block.dbg_info_prev) |prev| { | |
| 3436 | prev.dbg_info_next = text_block.dbg_info_next; | |
| 3437 | ||
| 3438 | // TODO the free list logic like we do for text blocks above | |
| 3439 | } else { | |
| 3440 | text_block.dbg_info_prev = null; | |
| 3441 | } | |
| 3442 | ||
| 3443 | if (text_block.dbg_info_next) |next| { | |
| 3444 | next.dbg_info_prev = text_block.dbg_info_prev; | |
| 3445 | } else { | |
| 3446 | text_block.dbg_info_next = null; | |
| 3447 | } | |
| 3448 | } | |
| 3449 | ||
| 3450 | fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void { | |
| 3451 | _ = self; | |
| 3452 | _ = text_block; | |
| 3453 | _ = new_block_size; | |
| 3454 | // TODO check the new capacity, and if it crosses the size threshold into a big enough | |
| 3455 | // capacity, insert a free list node for it. | |
| 3456 | } | |
| 3457 | ||
| 3458 | fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | |
| 3459 | const sym = self.locals.items[text_block.local_sym_index]; | |
| 3460 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; | |
| 3461 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); | |
| 3462 | if (!need_realloc) return sym.n_value; | |
| 3463 | return self.allocateTextBlock(text_block, new_block_size, alignment); | |
| 3464 | } | |
| 3465 | ||
| 3466 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | |
| 3467 | if (decl.link.macho.local_sym_index != 0) return; | |
| 3468 | ||
| 3469 | try self.locals.ensureUnusedCapacity(self.base.allocator, 1); | |
| 3470 | try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1); | |
| 3471 | ||
| 3472 | try self.decls.putNoClobber(self.base.allocator, decl, {}); | |
| 3473 | ||
| 3474 | if (self.locals_free_list.popOrNull()) |i| { | |
| 3475 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); | |
| 3476 | decl.link.macho.local_sym_index = i; | |
| 3477 | } else { | |
| 3478 | log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name }); | |
| 3479 | decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len); | |
| 3480 | _ = self.locals.addOneAssumeCapacity(); | |
| 3481 | } | |
| 3482 | ||
| 3483 | const got_index: u32 = blk: { | |
| 3484 | if (self.got_entries_free_list.popOrNull()) |i| { | |
| 3485 | log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name }); | |
| 3486 | break :blk i; | |
| 3487 | } else { | |
| 3488 | const got_index = @intCast(u32, self.got_entries.items.len); | |
| 3489 | log.debug("allocating GOT entry index {d} for {s}", .{ got_index, decl.name }); | |
| 3490 | _ = self.got_entries.addOneAssumeCapacity(); | |
| 3491 | self.got_entries_count_dirty = true; | |
| 3492 | self.rebase_info_dirty = true; | |
| 3493 | break :blk got_index; | |
| 3494 | } | |
| 3495 | }; | |
| 1135 | 3496 | |
| 1136 | 3497 | self.locals.items[decl.link.macho.local_sym_index] = .{ |
| 1137 | 3498 | .n_strx = 0, |
| ... | ... | @@ -1140,11 +3501,12 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1140 | 3501 | .n_desc = 0, |
| 1141 | 3502 | .n_value = 0, |
| 1142 | 3503 | }; |
| 1143 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1144 | .kind = .Local, | |
| 1145 | .symbol = decl.link.macho.local_sym_index, | |
| 1146 | .index = decl.link.macho.offset_table_index, | |
| 3504 | const got_entry = GotIndirectionKey{ | |
| 3505 | .where = .local, | |
| 3506 | .where_index = decl.link.macho.local_sym_index, | |
| 1147 | 3507 | }; |
| 3508 | self.got_entries.items[got_index] = got_entry; | |
| 3509 | try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); | |
| 1148 | 3510 | } |
| 1149 | 3511 | |
| 1150 | 3512 | pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| ... | ... | @@ -1179,6 +3541,8 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1179 | 3541 | } |
| 1180 | 3542 | } |
| 1181 | 3543 | |
| 3544 | self.active_decl = decl; | |
| 3545 | ||
| 1182 | 3546 | const res = if (debug_buffers) |dbg| |
| 1183 | 3547 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ |
| 1184 | 3548 | .dwarf = .{ |
| ... | ... | @@ -1190,92 +3554,25 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1190 | 3554 | else |
| 1191 | 3555 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 1192 | 3556 | switch (res) { |
| 1193 | .appended => {}, | |
| 3557 | .appended => { | |
| 3558 | // TODO clearing the code and relocs buffer should probably be orchestrated | |
| 3559 | // in a different, smarter, more automatic way somewhere else, in a more centralised | |
| 3560 | // way than this. | |
| 3561 | // If we don't clear the buffers here, we are up for some nasty surprises when | |
| 3562 | // this TextBlock is reused later on and was not freed by freeTextBlock(). | |
| 3563 | decl.link.macho.code.clearAndFree(self.base.allocator); | |
| 3564 | try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items); | |
| 3565 | }, | |
| 1194 | 3566 | .fail => |em| { |
| 1195 | // Clear any PIE fixups for this decl. | |
| 1196 | self.pie_fixups.shrinkRetainingCapacity(0); | |
| 1197 | // Clear any stub fixups for this decl. | |
| 1198 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 1199 | 3567 | decl.analysis = .codegen_failure; |
| 1200 | 3568 | try module.failed_decls.put(module.gpa, decl, em); |
| 1201 | 3569 | return; |
| 1202 | 3570 | }, |
| 1203 | 3571 | } |
| 1204 | const symbol = try self.placeDecl(decl, code_buffer.items.len); | |
| 1205 | ||
| 1206 | // Calculate displacements to target addr (if any). | |
| 1207 | while (self.pie_fixups.popOrNull()) |fixup| { | |
| 1208 | assert(fixup.size == 4); | |
| 1209 | const this_addr = symbol.n_value + fixup.offset; | |
| 1210 | const target_addr = fixup.target_addr; | |
| 1211 | ||
| 1212 | switch (self.base.options.target.cpu.arch) { | |
| 1213 | .x86_64 => { | |
| 1214 | const displacement = try math.cast(u32, target_addr - this_addr - 4); | |
| 1215 | mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement); | |
| 1216 | }, | |
| 1217 | .aarch64 => { | |
| 1218 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). | |
| 1219 | { | |
| 1220 | const inst = code_buffer.items[fixup.offset..][0..4]; | |
| 1221 | const parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1222 | aarch64.Instruction, | |
| 1223 | aarch64.Instruction.pc_relative_address, | |
| 1224 | ), inst); | |
| 1225 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1226 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1227 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); | |
| 1228 | parsed.immhi = @truncate(u19, pages >> 2); | |
| 1229 | parsed.immlo = @truncate(u2, pages); | |
| 1230 | } | |
| 1231 | { | |
| 1232 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; | |
| 1233 | const parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1234 | aarch64.Instruction, | |
| 1235 | aarch64.Instruction.load_store_register, | |
| 1236 | ), inst); | |
| 1237 | const narrowed = @truncate(u12, target_addr); | |
| 1238 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1239 | parsed.offset = offset; | |
| 1240 | } | |
| 1241 | }, | |
| 1242 | else => unreachable, // unsupported target architecture | |
| 1243 | } | |
| 1244 | } | |
| 1245 | ||
| 1246 | // Resolve stubs (if any) | |
| 1247 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1248 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | |
| 1249 | for (self.stub_fixups.items) |fixup| { | |
| 1250 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; | |
| 1251 | const text_addr = symbol.n_value + fixup.start; | |
| 1252 | switch (self.base.options.target.cpu.arch) { | |
| 1253 | .x86_64 => { | |
| 1254 | assert(stub_addr >= text_addr + fixup.len); | |
| 1255 | const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len); | |
| 1256 | const placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | |
| 1257 | mem.writeIntSliceLittle(u32, placeholder, displacement); | |
| 1258 | }, | |
| 1259 | .aarch64 => { | |
| 1260 | assert(stub_addr >= text_addr); | |
| 1261 | const displacement = try math.cast(i28, stub_addr - text_addr); | |
| 1262 | const placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | |
| 1263 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32()); | |
| 1264 | }, | |
| 1265 | else => unreachable, // unsupported target architecture | |
| 1266 | } | |
| 1267 | if (!fixup.already_defined) { | |
| 1268 | try self.writeStub(fixup.symbol); | |
| 1269 | try self.writeStubInStubHelper(fixup.symbol); | |
| 1270 | try self.writeLazySymbolPointer(fixup.symbol); | |
| 1271 | 3572 | |
| 1272 | self.rebase_info_dirty = true; | |
| 1273 | self.lazy_binding_info_dirty = true; | |
| 1274 | } | |
| 1275 | } | |
| 1276 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 3573 | const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len); | |
| 1277 | 3574 | |
| 1278 | try self.writeCode(symbol, code_buffer.items); | |
| 3575 | try self.writeCode(symbol, decl.link.macho.code.items); | |
| 1279 | 3576 | |
| 1280 | 3577 | if (debug_buffers) |db| { |
| 1281 | 3578 | try self.d_sym.?.commitDeclDebugInfo( |
| ... | ... | @@ -1327,6 +3624,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1327 | 3624 | } |
| 1328 | 3625 | } |
| 1329 | 3626 | |
| 3627 | self.active_decl = decl; | |
| 3628 | ||
| 1330 | 3629 | const res = if (debug_buffers) |dbg| |
| 1331 | 3630 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 1332 | 3631 | .ty = decl.ty, |
| ... | ... | @@ -1344,18 +3643,27 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1344 | 3643 | .val = decl.val, |
| 1345 | 3644 | }, &code_buffer, .none); |
| 1346 | 3645 | |
| 1347 | const code = switch (res) { | |
| 1348 | .externally_managed => |x| x, | |
| 1349 | .appended => code_buffer.items, | |
| 1350 | .fail => |em| { | |
| 1351 | decl.analysis = .codegen_failure; | |
| 1352 | try module.failed_decls.put(module.gpa, decl, em); | |
| 1353 | return; | |
| 1354 | }, | |
| 3646 | const code = blk: { | |
| 3647 | switch (res) { | |
| 3648 | .externally_managed => |x| break :blk x, | |
| 3649 | .appended => { | |
| 3650 | // TODO clearing the code and relocs buffer should probably be orchestrated | |
| 3651 | // in a different, smarter, more automatic way somewhere else, in a more centralised | |
| 3652 | // way than this. | |
| 3653 | // If we don't clear the buffers here, we are up for some nasty surprises when | |
| 3654 | // this TextBlock is reused later on and was not freed by freeTextBlock(). | |
| 3655 | decl.link.macho.code.clearAndFree(self.base.allocator); | |
| 3656 | try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items); | |
| 3657 | break :blk decl.link.macho.code.items; | |
| 3658 | }, | |
| 3659 | .fail => |em| { | |
| 3660 | decl.analysis = .codegen_failure; | |
| 3661 | try module.failed_decls.put(module.gpa, decl, em); | |
| 3662 | return; | |
| 3663 | }, | |
| 3664 | } | |
| 1355 | 3665 | }; |
| 1356 | 3666 | const symbol = try self.placeDecl(decl, code.len); |
| 1357 | assert(self.pie_fixups.items.len == 0); | |
| 1358 | assert(self.stub_fixups.items.len == 0); | |
| 1359 | 3667 | |
| 1360 | 3668 | try self.writeCode(symbol, code); |
| 1361 | 3669 | |
| ... | ... | @@ -1379,13 +3687,12 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 |
| 1379 | 3687 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1380 | 3688 | |
| 1381 | 3689 | if (vaddr != symbol.n_value) { |
| 1382 | log.debug(" (writing new offset table entry)", .{}); | |
| 1383 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1384 | .kind = .Local, | |
| 1385 | .symbol = decl.link.macho.local_sym_index, | |
| 1386 | .index = decl.link.macho.offset_table_index, | |
| 1387 | }; | |
| 1388 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | |
| 3690 | log.debug(" (writing new GOT entry)", .{}); | |
| 3691 | const got_index = self.got_entries_map.get(.{ | |
| 3692 | .where = .local, | |
| 3693 | .where_index = decl.link.macho.local_sym_index, | |
| 3694 | }) orelse unreachable; | |
| 3695 | try self.writeGotEntry(got_index); | |
| 1389 | 3696 | } |
| 1390 | 3697 | |
| 1391 | 3698 | symbol.n_value = vaddr; |
| ... | ... | @@ -1397,7 +3704,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 |
| 1397 | 3704 | const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)}); |
| 1398 | 3705 | defer self.base.allocator.free(new_name); |
| 1399 | 3706 | |
| 1400 | symbol.n_strx = try self.updateString(symbol.n_strx, new_name); | |
| 3707 | symbol.n_strx = try self.makeString(new_name); | |
| 1401 | 3708 | symbol.n_type = macho.N_SECT; |
| 1402 | 3709 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 1403 | 3710 | symbol.n_desc = 0; |
| ... | ... | @@ -1423,16 +3730,35 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 |
| 1423 | 3730 | .n_desc = 0, |
| 1424 | 3731 | .n_value = addr, |
| 1425 | 3732 | }; |
| 1426 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1427 | .kind = .Local, | |
| 1428 | .symbol = decl.link.macho.local_sym_index, | |
| 1429 | .index = decl.link.macho.offset_table_index, | |
| 1430 | }; | |
| 3733 | const got_index = self.got_entries_map.get(.{ | |
| 3734 | .where = .local, | |
| 3735 | .where_index = decl.link.macho.local_sym_index, | |
| 3736 | }) orelse unreachable; | |
| 3737 | try self.writeGotEntry(got_index); | |
| 1431 | 3738 | |
| 1432 | 3739 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1433 | 3740 | if (self.d_sym) |*ds| |
| 1434 | 3741 | try ds.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1435 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | |
| 3742 | } | |
| 3743 | ||
| 3744 | // Resolve relocations | |
| 3745 | try decl.link.macho.resolveRelocs(self); | |
| 3746 | // TODO this requires further investigation: should we dispose of resolved relocs, or keep them | |
| 3747 | // so that we can reapply them when moving/growing sections? | |
| 3748 | decl.link.macho.relocs.clearAndFree(self.base.allocator); | |
| 3749 | ||
| 3750 | // Apply pending updates | |
| 3751 | while (self.pending_updates.popOrNull()) |update| { | |
| 3752 | switch (update.kind) { | |
| 3753 | .got => unreachable, | |
| 3754 | .stub => { | |
| 3755 | try self.writeStub(update.index); | |
| 3756 | try self.writeStubInStubHelper(update.index); | |
| 3757 | try self.writeLazySymbolPointer(update.index); | |
| 3758 | self.rebase_info_dirty = true; | |
| 3759 | self.lazy_binding_info_dirty = true; | |
| 3760 | }, | |
| 3761 | } | |
| 1436 | 3762 | } |
| 1437 | 3763 | |
| 1438 | 3764 | return symbol; |
| ... | ... | @@ -1443,6 +3769,7 @@ fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void { |
| 1443 | 3769 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1444 | 3770 | const section_offset = symbol.n_value - text_section.addr; |
| 1445 | 3771 | const file_offset = text_section.offset + section_offset; |
| 3772 | log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(symbol.n_strx), file_offset }); | |
| 1446 | 3773 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1447 | 3774 | } |
| 1448 | 3775 | |
| ... | ... | @@ -1518,7 +3845,7 @@ pub fn updateDeclExports( |
| 1518 | 3845 | if (exp.link.macho.sym_index) |i| { |
| 1519 | 3846 | const sym = &self.globals.items[i]; |
| 1520 | 3847 | sym.* = .{ |
| 1521 | .n_strx = try self.updateString(sym.n_strx, exp_name), | |
| 3848 | .n_strx = sym.n_strx, | |
| 1522 | 3849 | .n_type = n_type, |
| 1523 | 3850 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| 1524 | 3851 | .n_desc = n_desc, |
| ... | ... | @@ -1529,7 +3856,7 @@ pub fn updateDeclExports( |
| 1529 | 3856 | const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { |
| 1530 | 3857 | _ = self.globals.addOneAssumeCapacity(); |
| 1531 | 3858 | self.export_info_dirty = true; |
| 1532 | break :blk self.globals.items.len - 1; | |
| 3859 | break :blk @intCast(u32, self.globals.items.len - 1); | |
| 1533 | 3860 | }; |
| 1534 | 3861 | self.globals.items[i] = .{ |
| 1535 | 3862 | .n_strx = name_str_index, |
| ... | ... | @@ -1538,6 +3865,12 @@ pub fn updateDeclExports( |
| 1538 | 3865 | .n_desc = n_desc, |
| 1539 | 3866 | .n_value = decl_sym.n_value, |
| 1540 | 3867 | }; |
| 3868 | const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, name_str_index); | |
| 3869 | resolv.value_ptr.* = .{ | |
| 3870 | .where = .global, | |
| 3871 | .where_index = i, | |
| 3872 | .local_sym_index = decl.link.macho.local_sym_index, | |
| 3873 | }; | |
| 1541 | 3874 | |
| 1542 | 3875 | exp.link.macho.sym_index = @intCast(u32, i); |
| 1543 | 3876 | } |
| ... | ... | @@ -1551,14 +3884,22 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 1551 | 3884 | } |
| 1552 | 3885 | |
| 1553 | 3886 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 3887 | log.debug("freeDecl {*}", .{decl}); | |
| 3888 | _ = self.decls.swapRemove(decl); | |
| 1554 | 3889 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 1555 | 3890 | self.freeTextBlock(&decl.link.macho); |
| 1556 | 3891 | if (decl.link.macho.local_sym_index != 0) { |
| 1557 | 3892 | self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; |
| 1558 | self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {}; | |
| 1559 | 3893 | |
| 1560 | self.locals.items[decl.link.macho.local_sym_index].n_type = 0; | |
| 3894 | const got_key = GotIndirectionKey{ | |
| 3895 | .where = .local, | |
| 3896 | .where_index = decl.link.macho.local_sym_index, | |
| 3897 | }; | |
| 3898 | const got_index = self.got_entries_map.get(got_key) orelse unreachable; | |
| 3899 | _ = self.got_entries_map.remove(got_key); | |
| 3900 | self.got_entries_free_list.append(self.base.allocator, got_index) catch {}; | |
| 1561 | 3901 | |
| 3902 | self.locals.items[decl.link.macho.local_sym_index].n_type = 0; | |
| 1562 | 3903 | decl.link.macho.local_sym_index = 0; |
| 1563 | 3904 | } |
| 1564 | 3905 | if (self.d_sym) |*ds| { |
| ... | ... | @@ -1598,54 +3939,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1598 | 3939 | .Lib => return error.TODOImplementWritingLibFiles, |
| 1599 | 3940 | } |
| 1600 | 3941 | |
| 1601 | if (self.header == null) { | |
| 1602 | var header: macho.mach_header_64 = undefined; | |
| 1603 | header.magic = macho.MH_MAGIC_64; | |
| 1604 | ||
| 1605 | const CpuInfo = struct { | |
| 1606 | cpu_type: macho.cpu_type_t, | |
| 1607 | cpu_subtype: macho.cpu_subtype_t, | |
| 1608 | }; | |
| 1609 | ||
| 1610 | const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) { | |
| 1611 | .aarch64 => .{ | |
| 1612 | .cpu_type = macho.CPU_TYPE_ARM64, | |
| 1613 | .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL, | |
| 1614 | }, | |
| 1615 | .x86_64 => .{ | |
| 1616 | .cpu_type = macho.CPU_TYPE_X86_64, | |
| 1617 | .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL, | |
| 1618 | }, | |
| 1619 | else => return error.UnsupportedMachOArchitecture, | |
| 1620 | }; | |
| 1621 | header.cputype = cpu_info.cpu_type; | |
| 1622 | header.cpusubtype = cpu_info.cpu_subtype; | |
| 1623 | ||
| 1624 | const filetype: u32 = switch (self.base.options.output_mode) { | |
| 1625 | .Exe => macho.MH_EXECUTE, | |
| 1626 | .Obj => macho.MH_OBJECT, | |
| 1627 | .Lib => switch (self.base.options.link_mode) { | |
| 1628 | .Static => return error.TODOStaticLibMachOType, | |
| 1629 | .Dynamic => macho.MH_DYLIB, | |
| 1630 | }, | |
| 1631 | }; | |
| 1632 | header.filetype = filetype; | |
| 1633 | // These will get populated at the end of flushing the results to file. | |
| 1634 | header.ncmds = 0; | |
| 1635 | header.sizeofcmds = 0; | |
| 1636 | ||
| 1637 | switch (self.base.options.output_mode) { | |
| 1638 | .Exe => { | |
| 1639 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE; | |
| 1640 | }, | |
| 1641 | else => { | |
| 1642 | header.flags = 0; | |
| 1643 | }, | |
| 1644 | } | |
| 1645 | header.reserved = 0; | |
| 1646 | self.header = header; | |
| 1647 | self.header_dirty = true; | |
| 1648 | } | |
| 1649 | 3942 | if (self.pagezero_segment_cmd_index == null) { |
| 1650 | 3943 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1651 | 3944 | try self.load_commands.append(self.base.allocator, .{ |
| ... | ... | @@ -1653,7 +3946,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1653 | 3946 | .vmsize = 0x100000000, // size always set to 4GB |
| 1654 | 3947 | }), |
| 1655 | 3948 | }); |
| 1656 | self.header_dirty = true; | |
| 1657 | 3949 | self.load_commands_dirty = true; |
| 1658 | 3950 | } |
| 1659 | 3951 | if (self.text_segment_cmd_index == null) { |
| ... | ... | @@ -1662,8 +3954,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1662 | 3954 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1663 | 3955 | |
| 1664 | 3956 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1665 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; | |
| 1666 | const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; | |
| 3957 | const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; | |
| 3958 | const ideal_size = self.header_pad + program_code_size_hint + 3 * got_size_hint; | |
| 1667 | 3959 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); |
| 1668 | 3960 | |
| 1669 | 3961 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| ... | ... | @@ -1677,7 +3969,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1677 | 3969 | .initprot = initprot, |
| 1678 | 3970 | }), |
| 1679 | 3971 | }); |
| 1680 | self.header_dirty = true; | |
| 1681 | 3972 | self.load_commands_dirty = true; |
| 1682 | 3973 | } |
| 1683 | 3974 | if (self.text_section_index == null) { |
| ... | ... | @@ -1702,7 +3993,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1702 | 3993 | .@"align" = alignment, |
| 1703 | 3994 | .flags = flags, |
| 1704 | 3995 | }); |
| 1705 | self.header_dirty = true; | |
| 1706 | 3996 | self.load_commands_dirty = true; |
| 1707 | 3997 | } |
| 1708 | 3998 | if (self.stubs_section_index == null) { |
| ... | ... | @@ -1734,7 +4024,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1734 | 4024 | .flags = flags, |
| 1735 | 4025 | .reserved2 = stub_size, |
| 1736 | 4026 | }); |
| 1737 | self.header_dirty = true; | |
| 1738 | 4027 | self.load_commands_dirty = true; |
| 1739 | 4028 | } |
| 1740 | 4029 | if (self.stub_helper_section_index == null) { |
| ... | ... | @@ -1760,7 +4049,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1760 | 4049 | .@"align" = alignment, |
| 1761 | 4050 | .flags = flags, |
| 1762 | 4051 | }); |
| 1763 | self.header_dirty = true; | |
| 1764 | 4052 | self.load_commands_dirty = true; |
| 1765 | 4053 | } |
| 1766 | 4054 | if (self.data_const_segment_cmd_index == null) { |
| ... | ... | @@ -1784,7 +4072,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1784 | 4072 | .initprot = initprot, |
| 1785 | 4073 | }), |
| 1786 | 4074 | }); |
| 1787 | self.header_dirty = true; | |
| 1788 | 4075 | self.load_commands_dirty = true; |
| 1789 | 4076 | } |
| 1790 | 4077 | if (self.got_section_index == null) { |
| ... | ... | @@ -1805,7 +4092,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1805 | 4092 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1806 | 4093 | .flags = flags, |
| 1807 | 4094 | }); |
| 1808 | self.header_dirty = true; | |
| 1809 | 4095 | self.load_commands_dirty = true; |
| 1810 | 4096 | } |
| 1811 | 4097 | if (self.data_segment_cmd_index == null) { |
| ... | ... | @@ -1829,7 +4115,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1829 | 4115 | .initprot = initprot, |
| 1830 | 4116 | }), |
| 1831 | 4117 | }); |
| 1832 | self.header_dirty = true; | |
| 1833 | 4118 | self.load_commands_dirty = true; |
| 1834 | 4119 | } |
| 1835 | 4120 | if (self.la_symbol_ptr_section_index == null) { |
| ... | ... | @@ -1850,7 +4135,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1850 | 4135 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1851 | 4136 | .flags = flags, |
| 1852 | 4137 | }); |
| 1853 | self.header_dirty = true; | |
| 1854 | 4138 | self.load_commands_dirty = true; |
| 1855 | 4139 | } |
| 1856 | 4140 | if (self.data_section_index == null) { |
| ... | ... | @@ -1869,7 +4153,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1869 | 4153 | .offset = @intCast(u32, off), |
| 1870 | 4154 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1871 | 4155 | }); |
| 1872 | self.header_dirty = true; | |
| 1873 | 4156 | self.load_commands_dirty = true; |
| 1874 | 4157 | } |
| 1875 | 4158 | if (self.linkedit_segment_cmd_index == null) { |
| ... | ... | @@ -1889,7 +4172,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1889 | 4172 | .initprot = initprot, |
| 1890 | 4173 | }), |
| 1891 | 4174 | }); |
| 1892 | self.header_dirty = true; | |
| 1893 | 4175 | self.load_commands_dirty = true; |
| 1894 | 4176 | } |
| 1895 | 4177 | if (self.dyld_info_cmd_index == null) { |
| ... | ... | @@ -1936,7 +4218,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1936 | 4218 | dyld.export_off = @intCast(u32, export_off); |
| 1937 | 4219 | dyld.export_size = expected_size; |
| 1938 | 4220 | |
| 1939 | self.header_dirty = true; | |
| 1940 | 4221 | self.load_commands_dirty = true; |
| 1941 | 4222 | } |
| 1942 | 4223 | if (self.symtab_cmd_index == null) { |
| ... | ... | @@ -1961,16 +4242,15 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1961 | 4242 | symtab.symoff = @intCast(u32, symtab_off); |
| 1962 | 4243 | symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint); |
| 1963 | 4244 | |
| 1964 | try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. | |
| 1965 | const strtab_size = self.string_table.items.len; | |
| 4245 | try self.strtab.append(self.base.allocator, 0); | |
| 4246 | const strtab_size = self.strtab.items.len; | |
| 1966 | 4247 | const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off); |
| 1967 | 4248 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); |
| 1968 | 4249 | symtab.stroff = @intCast(u32, strtab_off); |
| 1969 | 4250 | symtab.strsize = @intCast(u32, strtab_size); |
| 1970 | 4251 | |
| 1971 | self.header_dirty = true; | |
| 1972 | 4252 | self.load_commands_dirty = true; |
| 1973 | self.string_table_dirty = true; | |
| 4253 | self.strtab_dirty = true; | |
| 1974 | 4254 | } |
| 1975 | 4255 | if (self.dysymtab_cmd_index == null) { |
| 1976 | 4256 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -2005,7 +4285,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2005 | 4285 | .nlocrel = 0, |
| 2006 | 4286 | }, |
| 2007 | 4287 | }); |
| 2008 | self.header_dirty = true; | |
| 2009 | 4288 | self.load_commands_dirty = true; |
| 2010 | 4289 | } |
| 2011 | 4290 | if (self.dylinker_cmd_index == null) { |
| ... | ... | @@ -2015,7 +4294,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2015 | 4294 | @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), |
| 2016 | 4295 | @sizeOf(u64), |
| 2017 | 4296 | )); |
| 2018 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ | |
| 4297 | var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{ | |
| 2019 | 4298 | .cmd = macho.LC_LOAD_DYLINKER, |
| 2020 | 4299 | .cmdsize = cmdsize, |
| 2021 | 4300 | .name = @sizeOf(macho.dylinker_command), |
| ... | ... | @@ -2024,18 +4303,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2024 | 4303 | mem.set(u8, dylinker_cmd.data, 0); |
| 2025 | 4304 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); |
| 2026 | 4305 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); |
| 2027 | self.header_dirty = true; | |
| 2028 | 4306 | self.load_commands_dirty = true; |
| 2029 | 4307 | } |
| 2030 | 4308 | if (self.libsystem_cmd_index == null) { |
| 2031 | 4309 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 2032 | 4310 | |
| 2033 | var dylib_cmd = try createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); | |
| 4311 | var dylib_cmd = try commands.createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); | |
| 2034 | 4312 | errdefer dylib_cmd.deinit(self.base.allocator); |
| 2035 | 4313 | |
| 2036 | 4314 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 2037 | 4315 | |
| 2038 | self.header_dirty = true; | |
| 2039 | 4316 | self.load_commands_dirty = true; |
| 2040 | 4317 | } |
| 2041 | 4318 | if (self.main_cmd_index == null) { |
| ... | ... | @@ -2048,7 +4325,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2048 | 4325 | .stacksize = 0, |
| 2049 | 4326 | }, |
| 2050 | 4327 | }); |
| 2051 | self.header_dirty = true; | |
| 2052 | 4328 | self.load_commands_dirty = true; |
| 2053 | 4329 | } |
| 2054 | 4330 | if (self.version_min_cmd_index == null) { |
| ... | ... | @@ -2070,7 +4346,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2070 | 4346 | .sdk = version, |
| 2071 | 4347 | }, |
| 2072 | 4348 | }); |
| 2073 | self.header_dirty = true; | |
| 2074 | 4349 | self.load_commands_dirty = true; |
| 2075 | 4350 | } |
| 2076 | 4351 | if (self.source_version_cmd_index == null) { |
| ... | ... | @@ -2082,7 +4357,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2082 | 4357 | .version = 0x0, |
| 2083 | 4358 | }, |
| 2084 | 4359 | }); |
| 2085 | self.header_dirty = true; | |
| 2086 | 4360 | self.load_commands_dirty = true; |
| 2087 | 4361 | } |
| 2088 | 4362 | if (self.uuid_cmd_index == null) { |
| ... | ... | @@ -2094,7 +4368,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2094 | 4368 | }; |
| 2095 | 4369 | std.crypto.random.bytes(&uuid_cmd.uuid); |
| 2096 | 4370 | try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); |
| 2097 | self.header_dirty = true; | |
| 2098 | 4371 | self.load_commands_dirty = true; |
| 2099 | 4372 | } |
| 2100 | 4373 | if (self.code_signature_cmd_index == null) { |
| ... | ... | @@ -2107,31 +4380,32 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2107 | 4380 | .datasize = 0, |
| 2108 | 4381 | }, |
| 2109 | 4382 | }); |
| 2110 | self.header_dirty = true; | |
| 2111 | 4383 | self.load_commands_dirty = true; |
| 2112 | 4384 | } |
| 2113 | if (!self.nonlazy_imports.contains("dyld_stub_binder")) { | |
| 2114 | const index = @intCast(u32, self.nonlazy_imports.count()); | |
| 2115 | const name = try self.base.allocator.dupe(u8, "dyld_stub_binder"); | |
| 2116 | const offset = try self.makeString("dyld_stub_binder"); | |
| 2117 | try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{ | |
| 2118 | .symbol = .{ | |
| 2119 | .n_strx = offset, | |
| 2120 | .n_type = std.macho.N_UNDF | std.macho.N_EXT, | |
| 2121 | .n_sect = 0, | |
| 2122 | .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, | |
| 2123 | .n_value = 0, | |
| 2124 | }, | |
| 2125 | .dylib_ordinal = 1, // TODO this is currently hardcoded. | |
| 2126 | .index = index, | |
| 4385 | if (!self.strtab_dir.containsAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ | |
| 4386 | .strtab = &self.strtab, | |
| 4387 | })) { | |
| 4388 | const import_sym_index = @intCast(u32, self.imports.items.len); | |
| 4389 | const n_strx = try self.makeString("dyld_stub_binder"); | |
| 4390 | try self.imports.append(self.base.allocator, .{ | |
| 4391 | .n_strx = n_strx, | |
| 4392 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 4393 | .n_sect = 0, | |
| 4394 | .n_desc = packDylibOrdinal(1), | |
| 4395 | .n_value = 0, | |
| 2127 | 4396 | }); |
| 2128 | const off_index = @intCast(u32, self.offset_table.items.len); | |
| 2129 | try self.offset_table.append(self.base.allocator, .{ | |
| 2130 | .kind = .Extern, | |
| 2131 | .symbol = index, | |
| 2132 | .index = off_index, | |
| 4397 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 4398 | .where = .import, | |
| 4399 | .where_index = import_sym_index, | |
| 2133 | 4400 | }); |
| 2134 | try self.writeOffsetTableEntry(off_index); | |
| 4401 | const got_key = GotIndirectionKey{ | |
| 4402 | .where = .import, | |
| 4403 | .where_index = import_sym_index, | |
| 4404 | }; | |
| 4405 | const got_index = @intCast(u32, self.got_entries.items.len); | |
| 4406 | try self.got_entries.append(self.base.allocator, got_key); | |
| 4407 | try self.got_entries_map.putNoClobber(self.base.allocator, got_key, got_index); | |
| 4408 | try self.writeGotEntry(got_index); | |
| 2135 | 4409 | self.binding_info_dirty = true; |
| 2136 | 4410 | } |
| 2137 | 4411 | if (self.stub_helper_stubs_start_off == null) { |
| ... | ... | @@ -2172,7 +4446,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2172 | 4446 | // should be deleted because the block that it points to has grown to take up |
| 2173 | 4447 | // more of the extra capacity. |
| 2174 | 4448 | if (!big_block.freeListEligible(self.*)) { |
| 2175 | _ = self.text_block_free_list.swapRemove(i); | |
| 4449 | const bl = self.text_block_free_list.swapRemove(i); | |
| 4450 | bl.deinit(self.base.allocator); | |
| 2176 | 4451 | } else { |
| 2177 | 4452 | i += 1; |
| 2178 | 4453 | } |
| ... | ... | @@ -2244,64 +4519,45 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2244 | 4519 | return vaddr; |
| 2245 | 4520 | } |
| 2246 | 4521 | |
| 2247 | fn makeString(self: *MachO, bytes: []const u8) !u32 { | |
| 2248 | if (self.string_table_directory.get(bytes)) |offset| { | |
| 2249 | log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); | |
| 2250 | return offset; | |
| 2251 | } | |
| 2252 | ||
| 2253 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); | |
| 2254 | const offset = @intCast(u32, self.string_table.items.len); | |
| 4522 | pub fn addExternFn(self: *MachO, name: []const u8) !u32 { | |
| 4523 | const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); | |
| 4524 | defer self.base.allocator.free(sym_name); | |
| 2255 | 4525 | |
| 2256 | log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset }); | |
| 2257 | ||
| 2258 | self.string_table.appendSliceAssumeCapacity(bytes); | |
| 2259 | self.string_table.appendAssumeCapacity(0); | |
| 2260 | ||
| 2261 | try self.string_table_directory.putNoClobber( | |
| 2262 | self.base.allocator, | |
| 2263 | try self.base.allocator.dupe(u8, bytes), | |
| 2264 | offset, | |
| 2265 | ); | |
| 2266 | ||
| 2267 | self.string_table_dirty = true; | |
| 2268 | if (self.d_sym) |*ds| | |
| 2269 | ds.string_table_dirty = true; | |
| 2270 | ||
| 2271 | return offset; | |
| 2272 | } | |
| 4526 | if (self.strtab_dir.getAdapted(@as([]const u8, sym_name), StringSliceAdapter{ | |
| 4527 | .strtab = &self.strtab, | |
| 4528 | })) |n_strx| { | |
| 4529 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 4530 | return resolv.where_index; | |
| 4531 | } | |
| 2273 | 4532 | |
| 2274 | fn getString(self: *MachO, str_off: u32) []const u8 { | |
| 2275 | assert(str_off < self.string_table.items.len); | |
| 2276 | return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off)); | |
| 2277 | } | |
| 4533 | log.debug("adding new extern function '{s}' with dylib ordinal 1", .{sym_name}); | |
| 4534 | const import_sym_index = @intCast(u32, self.imports.items.len); | |
| 4535 | const n_strx = try self.makeString(sym_name); | |
| 4536 | try self.imports.append(self.base.allocator, .{ | |
| 4537 | .n_strx = n_strx, | |
| 4538 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 4539 | .n_sect = 0, | |
| 4540 | .n_desc = packDylibOrdinal(1), | |
| 4541 | .n_value = 0, | |
| 4542 | }); | |
| 4543 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 4544 | .where = .import, | |
| 4545 | .where_index = import_sym_index, | |
| 4546 | }); | |
| 2278 | 4547 | |
| 2279 | fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { | |
| 2280 | const existing_name = self.getString(old_str_off); | |
| 2281 | if (mem.eql(u8, existing_name, new_name)) { | |
| 2282 | return old_str_off; | |
| 2283 | } | |
| 2284 | return self.makeString(new_name); | |
| 2285 | } | |
| 4548 | const stubs_index = @intCast(u32, self.stubs.items.len); | |
| 4549 | try self.stubs.append(self.base.allocator, import_sym_index); | |
| 4550 | try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index); | |
| 2286 | 4551 | |
| 2287 | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { | |
| 2288 | const index = @intCast(u32, self.lazy_imports.count()); | |
| 2289 | const offset = try self.makeString(name); | |
| 2290 | const sym_name = try self.base.allocator.dupe(u8, name); | |
| 2291 | const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem. | |
| 2292 | try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{ | |
| 2293 | .symbol = .{ | |
| 2294 | .n_strx = offset, | |
| 2295 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 2296 | .n_sect = 0, | |
| 2297 | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, | |
| 2298 | .n_value = 0, | |
| 2299 | }, | |
| 2300 | .dylib_ordinal = dylib_ordinal, | |
| 2301 | .index = index, | |
| 4552 | // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail }, | |
| 4553 | // which obviously doesn't include file writing op errors. So instead of trying to write the stub | |
| 4554 | // entry right here and now, queue it up and dispose of when updating decl. | |
| 4555 | try self.pending_updates.append(self.base.allocator, .{ | |
| 4556 | .kind = .stub, | |
| 4557 | .index = stubs_index, | |
| 2302 | 4558 | }); |
| 2303 | log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal }); | |
| 2304 | return index; | |
| 4559 | ||
| 4560 | return import_sym_index; | |
| 2305 | 4561 | } |
| 2306 | 4562 | |
| 2307 | 4563 | const NextSegmentAddressAndOffset = struct { |
| ... | ... | @@ -2455,29 +4711,26 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2455 | 4711 | return st; |
| 2456 | 4712 | } |
| 2457 | 4713 | |
| 2458 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | |
| 4714 | fn writeGotEntry(self: *MachO, index: usize) !void { | |
| 2459 | 4715 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2460 | 4716 | const sect = &seg.sections.items[self.got_section_index.?]; |
| 2461 | 4717 | const off = sect.offset + @sizeOf(u64) * index; |
| 2462 | 4718 | |
| 2463 | if (self.offset_table_count_dirty) { | |
| 4719 | if (self.got_entries_count_dirty) { | |
| 2464 | 4720 | // TODO relocate. |
| 2465 | self.offset_table_count_dirty = false; | |
| 4721 | self.got_entries_count_dirty = false; | |
| 2466 | 4722 | } |
| 2467 | 4723 | |
| 2468 | const got_entry = self.offset_table.items[index]; | |
| 2469 | const sym = blk: { | |
| 2470 | switch (got_entry.kind) { | |
| 2471 | .Local => { | |
| 2472 | break :blk self.locals.items[got_entry.symbol]; | |
| 2473 | }, | |
| 2474 | .Extern => { | |
| 2475 | break :blk self.nonlazy_imports.values()[got_entry.symbol].symbol; | |
| 2476 | }, | |
| 2477 | } | |
| 4724 | const got_entry = self.got_entries.items[index]; | |
| 4725 | const sym = switch (got_entry.where) { | |
| 4726 | .local => self.locals.items[got_entry.where_index], | |
| 4727 | .import => self.imports.items[got_entry.where_index], | |
| 2478 | 4728 | }; |
| 2479 | const sym_name = self.getString(sym.n_strx); | |
| 2480 | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name }); | |
| 4729 | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ | |
| 4730 | off, | |
| 4731 | sym.n_value, | |
| 4732 | self.getString(sym.n_strx), | |
| 4733 | }); | |
| 2481 | 4734 | try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off); |
| 2482 | 4735 | } |
| 2483 | 4736 | |
| ... | ... | @@ -2755,7 +5008,7 @@ fn relocateSymbolTable(self: *MachO) !void { |
| 2755 | 5008 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2756 | 5009 | const nlocals = self.locals.items.len; |
| 2757 | 5010 | const nglobals = self.globals.items.len; |
| 2758 | const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count(); | |
| 5011 | const nundefs = self.imports.items.len; | |
| 2759 | 5012 | const nsyms = nlocals + nglobals + nundefs; |
| 2760 | 5013 | |
| 2761 | 5014 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -2775,7 +5028,7 @@ fn relocateSymbolTable(self: *MachO) !void { |
| 2775 | 5028 | const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size); |
| 2776 | 5029 | if (amt != existing_size) return error.InputOutput; |
| 2777 | 5030 | symtab.symoff = @intCast(u32, new_symoff); |
| 2778 | self.string_table_needs_relocation = true; | |
| 5031 | self.strtab_needs_relocation = true; | |
| 2779 | 5032 | } |
| 2780 | 5033 | symtab.nsyms = @intCast(u32, nsyms); |
| 2781 | 5034 | self.load_commands_dirty = true; |
| ... | ... | @@ -2800,17 +5053,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2800 | 5053 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2801 | 5054 | const nlocals = self.locals.items.len; |
| 2802 | 5055 | const nglobals = self.globals.items.len; |
| 2803 | ||
| 2804 | const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count(); | |
| 2805 | var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); | |
| 2806 | defer undefs.deinit(); | |
| 2807 | try undefs.ensureCapacity(nundefs); | |
| 2808 | for (self.lazy_imports.values()) |*value| { | |
| 2809 | undefs.appendAssumeCapacity(value.symbol); | |
| 2810 | } | |
| 2811 | for (self.nonlazy_imports.values()) |*value| { | |
| 2812 | undefs.appendAssumeCapacity(value.symbol); | |
| 2813 | } | |
| 5056 | const nundefs = self.imports.items.len; | |
| 2814 | 5057 | |
| 2815 | 5058 | const locals_off = symtab.symoff; |
| 2816 | 5059 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| ... | ... | @@ -2823,7 +5066,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2823 | 5066 | const undefs_off = globals_off + globals_size; |
| 2824 | 5067 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| 2825 | 5068 | log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); |
| 2826 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); | |
| 5069 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off); | |
| 2827 | 5070 | |
| 2828 | 5071 | // Update dynamic symbol table. |
| 2829 | 5072 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| ... | ... | @@ -2849,10 +5092,10 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2849 | 5092 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2850 | 5093 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 2851 | 5094 | |
| 2852 | const lazy_count = self.lazy_imports.count(); | |
| 2853 | const got_entries = self.offset_table.items; | |
| 5095 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 5096 | const ngot_entries = @intCast(u32, self.got_entries.items.len); | |
| 2854 | 5097 | const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff); |
| 2855 | const nindirectsyms = @intCast(u32, lazy_count * 2 + got_entries.len); | |
| 5098 | const nindirectsyms = nstubs * 2 + ngot_entries; | |
| 2856 | 5099 | const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32)); |
| 2857 | 5100 | |
| 2858 | 5101 | if (needed_size > allocated_size) { |
| ... | ... | @@ -2871,41 +5114,85 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2871 | 5114 | var writer = stream.writer(); |
| 2872 | 5115 | |
| 2873 | 5116 | stubs.reserved1 = 0; |
| 2874 | { | |
| 2875 | var i: usize = 0; | |
| 2876 | while (i < lazy_count) : (i += 1) { | |
| 2877 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); | |
| 2878 | try writer.writeIntLittle(u32, symtab_idx); | |
| 2879 | } | |
| 5117 | for (self.stubs.items) |id| { | |
| 5118 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 2880 | 5119 | } |
| 2881 | 5120 | |
| 2882 | const base_id = @intCast(u32, lazy_count); | |
| 2883 | got.reserved1 = base_id; | |
| 2884 | for (got_entries) |entry| { | |
| 2885 | switch (entry.kind) { | |
| 2886 | .Local => { | |
| 2887 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 5121 | got.reserved1 = nstubs; | |
| 5122 | for (self.got_entries.items) |entry| { | |
| 5123 | switch (entry.where) { | |
| 5124 | .import => { | |
| 5125 | try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index); | |
| 2888 | 5126 | }, |
| 2889 | .Extern => { | |
| 2890 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id); | |
| 2891 | try writer.writeIntLittle(u32, symtab_idx); | |
| 5127 | .local => { | |
| 5128 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 2892 | 5129 | }, |
| 2893 | 5130 | } |
| 2894 | 5131 | } |
| 2895 | 5132 | |
| 2896 | la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len); | |
| 2897 | { | |
| 2898 | var i: usize = 0; | |
| 2899 | while (i < lazy_count) : (i += 1) { | |
| 2900 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); | |
| 2901 | try writer.writeIntLittle(u32, symtab_idx); | |
| 2902 | } | |
| 5133 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; | |
| 5134 | for (self.stubs.items) |id| { | |
| 5135 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 2903 | 5136 | } |
| 2904 | 5137 | |
| 2905 | 5138 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); |
| 2906 | 5139 | self.load_commands_dirty = true; |
| 2907 | 5140 | } |
| 2908 | 5141 | |
| 5142 | fn writeDices(self: *MachO) !void { | |
| 5143 | if (!self.has_dices) return; | |
| 5144 | ||
| 5145 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 5146 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; | |
| 5147 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 5148 | ||
| 5149 | var buf = std.ArrayList(u8).init(self.base.allocator); | |
| 5150 | defer buf.deinit(); | |
| 5151 | ||
| 5152 | var block: *TextBlock = self.blocks.get(.{ | |
| 5153 | .seg = self.text_segment_cmd_index orelse return, | |
| 5154 | .sect = self.text_section_index orelse return, | |
| 5155 | }) orelse return; | |
| 5156 | ||
| 5157 | while (block.prev) |prev| { | |
| 5158 | block = prev; | |
| 5159 | } | |
| 5160 | ||
| 5161 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 5162 | const text_sect = text_seg.sections.items[self.text_section_index.?]; | |
| 5163 | ||
| 5164 | while (true) { | |
| 5165 | if (block.dices.items.len > 0) { | |
| 5166 | const sym = self.locals.items[block.local_sym_index]; | |
| 5167 | const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); | |
| 5168 | ||
| 5169 | try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry)); | |
| 5170 | for (block.dices.items) |dice| { | |
| 5171 | const rebased_dice = macho.data_in_code_entry{ | |
| 5172 | .offset = base_off + dice.offset, | |
| 5173 | .length = dice.length, | |
| 5174 | .kind = dice.kind, | |
| 5175 | }; | |
| 5176 | buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice)); | |
| 5177 | } | |
| 5178 | } | |
| 5179 | ||
| 5180 | if (block.next) |next| { | |
| 5181 | block = next; | |
| 5182 | } else break; | |
| 5183 | } | |
| 5184 | ||
| 5185 | const datasize = @intCast(u32, buf.items.len); | |
| 5186 | ||
| 5187 | dice_cmd.dataoff = @intCast(u32, fileoff); | |
| 5188 | dice_cmd.datasize = datasize; | |
| 5189 | seg.inner.filesize += datasize; | |
| 5190 | ||
| 5191 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize }); | |
| 5192 | ||
| 5193 | try self.base.file.?.pwriteAll(buf.items, fileoff); | |
| 5194 | } | |
| 5195 | ||
| 2909 | 5196 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 2910 | 5197 | // TODO figure out how not to rewrite padding every single time. |
| 2911 | 5198 | const tracy = trace(@src()); |
| ... | ... | @@ -2961,7 +5248,7 @@ fn writeCodeSignature(self: *MachO) !void { |
| 2961 | 5248 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); |
| 2962 | 5249 | } |
| 2963 | 5250 | |
| 2964 | fn writeExportTrie(self: *MachO) !void { | |
| 5251 | fn writeExportInfo(self: *MachO) !void { | |
| 2965 | 5252 | if (!self.export_info_dirty) return; |
| 2966 | 5253 | if (self.globals.items.len == 0) return; |
| 2967 | 5254 | |
| ... | ... | @@ -2972,13 +5259,18 @@ fn writeExportTrie(self: *MachO) !void { |
| 2972 | 5259 | defer trie.deinit(); |
| 2973 | 5260 | |
| 2974 | 5261 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2975 | for (self.globals.items) |symbol| { | |
| 2976 | // TODO figure out if we should put all global symbols into the export trie | |
| 2977 | const name = self.getString(symbol.n_strx); | |
| 2978 | assert(symbol.n_value >= text_segment.inner.vmaddr); | |
| 5262 | const base_address = text_segment.inner.vmaddr; | |
| 5263 | ||
| 5264 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. | |
| 5265 | log.debug("writing export trie", .{}); | |
| 5266 | ||
| 5267 | for (self.globals.items) |sym| { | |
| 5268 | const sym_name = self.getString(sym.n_strx); | |
| 5269 | log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value }); | |
| 5270 | ||
| 2979 | 5271 | try trie.put(.{ |
| 2980 | .name = name, | |
| 2981 | .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, | |
| 5272 | .name = sym_name, | |
| 5273 | .vmaddr_offset = sym.n_value - base_address, | |
| 2982 | 5274 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, |
| 2983 | 5275 | }); |
| 2984 | 5276 | } |
| ... | ... | @@ -3016,31 +5308,60 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 3016 | 5308 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); |
| 3017 | 5309 | defer pointers.deinit(); |
| 3018 | 5310 | |
| 5311 | { | |
| 5312 | var it = self.blocks.iterator(); | |
| 5313 | while (it.next()) |entry| { | |
| 5314 | const match = entry.key_ptr.*; | |
| 5315 | var block: *TextBlock = entry.value_ptr.*; | |
| 5316 | ||
| 5317 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 5318 | ||
| 5319 | const seg = self.load_commands.items[match.seg].Segment; | |
| 5320 | ||
| 5321 | while (true) { | |
| 5322 | const sym = self.locals.items[block.local_sym_index]; | |
| 5323 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 5324 | ||
| 5325 | for (block.rebases.items) |offset| { | |
| 5326 | try pointers.append(.{ | |
| 5327 | .offset = base_offset + offset, | |
| 5328 | .segment_id = match.seg, | |
| 5329 | }); | |
| 5330 | } | |
| 5331 | ||
| 5332 | if (block.prev) |prev| { | |
| 5333 | block = prev; | |
| 5334 | } else break; | |
| 5335 | } | |
| 5336 | } | |
| 5337 | } | |
| 5338 | ||
| 3019 | 5339 | if (self.got_section_index) |idx| { |
| 3020 | 5340 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 3021 | 5341 | const sect = seg.sections.items[idx]; |
| 3022 | 5342 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3023 | const segment_id = self.data_const_segment_cmd_index.?; | |
| 5343 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 5344 | ||
| 5345 | for (self.got_entries.items) |entry, i| { | |
| 5346 | if (entry.where == .import) continue; | |
| 3024 | 5347 | |
| 3025 | for (self.offset_table.items) |entry| { | |
| 3026 | if (entry.kind == .Extern) continue; | |
| 3027 | 5348 | try pointers.append(.{ |
| 3028 | .offset = base_offset + entry.index * @sizeOf(u64), | |
| 5349 | .offset = base_offset + i * @sizeOf(u64), | |
| 3029 | 5350 | .segment_id = segment_id, |
| 3030 | 5351 | }); |
| 3031 | 5352 | } |
| 3032 | 5353 | } |
| 3033 | 5354 | |
| 3034 | 5355 | if (self.la_symbol_ptr_section_index) |idx| { |
| 3035 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.count()); | |
| 3036 | 5356 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 3037 | 5357 | const sect = seg.sections.items[idx]; |
| 3038 | 5358 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3039 | const segment_id = self.data_segment_cmd_index.?; | |
| 5359 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 3040 | 5360 | |
| 3041 | for (self.lazy_imports.values()) |*value| { | |
| 5361 | try pointers.ensureUnusedCapacity(self.stubs.items.len); | |
| 5362 | for (self.stubs.items) |_, i| { | |
| 3042 | 5363 | pointers.appendAssumeCapacity(.{ |
| 3043 | .offset = base_offset + value.index * @sizeOf(u64), | |
| 5364 | .offset = base_offset + i * @sizeOf(u64), | |
| 3044 | 5365 | .segment_id = segment_id, |
| 3045 | 5366 | }); |
| 3046 | 5367 | } |
| ... | ... | @@ -3073,7 +5394,7 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 3073 | 5394 | self.rebase_info_dirty = false; |
| 3074 | 5395 | } |
| 3075 | 5396 | |
| 3076 | fn writeBindingInfoTable(self: *MachO) !void { | |
| 5397 | fn writeBindInfoTable(self: *MachO) !void { | |
| 3077 | 5398 | if (!self.binding_info_dirty) return; |
| 3078 | 5399 | |
| 3079 | 5400 | const tracy = trace(@src()); |
| ... | ... | @@ -3088,19 +5409,50 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 3088 | 5409 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3089 | 5410 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); |
| 3090 | 5411 | |
| 3091 | for (self.offset_table.items) |entry| { | |
| 3092 | if (entry.kind == .Local) continue; | |
| 3093 | const import_key = self.nonlazy_imports.keys()[entry.symbol]; | |
| 3094 | const import_ordinal = self.nonlazy_imports.values()[entry.symbol].dylib_ordinal; | |
| 5412 | for (self.got_entries.items) |entry, i| { | |
| 5413 | if (entry.where == .local) continue; | |
| 5414 | ||
| 5415 | const sym = self.imports.items[entry.where_index]; | |
| 3095 | 5416 | try pointers.append(.{ |
| 3096 | .offset = base_offset + entry.index * @sizeOf(u64), | |
| 5417 | .offset = base_offset + i * @sizeOf(u64), | |
| 3097 | 5418 | .segment_id = segment_id, |
| 3098 | .dylib_ordinal = import_ordinal, | |
| 3099 | .name = import_key, | |
| 5419 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 5420 | .name = self.getString(sym.n_strx), | |
| 3100 | 5421 | }); |
| 3101 | 5422 | } |
| 3102 | 5423 | } |
| 3103 | 5424 | |
| 5425 | { | |
| 5426 | var it = self.blocks.iterator(); | |
| 5427 | while (it.next()) |entry| { | |
| 5428 | const match = entry.key_ptr.*; | |
| 5429 | var block: *TextBlock = entry.value_ptr.*; | |
| 5430 | ||
| 5431 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 5432 | ||
| 5433 | const seg = self.load_commands.items[match.seg].Segment; | |
| 5434 | ||
| 5435 | while (true) { | |
| 5436 | const sym = self.locals.items[block.local_sym_index]; | |
| 5437 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 5438 | ||
| 5439 | for (block.bindings.items) |binding| { | |
| 5440 | const bind_sym = self.imports.items[binding.local_sym_index]; | |
| 5441 | try pointers.append(.{ | |
| 5442 | .offset = binding.offset + base_offset, | |
| 5443 | .segment_id = match.seg, | |
| 5444 | .dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc), | |
| 5445 | .name = self.getString(bind_sym.n_strx), | |
| 5446 | }); | |
| 5447 | } | |
| 5448 | ||
| 5449 | if (block.prev) |prev| { | |
| 5450 | block = prev; | |
| 5451 | } else break; | |
| 5452 | } | |
| 5453 | } | |
| 5454 | } | |
| 5455 | ||
| 3104 | 5456 | const size = try bind.bindInfoSize(pointers.items); |
| 3105 | 5457 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 3106 | 5458 | defer self.base.allocator.free(buffer); |
| ... | ... | @@ -3126,7 +5478,7 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 3126 | 5478 | self.binding_info_dirty = false; |
| 3127 | 5479 | } |
| 3128 | 5480 | |
| 3129 | fn writeLazyBindingInfoTable(self: *MachO) !void { | |
| 5481 | fn writeLazyBindInfoTable(self: *MachO) !void { | |
| 3130 | 5482 | if (!self.lazy_binding_info_dirty) return; |
| 3131 | 5483 | |
| 3132 | 5484 | const tracy = trace(@src()); |
| ... | ... | @@ -3136,21 +5488,20 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 3136 | 5488 | defer pointers.deinit(); |
| 3137 | 5489 | |
| 3138 | 5490 | if (self.la_symbol_ptr_section_index) |idx| { |
| 3139 | try pointers.ensureCapacity(self.lazy_imports.count()); | |
| 3140 | 5491 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 3141 | 5492 | const sect = seg.sections.items[idx]; |
| 3142 | 5493 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 3143 | 5494 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); |
| 3144 | 5495 | |
| 3145 | const slice = self.lazy_imports.entries.slice(); | |
| 3146 | const keys = slice.items(.key); | |
| 3147 | const values = slice.items(.value); | |
| 3148 | for (keys) |*key, i| { | |
| 5496 | try pointers.ensureUnusedCapacity(self.stubs.items.len); | |
| 5497 | ||
| 5498 | for (self.stubs.items) |import_id, i| { | |
| 5499 | const sym = self.imports.items[import_id]; | |
| 3149 | 5500 | pointers.appendAssumeCapacity(.{ |
| 3150 | .offset = base_offset + values[i].index * @sizeOf(u64), | |
| 5501 | .offset = base_offset + i * @sizeOf(u64), | |
| 3151 | 5502 | .segment_id = segment_id, |
| 3152 | .dylib_ordinal = values[i].dylib_ordinal, | |
| 3153 | .name = key.*, | |
| 5503 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 5504 | .name = self.getString(sym.n_strx), | |
| 3154 | 5505 | }); |
| 3155 | 5506 | } |
| 3156 | 5507 | } |
| ... | ... | @@ -3182,7 +5533,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 3182 | 5533 | } |
| 3183 | 5534 | |
| 3184 | 5535 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3185 | if (self.lazy_imports.count() == 0) return; | |
| 5536 | if (self.stubs.items.len == 0) return; | |
| 3186 | 5537 | |
| 3187 | 5538 | var stream = std.io.fixedBufferStream(buffer); |
| 3188 | 5539 | var reader = stream.reader(); |
| ... | ... | @@ -3227,7 +5578,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3227 | 5578 | else => {}, |
| 3228 | 5579 | } |
| 3229 | 5580 | } |
| 3230 | assert(self.lazy_imports.count() <= offsets.items.len); | |
| 5581 | assert(self.stubs.items.len <= offsets.items.len); | |
| 3231 | 5582 | |
| 3232 | 5583 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 3233 | 5584 | .x86_64 => 10, |
| ... | ... | @@ -3240,34 +5591,51 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3240 | 5591 | else => unreachable, |
| 3241 | 5592 | }; |
| 3242 | 5593 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 3243 | for (offsets.items[0..self.lazy_imports.count()]) |offset, i| { | |
| 3244 | const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off; | |
| 3245 | mem.writeIntLittle(u32, &buf, offset); | |
| 5594 | for (self.stubs.items) |_, index| { | |
| 5595 | const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; | |
| 5596 | mem.writeIntLittle(u32, &buf, offsets.items[index]); | |
| 3246 | 5597 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| 3247 | 5598 | } |
| 3248 | 5599 | } |
| 3249 | 5600 | |
| 3250 | 5601 | fn writeStringTable(self: *MachO) !void { |
| 3251 | if (!self.string_table_dirty) return; | |
| 5602 | if (!self.strtab_dirty) return; | |
| 3252 | 5603 | |
| 3253 | 5604 | const tracy = trace(@src()); |
| 3254 | 5605 | defer tracy.end(); |
| 3255 | 5606 | |
| 3256 | 5607 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 3257 | 5608 | const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); |
| 3258 | const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64)); | |
| 5609 | const needed_size = mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)); | |
| 3259 | 5610 | |
| 3260 | if (needed_size > allocated_size or self.string_table_needs_relocation) { | |
| 5611 | if (needed_size > allocated_size or self.strtab_needs_relocation) { | |
| 3261 | 5612 | symtab.strsize = 0; |
| 3262 | 5613 | symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff)); |
| 3263 | self.string_table_needs_relocation = false; | |
| 5614 | self.strtab_needs_relocation = false; | |
| 3264 | 5615 | } |
| 3265 | 5616 | symtab.strsize = @intCast(u32, needed_size); |
| 3266 | 5617 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 3267 | 5618 | |
| 3268 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | |
| 5619 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); | |
| 3269 | 5620 | self.load_commands_dirty = true; |
| 3270 | self.string_table_dirty = false; | |
| 5621 | self.strtab_dirty = false; | |
| 5622 | } | |
| 5623 | ||
| 5624 | fn writeStringTableZld(self: *MachO) !void { | |
| 5625 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 5626 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 5627 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 5628 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); | |
| 5629 | seg.inner.filesize += symtab.strsize; | |
| 5630 | ||
| 5631 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | |
| 5632 | ||
| 5633 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); | |
| 5634 | ||
| 5635 | if (symtab.strsize > self.strtab.items.len and self.base.options.target.cpu.arch == .x86_64) { | |
| 5636 | // This is the last section, so we need to pad it out. | |
| 5637 | try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | |
| 5638 | } | |
| 3271 | 5639 | } |
| 3272 | 5640 | |
| 3273 | 5641 | fn updateLinkeditSegmentSizes(self: *MachO) !void { |
| ... | ... | @@ -3334,24 +5702,57 @@ fn writeLoadCommands(self: *MachO) !void { |
| 3334 | 5702 | } |
| 3335 | 5703 | |
| 3336 | 5704 | const off = @sizeOf(macho.mach_header_64); |
| 5705 | ||
| 3337 | 5706 | log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); |
| 5707 | ||
| 3338 | 5708 | try self.base.file.?.pwriteAll(buffer, off); |
| 3339 | 5709 | self.load_commands_dirty = false; |
| 3340 | 5710 | } |
| 3341 | 5711 | |
| 3342 | 5712 | /// Writes Mach-O file header. |
| 3343 | 5713 | fn writeHeader(self: *MachO) !void { |
| 3344 | if (!self.header_dirty) return; | |
| 5714 | var header = commands.emptyHeader(.{ | |
| 5715 | .flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL, | |
| 5716 | }); | |
| 5717 | ||
| 5718 | switch (self.base.options.target.cpu.arch) { | |
| 5719 | .aarch64 => { | |
| 5720 | header.cputype = macho.CPU_TYPE_ARM64; | |
| 5721 | header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; | |
| 5722 | }, | |
| 5723 | .x86_64 => { | |
| 5724 | header.cputype = macho.CPU_TYPE_X86_64; | |
| 5725 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; | |
| 5726 | }, | |
| 5727 | else => return error.UnsupportedCpuArchitecture, | |
| 5728 | } | |
| 5729 | ||
| 5730 | switch (self.base.options.output_mode) { | |
| 5731 | .Exe => { | |
| 5732 | header.filetype = macho.MH_EXECUTE; | |
| 5733 | }, | |
| 5734 | .Lib => { | |
| 5735 | // By this point, it can only be a dylib. | |
| 5736 | header.filetype = macho.MH_DYLIB; | |
| 5737 | header.flags |= macho.MH_NO_REEXPORTED_DYLIBS; | |
| 5738 | }, | |
| 5739 | else => unreachable, | |
| 5740 | } | |
| 5741 | ||
| 5742 | if (self.tlv_section_index) |_| { | |
| 5743 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 5744 | } | |
| 5745 | ||
| 5746 | header.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 5747 | header.sizeofcmds = 0; | |
| 3345 | 5748 | |
| 3346 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 3347 | var sizeofcmds: u32 = 0; | |
| 3348 | 5749 | for (self.load_commands.items) |cmd| { |
| 3349 | sizeofcmds += cmd.cmdsize(); | |
| 5750 | header.sizeofcmds += cmd.cmdsize(); | |
| 3350 | 5751 | } |
| 3351 | self.header.?.sizeofcmds = sizeofcmds; | |
| 3352 | log.debug("writing Mach-O header {}", .{self.header.?}); | |
| 3353 | try self.base.file.?.pwriteAll(mem.asBytes(&self.header.?), 0); | |
| 3354 | self.header_dirty = false; | |
| 5752 | ||
| 5753 | log.debug("writing Mach-O header {}", .{header}); | |
| 5754 | ||
| 5755 | try self.base.file.?.pwriteAll(mem.asBytes(&header), 0); | |
| 3355 | 5756 | } |
| 3356 | 5757 | |
| 3357 | 5758 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| ... | ... | @@ -3359,3 +5760,179 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 3359 | 5760 | return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch |
| 3360 | 5761 | std.math.maxInt(@TypeOf(actual_size)); |
| 3361 | 5762 | } |
| 5763 | ||
| 5764 | pub fn makeString(self: *MachO, string: []const u8) !u32 { | |
| 5765 | if (self.strtab_dir.getAdapted(@as([]const u8, string), StringSliceAdapter{ .strtab = &self.strtab })) |off| { | |
| 5766 | log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off }); | |
| 5767 | return off; | |
| 5768 | } | |
| 5769 | ||
| 5770 | try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1); | |
| 5771 | const new_off = @intCast(u32, self.strtab.items.len); | |
| 5772 | ||
| 5773 | log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off }); | |
| 5774 | ||
| 5775 | self.strtab.appendSliceAssumeCapacity(string); | |
| 5776 | self.strtab.appendAssumeCapacity(0); | |
| 5777 | ||
| 5778 | try self.strtab_dir.putContext(self.base.allocator, new_off, new_off, StringIndexContext{ | |
| 5779 | .strtab = &self.strtab, | |
| 5780 | }); | |
| 5781 | ||
| 5782 | return new_off; | |
| 5783 | } | |
| 5784 | ||
| 5785 | pub fn getString(self: *MachO, off: u32) []const u8 { | |
| 5786 | assert(off < self.strtab.items.len); | |
| 5787 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); | |
| 5788 | } | |
| 5789 | ||
| 5790 | pub fn symbolIsStab(sym: macho.nlist_64) bool { | |
| 5791 | return (macho.N_STAB & sym.n_type) != 0; | |
| 5792 | } | |
| 5793 | ||
| 5794 | pub fn symbolIsPext(sym: macho.nlist_64) bool { | |
| 5795 | return (macho.N_PEXT & sym.n_type) != 0; | |
| 5796 | } | |
| 5797 | ||
| 5798 | pub fn symbolIsExt(sym: macho.nlist_64) bool { | |
| 5799 | return (macho.N_EXT & sym.n_type) != 0; | |
| 5800 | } | |
| 5801 | ||
| 5802 | pub fn symbolIsSect(sym: macho.nlist_64) bool { | |
| 5803 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5804 | return type_ == macho.N_SECT; | |
| 5805 | } | |
| 5806 | ||
| 5807 | pub fn symbolIsUndf(sym: macho.nlist_64) bool { | |
| 5808 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5809 | return type_ == macho.N_UNDF; | |
| 5810 | } | |
| 5811 | ||
| 5812 | pub fn symbolIsIndr(sym: macho.nlist_64) bool { | |
| 5813 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5814 | return type_ == macho.N_INDR; | |
| 5815 | } | |
| 5816 | ||
| 5817 | pub fn symbolIsAbs(sym: macho.nlist_64) bool { | |
| 5818 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5819 | return type_ == macho.N_ABS; | |
| 5820 | } | |
| 5821 | ||
| 5822 | pub fn symbolIsWeakDef(sym: macho.nlist_64) bool { | |
| 5823 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; | |
| 5824 | } | |
| 5825 | ||
| 5826 | pub fn symbolIsWeakRef(sym: macho.nlist_64) bool { | |
| 5827 | return (sym.n_desc & macho.N_WEAK_REF) != 0; | |
| 5828 | } | |
| 5829 | ||
| 5830 | pub fn symbolIsTentative(sym: macho.nlist_64) bool { | |
| 5831 | if (!symbolIsUndf(sym)) return false; | |
| 5832 | return sym.n_value != 0; | |
| 5833 | } | |
| 5834 | ||
| 5835 | pub fn symbolIsNull(sym: macho.nlist_64) bool { | |
| 5836 | return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0; | |
| 5837 | } | |
| 5838 | ||
| 5839 | pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool { | |
| 5840 | if (!symbolIsSect(sym)) return false; | |
| 5841 | if (symbolIsExt(sym)) return false; | |
| 5842 | return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); | |
| 5843 | } | |
| 5844 | ||
| 5845 | fn packDylibOrdinal(ordinal: u16) u16 { | |
| 5846 | return ordinal * macho.N_SYMBOL_RESOLVER; | |
| 5847 | } | |
| 5848 | ||
| 5849 | fn unpackDylibOrdinal(pack: u16) u16 { | |
| 5850 | return @divExact(pack, macho.N_SYMBOL_RESOLVER); | |
| 5851 | } | |
| 5852 | ||
| 5853 | pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize { | |
| 5854 | if (!@hasDecl(@TypeOf(predicate), "predicate")) | |
| 5855 | @compileError("Predicate is required to define fn predicate(@This(), T) bool"); | |
| 5856 | ||
| 5857 | if (start == haystack.len) return start; | |
| 5858 | ||
| 5859 | var i = start; | |
| 5860 | while (i < haystack.len) : (i += 1) { | |
| 5861 | if (predicate.predicate(haystack[i])) break; | |
| 5862 | } | |
| 5863 | return i; | |
| 5864 | } | |
| 5865 | ||
| 5866 | fn createSectionOrdinal(self: *MachO, match: MatchingSection) !void { | |
| 5867 | if (self.section_to_ordinal.contains(match)) return; | |
| 5868 | const ordinal = @intCast(u8, self.section_ordinals.items.len); | |
| 5869 | try self.section_ordinals.append(self.base.allocator, match); | |
| 5870 | try self.section_to_ordinal.putNoClobber(self.base.allocator, match, ordinal); | |
| 5871 | } | |
| 5872 | ||
| 5873 | fn printSymtabAndTextBlock(self: *MachO) void { | |
| 5874 | log.debug("locals", .{}); | |
| 5875 | for (self.locals.items) |sym, id| { | |
| 5876 | log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 5877 | } | |
| 5878 | ||
| 5879 | log.debug("globals", .{}); | |
| 5880 | for (self.globals.items) |sym, id| { | |
| 5881 | log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 5882 | } | |
| 5883 | ||
| 5884 | log.debug("tentatives", .{}); | |
| 5885 | for (self.tentatives.items) |sym, id| { | |
| 5886 | log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 5887 | } | |
| 5888 | ||
| 5889 | log.debug("undefines", .{}); | |
| 5890 | for (self.undefs.items) |sym, id| { | |
| 5891 | log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 5892 | } | |
| 5893 | ||
| 5894 | log.debug("imports", .{}); | |
| 5895 | for (self.imports.items) |sym, id| { | |
| 5896 | log.debug(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 5897 | } | |
| 5898 | ||
| 5899 | { | |
| 5900 | log.debug("symbol resolver", .{}); | |
| 5901 | var it = self.symbol_resolver.keyIterator(); | |
| 5902 | while (it.next()) |key_ptr| { | |
| 5903 | const sym_name = self.getString(key_ptr.*); | |
| 5904 | log.debug(" {s} => {}", .{ sym_name, self.symbol_resolver.get(key_ptr.*).? }); | |
| 5905 | } | |
| 5906 | } | |
| 5907 | ||
| 5908 | log.debug("mappings", .{}); | |
| 5909 | for (self.objects.items) |object| { | |
| 5910 | log.debug(" in object {s}", .{object.name.?}); | |
| 5911 | for (object.symtab.items) |sym, sym_id| { | |
| 5912 | if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| { | |
| 5913 | log.debug(" | {d} => {d}", .{ sym_id, local_id }); | |
| 5914 | } else { | |
| 5915 | log.debug(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) }); | |
| 5916 | } | |
| 5917 | } | |
| 5918 | } | |
| 5919 | ||
| 5920 | { | |
| 5921 | var it = self.blocks.iterator(); | |
| 5922 | while (it.next()) |entry| { | |
| 5923 | const seg = self.load_commands.items[entry.key_ptr.seg].Segment; | |
| 5924 | const sect = seg.sections.items[entry.key_ptr.sect]; | |
| 5925 | ||
| 5926 | var block: *TextBlock = entry.value_ptr.*; | |
| 5927 | ||
| 5928 | log.debug("\n\n{s},{s} contents:", .{ commands.segmentName(sect), commands.sectionName(sect) }); | |
| 5929 | log.debug("{}", .{sect}); | |
| 5930 | log.debug("{}", .{block}); | |
| 5931 | ||
| 5932 | while (block.prev) |prev| { | |
| 5933 | block = prev; | |
| 5934 | log.debug("{}", .{block}); | |
| 5935 | } | |
| 5936 | } | |
| 5937 | } | |
| 5938 | } |
src/link/MachO/Archive.zig+6| ... | ... | @@ -81,6 +81,11 @@ const ar_hdr = extern struct { |
| 81 | 81 | } |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | fn date(self: ar_hdr) !u64 { | |
| 85 | const value = getValue(&self.ar_date); | |
| 86 | return std.fmt.parseInt(u64, value, 10); | |
| 87 | } | |
| 88 | ||
| 84 | 89 | fn size(self: ar_hdr) !u32 { |
| 85 | 90 | const value = getValue(&self.ar_size); |
| 86 | 91 | return std.fmt.parseInt(u32, value, 10); |
| ... | ... | @@ -264,6 +269,7 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { |
| 264 | 269 | .file = try fs.cwd().openFile(self.name.?, .{}), |
| 265 | 270 | .name = name, |
| 266 | 271 | .file_offset = @intCast(u32, try reader.context.getPos()), |
| 272 | .mtime = try self.header.?.date(), | |
| 267 | 273 | }; |
| 268 | 274 | try object.parse(); |
| 269 | 275 | try reader.context.seekTo(0); |
src/link/MachO/DebugSymbols.zig+42-60| ... | ... | @@ -3,7 +3,7 @@ const DebugSymbols = @This(); |
| 3 | 3 | const std = @import("std"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const fs = std.fs; |
| 6 | const log = std.log.scoped(.link); | |
| 6 | const log = std.log.scoped(.dsym); | |
| 7 | 7 | const macho = std.macho; |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const DW = std.dwarf; |
| ... | ... | @@ -27,9 +27,6 @@ const page_size: u16 = 0x1000; |
| 27 | 27 | base: *MachO, |
| 28 | 28 | file: fs.File, |
| 29 | 29 | |
| 30 | /// Mach header | |
| 31 | header: ?macho.mach_header_64 = null, | |
| 32 | ||
| 33 | 30 | /// Table of all load commands |
| 34 | 31 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 35 | 32 | /// __PAGEZERO segment |
| ... | ... | @@ -78,9 +75,8 @@ dbg_info_decl_last: ?*TextBlock = null, |
| 78 | 75 | /// Table of debug symbol names aka the debug string table. |
| 79 | 76 | debug_string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 80 | 77 | |
| 81 | header_dirty: bool = false, | |
| 82 | 78 | load_commands_dirty: bool = false, |
| 83 | string_table_dirty: bool = false, | |
| 79 | strtab_dirty: bool = false, | |
| 84 | 80 | debug_string_table_dirty: bool = false, |
| 85 | 81 | debug_abbrev_section_dirty: bool = false, |
| 86 | 82 | debug_aranges_section_dirty: bool = false, |
| ... | ... | @@ -106,26 +102,10 @@ const min_nop_size = 2; |
| 106 | 102 | /// You must call this function *after* `MachO.populateMissingMetadata()` |
| 107 | 103 | /// has been called to get a viable debug symbols output. |
| 108 | 104 | pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void { |
| 109 | if (self.header == null) { | |
| 110 | const base_header = self.base.header.?; | |
| 111 | var header: macho.mach_header_64 = undefined; | |
| 112 | header.magic = macho.MH_MAGIC_64; | |
| 113 | header.cputype = base_header.cputype; | |
| 114 | header.cpusubtype = base_header.cpusubtype; | |
| 115 | header.filetype = macho.MH_DSYM; | |
| 116 | // These will get populated at the end of flushing the results to file. | |
| 117 | header.ncmds = 0; | |
| 118 | header.sizeofcmds = 0; | |
| 119 | header.flags = 0; | |
| 120 | header.reserved = 0; | |
| 121 | self.header = header; | |
| 122 | self.header_dirty = true; | |
| 123 | } | |
| 124 | 105 | if (self.uuid_cmd_index == null) { |
| 125 | 106 | const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?]; |
| 126 | 107 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 127 | 108 | try self.load_commands.append(allocator, base_cmd); |
| 128 | self.header_dirty = true; | |
| 129 | 109 | self.load_commands_dirty = true; |
| 130 | 110 | } |
| 131 | 111 | if (self.symtab_cmd_index == null) { |
| ... | ... | @@ -134,11 +114,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 134 | 114 | const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64); |
| 135 | 115 | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); |
| 136 | 116 | |
| 137 | log.debug("found dSym symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); | |
| 117 | log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); | |
| 138 | 118 | |
| 139 | 119 | const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1); |
| 140 | 120 | |
| 141 | log.debug("found dSym string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize }); | |
| 121 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize }); | |
| 142 | 122 | |
| 143 | 123 | try self.load_commands.append(allocator, .{ |
| 144 | 124 | .Symtab = .{ |
| ... | ... | @@ -150,16 +130,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 150 | 130 | .strsize = base_cmd.strsize, |
| 151 | 131 | }, |
| 152 | 132 | }); |
| 153 | self.header_dirty = true; | |
| 154 | 133 | self.load_commands_dirty = true; |
| 155 | self.string_table_dirty = true; | |
| 134 | self.strtab_dirty = true; | |
| 156 | 135 | } |
| 157 | 136 | if (self.pagezero_segment_cmd_index == null) { |
| 158 | 137 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 159 | 138 | const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment; |
| 160 | 139 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| 161 | 140 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 162 | self.header_dirty = true; | |
| 163 | 141 | self.load_commands_dirty = true; |
| 164 | 142 | } |
| 165 | 143 | if (self.text_segment_cmd_index == null) { |
| ... | ... | @@ -167,7 +145,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 167 | 145 | const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment; |
| 168 | 146 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| 169 | 147 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 170 | self.header_dirty = true; | |
| 171 | 148 | self.load_commands_dirty = true; |
| 172 | 149 | } |
| 173 | 150 | if (self.data_const_segment_cmd_index == null) outer: { |
| ... | ... | @@ -176,7 +153,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 176 | 153 | const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].Segment; |
| 177 | 154 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| 178 | 155 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 179 | self.header_dirty = true; | |
| 180 | 156 | self.load_commands_dirty = true; |
| 181 | 157 | } |
| 182 | 158 | if (self.data_segment_cmd_index == null) outer: { |
| ... | ... | @@ -185,7 +161,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 185 | 161 | const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment; |
| 186 | 162 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| 187 | 163 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 188 | self.header_dirty = true; | |
| 189 | 164 | self.load_commands_dirty = true; |
| 190 | 165 | } |
| 191 | 166 | if (self.linkedit_segment_cmd_index == null) { |
| ... | ... | @@ -196,7 +171,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 196 | 171 | cmd.inner.fileoff = self.linkedit_off; |
| 197 | 172 | cmd.inner.filesize = self.linkedit_size; |
| 198 | 173 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 199 | self.header_dirty = true; | |
| 200 | 174 | self.load_commands_dirty = true; |
| 201 | 175 | } |
| 202 | 176 | if (self.dwarf_segment_cmd_index == null) { |
| ... | ... | @@ -208,7 +182,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 208 | 182 | const off = linkedit.inner.fileoff + linkedit.inner.filesize; |
| 209 | 183 | const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize; |
| 210 | 184 | |
| 211 | log.debug("found dSym __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); | |
| 185 | log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); | |
| 212 | 186 | |
| 213 | 187 | try self.load_commands.append(allocator, .{ |
| 214 | 188 | .Segment = SegmentCommand.empty("__DWARF", .{ |
| ... | ... | @@ -218,7 +192,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 218 | 192 | .filesize = needed_size, |
| 219 | 193 | }), |
| 220 | 194 | }); |
| 221 | self.header_dirty = true; | |
| 222 | 195 | self.load_commands_dirty = true; |
| 223 | 196 | } |
| 224 | 197 | if (self.debug_str_section_index == null) { |
| ... | ... | @@ -232,7 +205,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 232 | 205 | .offset = @intCast(u32, dwarf_segment.inner.fileoff), |
| 233 | 206 | .@"align" = 1, |
| 234 | 207 | }); |
| 235 | self.header_dirty = true; | |
| 236 | 208 | self.load_commands_dirty = true; |
| 237 | 209 | self.debug_string_table_dirty = true; |
| 238 | 210 | } |
| ... | ... | @@ -244,7 +216,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 244 | 216 | const p_align = 1; |
| 245 | 217 | const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); |
| 246 | 218 | |
| 247 | log.debug("found dSym __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 219 | log.debug("found __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 248 | 220 | |
| 249 | 221 | try dwarf_segment.addSection(allocator, "__debug_info", .{ |
| 250 | 222 | .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, |
| ... | ... | @@ -252,7 +224,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 252 | 224 | .offset = @intCast(u32, off), |
| 253 | 225 | .@"align" = p_align, |
| 254 | 226 | }); |
| 255 | self.header_dirty = true; | |
| 256 | 227 | self.load_commands_dirty = true; |
| 257 | 228 | self.debug_info_header_dirty = true; |
| 258 | 229 | } |
| ... | ... | @@ -264,7 +235,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 264 | 235 | const p_align = 1; |
| 265 | 236 | const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); |
| 266 | 237 | |
| 267 | log.debug("found dSym __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 238 | log.debug("found __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 268 | 239 | |
| 269 | 240 | try dwarf_segment.addSection(allocator, "__debug_abbrev", .{ |
| 270 | 241 | .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, |
| ... | ... | @@ -272,7 +243,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 272 | 243 | .offset = @intCast(u32, off), |
| 273 | 244 | .@"align" = p_align, |
| 274 | 245 | }); |
| 275 | self.header_dirty = true; | |
| 276 | 246 | self.load_commands_dirty = true; |
| 277 | 247 | self.debug_abbrev_section_dirty = true; |
| 278 | 248 | } |
| ... | ... | @@ -284,7 +254,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 284 | 254 | const p_align = 16; |
| 285 | 255 | const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); |
| 286 | 256 | |
| 287 | log.debug("found dSym __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 257 | log.debug("found __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 288 | 258 | |
| 289 | 259 | try dwarf_segment.addSection(allocator, "__debug_aranges", .{ |
| 290 | 260 | .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, |
| ... | ... | @@ -292,7 +262,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 292 | 262 | .offset = @intCast(u32, off), |
| 293 | 263 | .@"align" = p_align, |
| 294 | 264 | }); |
| 295 | self.header_dirty = true; | |
| 296 | 265 | self.load_commands_dirty = true; |
| 297 | 266 | self.debug_aranges_section_dirty = true; |
| 298 | 267 | } |
| ... | ... | @@ -304,7 +273,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 304 | 273 | const p_align = 1; |
| 305 | 274 | const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); |
| 306 | 275 | |
| 307 | log.debug("found dSym __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 276 | log.debug("found __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); | |
| 308 | 277 | |
| 309 | 278 | try dwarf_segment.addSection(allocator, "__debug_line", .{ |
| 310 | 279 | .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, |
| ... | ... | @@ -312,7 +281,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 312 | 281 | .offset = @intCast(u32, off), |
| 313 | 282 | .@"align" = p_align, |
| 314 | 283 | }); |
| 315 | self.header_dirty = true; | |
| 316 | 284 | self.load_commands_dirty = true; |
| 317 | 285 | self.debug_line_header_dirty = true; |
| 318 | 286 | } |
| ... | ... | @@ -624,9 +592,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt |
| 624 | 592 | try self.writeLoadCommands(allocator); |
| 625 | 593 | try self.writeHeader(); |
| 626 | 594 | |
| 627 | assert(!self.header_dirty); | |
| 628 | 595 | assert(!self.load_commands_dirty); |
| 629 | assert(!self.string_table_dirty); | |
| 596 | assert(!self.strtab_dirty); | |
| 630 | 597 | assert(!self.debug_abbrev_section_dirty); |
| 631 | 598 | assert(!self.debug_aranges_section_dirty); |
| 632 | 599 | assert(!self.debug_string_table_dirty); |
| ... | ... | @@ -716,23 +683,38 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void { |
| 716 | 683 | } |
| 717 | 684 | |
| 718 | 685 | const off = @sizeOf(macho.mach_header_64); |
| 719 | log.debug("writing {} dSym load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); | |
| 686 | log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); | |
| 720 | 687 | try self.file.pwriteAll(buffer, off); |
| 721 | 688 | self.load_commands_dirty = false; |
| 722 | 689 | } |
| 723 | 690 | |
| 724 | 691 | fn writeHeader(self: *DebugSymbols) !void { |
| 725 | if (!self.header_dirty) return; | |
| 692 | var header = emptyHeader(.{ | |
| 693 | .filetype = macho.MH_DSYM, | |
| 694 | }); | |
| 695 | ||
| 696 | switch (self.base.base.options.target.cpu.arch) { | |
| 697 | .aarch64 => { | |
| 698 | header.cputype = macho.CPU_TYPE_ARM64; | |
| 699 | header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; | |
| 700 | }, | |
| 701 | .x86_64 => { | |
| 702 | header.cputype = macho.CPU_TYPE_X86_64; | |
| 703 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; | |
| 704 | }, | |
| 705 | else => return error.UnsupportedCpuArchitecture, | |
| 706 | } | |
| 707 | ||
| 708 | header.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 709 | header.sizeofcmds = 0; | |
| 726 | 710 | |
| 727 | self.header.?.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 728 | var sizeofcmds: u32 = 0; | |
| 729 | 711 | for (self.load_commands.items) |cmd| { |
| 730 | sizeofcmds += cmd.cmdsize(); | |
| 712 | header.sizeofcmds += cmd.cmdsize(); | |
| 731 | 713 | } |
| 732 | self.header.?.sizeofcmds = sizeofcmds; | |
| 733 | log.debug("writing Mach-O dSym header {}", .{self.header.?}); | |
| 734 | try self.file.pwriteAll(mem.asBytes(&self.header.?), 0); | |
| 735 | self.header_dirty = false; | |
| 714 | ||
| 715 | log.debug("writing Mach-O header {}", .{header}); | |
| 716 | ||
| 717 | try self.file.pwriteAll(mem.asBytes(&header), 0); | |
| 736 | 718 | } |
| 737 | 719 | |
| 738 | 720 | fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { |
| ... | ... | @@ -798,7 +780,7 @@ fn relocateSymbolTable(self: *DebugSymbols) !void { |
| 798 | 780 | const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); |
| 799 | 781 | |
| 800 | 782 | assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); // TODO expand LINKEDIT segment. |
| 801 | log.debug("relocating dSym symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ | |
| 783 | log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ | |
| 802 | 784 | symtab.symoff, |
| 803 | 785 | symtab.symoff + existing_size, |
| 804 | 786 | new_symoff, |
| ... | ... | @@ -820,30 +802,30 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void { |
| 820 | 802 | try self.relocateSymbolTable(); |
| 821 | 803 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 822 | 804 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 823 | log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off }); | |
| 805 | log.debug("writing local symbol {} at 0x{x}", .{ index, off }); | |
| 824 | 806 | try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off); |
| 825 | 807 | } |
| 826 | 808 | |
| 827 | 809 | fn writeStringTable(self: *DebugSymbols) !void { |
| 828 | if (!self.string_table_dirty) return; | |
| 810 | if (!self.strtab_dirty) return; | |
| 829 | 811 | |
| 830 | 812 | const tracy = trace(@src()); |
| 831 | 813 | defer tracy.end(); |
| 832 | 814 | |
| 833 | 815 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 834 | 816 | const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); |
| 835 | const needed_size = mem.alignForwardGeneric(u64, self.base.string_table.items.len, @alignOf(u64)); | |
| 817 | const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64)); | |
| 836 | 818 | |
| 837 | 819 | if (needed_size > allocated_size) { |
| 838 | 820 | symtab.strsize = 0; |
| 839 | 821 | symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); |
| 840 | 822 | } |
| 841 | 823 | symtab.strsize = @intCast(u32, needed_size); |
| 842 | log.debug("writing dSym string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | |
| 824 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | |
| 843 | 825 | |
| 844 | try self.file.pwriteAll(self.base.string_table.items, symtab.stroff); | |
| 826 | try self.file.pwriteAll(self.base.strtab.items, symtab.stroff); | |
| 845 | 827 | self.load_commands_dirty = true; |
| 846 | self.string_table_dirty = false; | |
| 828 | self.strtab_dirty = false; | |
| 847 | 829 | } |
| 848 | 830 | |
| 849 | 831 | pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { |
src/link/MachO/Dylib.zig+8-22| ... | ... | @@ -12,13 +12,12 @@ const fat = @import("fat.zig"); |
| 12 | 12 | |
| 13 | 13 | const Allocator = mem.Allocator; |
| 14 | 14 | const Arch = std.Target.Cpu.Arch; |
| 15 | const Symbol = @import("Symbol.zig"); | |
| 16 | 15 | const LibStub = @import("../tapi.zig").LibStub; |
| 16 | const MachO = @import("../MachO.zig"); | |
| 17 | 17 | |
| 18 | 18 | usingnamespace @import("commands.zig"); |
| 19 | 19 | |
| 20 | 20 | allocator: *Allocator, |
| 21 | ||
| 22 | 21 | arch: ?Arch = null, |
| 23 | 22 | header: ?macho.mach_header_64 = null, |
| 24 | 23 | file: ?fs.File = null, |
| ... | ... | @@ -146,7 +145,12 @@ pub const CreateOpts = struct { |
| 146 | 145 | id: ?Id = null, |
| 147 | 146 | }; |
| 148 | 147 | |
| 149 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib { | |
| 148 | pub fn createAndParseFromPath( | |
| 149 | allocator: *Allocator, | |
| 150 | arch: Arch, | |
| 151 | path: []const u8, | |
| 152 | opts: CreateOpts, | |
| 153 | ) Error!?[]*Dylib { | |
| 150 | 154 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 151 | 155 | error.FileNotFound => return null, |
| 152 | 156 | else => |e| return e, |
| ... | ... | @@ -320,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void { |
| 320 | 324 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); |
| 321 | 325 | |
| 322 | 326 | for (slice) |sym| { |
| 323 | const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym)); | |
| 327 | const add_to_symtab = MachO.symbolIsExt(sym) and (MachO.symbolIsSect(sym) or MachO.symbolIsIndr(sym)); | |
| 324 | 328 | |
| 325 | 329 | if (!add_to_symtab) continue; |
| 326 | 330 | |
| ... | ... | @@ -502,21 +506,3 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { |
| 502 | 506 | } |
| 503 | 507 | } |
| 504 | 508 | } |
| 505 | ||
| 506 | pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { | |
| 507 | if (!self.symbols.contains(sym_name)) return null; | |
| 508 | ||
| 509 | const name = try self.allocator.dupe(u8, sym_name); | |
| 510 | const proxy = try self.allocator.create(Symbol.Proxy); | |
| 511 | errdefer self.allocator.destroy(proxy); | |
| 512 | ||
| 513 | proxy.* = .{ | |
| 514 | .base = .{ | |
| 515 | .@"type" = .proxy, | |
| 516 | .name = name, | |
| 517 | }, | |
| 518 | .file = self, | |
| 519 | }; | |
| 520 | ||
| 521 | return &proxy.base; | |
| 522 | } |
src/link/MachO/Object.zig+574-228| ... | ... | @@ -7,14 +7,14 @@ const fs = std.fs; |
| 7 | 7 | const io = std.io; |
| 8 | 8 | const log = std.log.scoped(.object); |
| 9 | 9 | const macho = std.macho; |
| 10 | const math = std.math; | |
| 10 | 11 | const mem = std.mem; |
| 11 | const reloc = @import("reloc.zig"); | |
| 12 | const sort = std.sort; | |
| 12 | 13 | |
| 13 | 14 | const Allocator = mem.Allocator; |
| 14 | 15 | const Arch = std.Target.Cpu.Arch; |
| 15 | const Relocation = reloc.Relocation; | |
| 16 | const Symbol = @import("Symbol.zig"); | |
| 17 | const parseName = @import("Zld.zig").parseName; | |
| 16 | const MachO = @import("../MachO.zig"); | |
| 17 | const TextBlock = @import("TextBlock.zig"); | |
| 18 | 18 | |
| 19 | 19 | usingnamespace @import("commands.zig"); |
| 20 | 20 | |
| ... | ... | @@ -26,7 +26,6 @@ file_offset: ?u32 = null, |
| 26 | 26 | name: ?[]const u8 = null, |
| 27 | 27 | |
| 28 | 28 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 29 | sections: std.ArrayListUnmanaged(Section) = .{}, | |
| 30 | 29 | |
| 31 | 30 | segment_cmd_index: ?u16 = null, |
| 32 | 31 | symtab_cmd_index: ?u16 = null, |
| ... | ... | @@ -44,71 +43,23 @@ dwarf_debug_str_index: ?u16 = null, |
| 44 | 43 | dwarf_debug_line_index: ?u16 = null, |
| 45 | 44 | dwarf_debug_ranges_index: ?u16 = null, |
| 46 | 45 | |
| 47 | symbols: std.ArrayListUnmanaged(*Symbol) = .{}, | |
| 48 | initializers: std.ArrayListUnmanaged(*Symbol) = .{}, | |
| 46 | symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 47 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 49 | 48 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 50 | 49 | |
| 51 | tu_path: ?[]const u8 = null, | |
| 52 | tu_mtime: ?u64 = null, | |
| 50 | // Debug info | |
| 51 | debug_info: ?DebugInfo = null, | |
| 52 | tu_name: ?[]const u8 = null, | |
| 53 | tu_comp_dir: ?[]const u8 = null, | |
| 54 | mtime: ?u64 = null, | |
| 53 | 55 | |
| 54 | pub const Section = struct { | |
| 55 | inner: macho.section_64, | |
| 56 | code: []u8, | |
| 57 | relocs: ?[]*Relocation, | |
| 58 | target_map: ?struct { | |
| 59 | segment_id: u16, | |
| 60 | section_id: u16, | |
| 61 | offset: u32, | |
| 62 | } = null, | |
| 63 | ||
| 64 | pub fn deinit(self: *Section, allocator: *Allocator) void { | |
| 65 | allocator.free(self.code); | |
| 66 | ||
| 67 | if (self.relocs) |relocs| { | |
| 68 | for (relocs) |rel| { | |
| 69 | allocator.destroy(rel); | |
| 70 | } | |
| 71 | allocator.free(relocs); | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | pub fn segname(self: Section) []const u8 { | |
| 76 | return parseName(&self.inner.segname); | |
| 77 | } | |
| 78 | ||
| 79 | pub fn sectname(self: Section) []const u8 { | |
| 80 | return parseName(&self.inner.sectname); | |
| 81 | } | |
| 82 | ||
| 83 | pub fn flags(self: Section) u32 { | |
| 84 | return self.inner.flags; | |
| 85 | } | |
| 86 | ||
| 87 | pub fn sectionType(self: Section) u8 { | |
| 88 | return @truncate(u8, self.flags() & 0xff); | |
| 89 | } | |
| 90 | ||
| 91 | pub fn sectionAttrs(self: Section) u32 { | |
| 92 | return self.flags() & 0xffffff00; | |
| 93 | } | |
| 56 | text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{}, | |
| 57 | sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{}, | |
| 94 | 58 | |
| 95 | pub fn isCode(self: Section) bool { | |
| 96 | const attr = self.sectionAttrs(); | |
| 97 | return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0; | |
| 98 | } | |
| 99 | ||
| 100 | pub fn isDebug(self: Section) bool { | |
| 101 | return self.sectionAttrs() & macho.S_ATTR_DEBUG != 0; | |
| 102 | } | |
| 103 | ||
| 104 | pub fn dontDeadStrip(self: Section) bool { | |
| 105 | return self.sectionAttrs() & macho.S_ATTR_NO_DEAD_STRIP != 0; | |
| 106 | } | |
| 107 | ||
| 108 | pub fn dontDeadStripIfReferencesLive(self: Section) bool { | |
| 109 | return self.sectionAttrs() & macho.S_ATTR_LIVE_SUPPORT != 0; | |
| 110 | } | |
| 111 | }; | |
| 59 | // TODO symbol mapping and its inverse can probably be simple arrays | |
| 60 | // instead of hash maps. | |
| 61 | symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{}, | |
| 62 | reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{}, | |
| 112 | 63 | |
| 113 | 64 | const DebugInfo = struct { |
| 114 | 65 | inner: dwarf.DwarfInfo, |
| ... | ... | @@ -211,27 +162,28 @@ pub fn deinit(self: *Object) void { |
| 211 | 162 | lc.deinit(self.allocator); |
| 212 | 163 | } |
| 213 | 164 | self.load_commands.deinit(self.allocator); |
| 214 | ||
| 215 | for (self.sections.items) |*sect| { | |
| 216 | sect.deinit(self.allocator); | |
| 165 | self.data_in_code_entries.deinit(self.allocator); | |
| 166 | self.symtab.deinit(self.allocator); | |
| 167 | self.strtab.deinit(self.allocator); | |
| 168 | self.text_blocks.deinit(self.allocator); | |
| 169 | self.sections_as_symbols.deinit(self.allocator); | |
| 170 | self.symbol_mapping.deinit(self.allocator); | |
| 171 | self.reverse_symbol_mapping.deinit(self.allocator); | |
| 172 | ||
| 173 | if (self.debug_info) |*db| { | |
| 174 | db.deinit(self.allocator); | |
| 217 | 175 | } |
| 218 | self.sections.deinit(self.allocator); | |
| 219 | 176 | |
| 220 | for (self.symbols.items) |sym| { | |
| 221 | sym.deinit(self.allocator); | |
| 222 | self.allocator.destroy(sym); | |
| 177 | if (self.tu_name) |n| { | |
| 178 | self.allocator.free(n); | |
| 223 | 179 | } |
| 224 | self.symbols.deinit(self.allocator); | |
| 225 | 180 | |
| 226 | self.data_in_code_entries.deinit(self.allocator); | |
| 227 | self.initializers.deinit(self.allocator); | |
| 228 | ||
| 229 | if (self.name) |n| { | |
| 181 | if (self.tu_comp_dir) |n| { | |
| 230 | 182 | self.allocator.free(n); |
| 231 | 183 | } |
| 232 | 184 | |
| 233 | if (self.tu_path) |tu_path| { | |
| 234 | self.allocator.free(tu_path); | |
| 185 | if (self.name) |n| { | |
| 186 | self.allocator.free(n); | |
| 235 | 187 | } |
| 236 | 188 | } |
| 237 | 189 | |
| ... | ... | @@ -270,10 +222,8 @@ pub fn parse(self: *Object) !void { |
| 270 | 222 | self.header = header; |
| 271 | 223 | |
| 272 | 224 | try self.readLoadCommands(reader); |
| 273 | try self.parseSymbols(); | |
| 274 | try self.parseSections(); | |
| 225 | try self.parseSymtab(); | |
| 275 | 226 | try self.parseDataInCode(); |
| 276 | try self.parseInitializers(); | |
| 277 | 227 | try self.parseDebugInfo(); |
| 278 | 228 | } |
| 279 | 229 | |
| ... | ... | @@ -290,8 +240,8 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { |
| 290 | 240 | var seg = cmd.Segment; |
| 291 | 241 | for (seg.sections.items) |*sect, j| { |
| 292 | 242 | const index = @intCast(u16, j); |
| 293 | const segname = parseName(&sect.segname); | |
| 294 | const sectname = parseName(&sect.sectname); | |
| 243 | const segname = segmentName(sect.*); | |
| 244 | const sectname = sectionName(sect.*); | |
| 295 | 245 | if (mem.eql(u8, segname, "__DWARF")) { |
| 296 | 246 | if (mem.eql(u8, sectname, "__debug_info")) { |
| 297 | 247 | self.dwarf_debug_info_index = index; |
| ... | ... | @@ -345,153 +295,561 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { |
| 345 | 295 | } |
| 346 | 296 | } |
| 347 | 297 | |
| 348 | pub fn parseSections(self: *Object) !void { | |
| 349 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | |
| 350 | ||
| 351 | log.debug("parsing sections in {s}", .{self.name.?}); | |
| 352 | ||
| 353 | try self.sections.ensureCapacity(self.allocator, seg.sections.items.len); | |
| 298 | const NlistWithIndex = struct { | |
| 299 | nlist: macho.nlist_64, | |
| 300 | index: u32, | |
| 301 | ||
| 302 | fn lessThan(_: void, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { | |
| 303 | // We sort by type: defined < undefined, and | |
| 304 | // afterwards by address in each group. Normally, dysymtab should | |
| 305 | // be enough to guarantee the sort, but turns out not every compiler | |
| 306 | // is kind enough to specify the symbols in the correct order. | |
| 307 | if (MachO.symbolIsSect(lhs.nlist)) { | |
| 308 | if (MachO.symbolIsSect(rhs.nlist)) { | |
| 309 | // Same group, sort by address. | |
| 310 | return lhs.nlist.n_value < rhs.nlist.n_value; | |
| 311 | } else { | |
| 312 | return true; | |
| 313 | } | |
| 314 | } else { | |
| 315 | return false; | |
| 316 | } | |
| 317 | } | |
| 354 | 318 | |
| 355 | for (seg.sections.items) |sect| { | |
| 356 | log.debug("parsing section '{s},{s}'", .{ parseName(&sect.segname), parseName(&sect.sectname) }); | |
| 357 | // Read sections' code | |
| 358 | var code = try self.allocator.alloc(u8, @intCast(usize, sect.size)); | |
| 359 | _ = try self.file.?.preadAll(code, sect.offset); | |
| 319 | fn filterInSection(symbols: []NlistWithIndex, sect: macho.section_64) []NlistWithIndex { | |
| 320 | const Predicate = struct { | |
| 321 | addr: u64, | |
| 360 | 322 | |
| 361 | var section = Section{ | |
| 362 | .inner = sect, | |
| 363 | .code = code, | |
| 364 | .relocs = null, | |
| 323 | pub fn predicate(self: @This(), symbol: NlistWithIndex) bool { | |
| 324 | return symbol.nlist.n_value >= self.addr; | |
| 325 | } | |
| 365 | 326 | }; |
| 366 | 327 | |
| 367 | // Parse relocations | |
| 368 | if (sect.nreloc > 0) { | |
| 369 | var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); | |
| 370 | defer self.allocator.free(raw_relocs); | |
| 328 | const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); | |
| 329 | const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | |
| 371 | 330 | |
| 372 | _ = try self.file.?.preadAll(raw_relocs, sect.reloff); | |
| 331 | return symbols[start..end]; | |
| 332 | } | |
| 333 | }; | |
| 373 | 334 | |
| 374 | section.relocs = try reloc.parse( | |
| 375 | self.allocator, | |
| 376 | self.arch.?, | |
| 377 | section.code, | |
| 378 | mem.bytesAsSlice(macho.relocation_info, raw_relocs), | |
| 379 | self.symbols.items, | |
| 380 | ); | |
| 335 | fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry { | |
| 336 | const Predicate = struct { | |
| 337 | addr: u64, | |
| 338 | ||
| 339 | pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { | |
| 340 | return dice.offset >= self.addr; | |
| 381 | 341 | } |
| 342 | }; | |
| 382 | 343 | |
| 383 | self.sections.appendAssumeCapacity(section); | |
| 384 | } | |
| 344 | const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); | |
| 345 | const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); | |
| 346 | ||
| 347 | return dices[start..end]; | |
| 385 | 348 | } |
| 386 | 349 | |
| 387 | pub fn parseInitializers(self: *Object) !void { | |
| 388 | const index = self.mod_init_func_section_index orelse return; | |
| 389 | const section = self.sections.items[index]; | |
| 350 | const TextBlockParser = struct { | |
| 351 | allocator: *Allocator, | |
| 352 | section: macho.section_64, | |
| 353 | code: []u8, | |
| 354 | relocs: []macho.relocation_info, | |
| 355 | object: *Object, | |
| 356 | macho_file: *MachO, | |
| 357 | nlists: []NlistWithIndex, | |
| 358 | index: u32 = 0, | |
| 359 | match: MachO.MatchingSection, | |
| 360 | ||
| 361 | fn peek(self: *TextBlockParser) ?NlistWithIndex { | |
| 362 | return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; | |
| 363 | } | |
| 390 | 364 | |
| 391 | log.debug("parsing initializers in {s}", .{self.name.?}); | |
| 365 | const SeniorityContext = struct { | |
| 366 | object: *Object, | |
| 367 | }; | |
| 392 | 368 | |
| 393 | // Parse C++ initializers | |
| 394 | const relocs = section.relocs orelse unreachable; | |
| 395 | try self.initializers.ensureCapacity(self.allocator, relocs.len); | |
| 396 | for (relocs) |rel| { | |
| 397 | self.initializers.appendAssumeCapacity(rel.target.symbol); | |
| 369 | fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { | |
| 370 | if (!MachO.symbolIsExt(rhs.nlist)) { | |
| 371 | return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); | |
| 372 | } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) { | |
| 373 | return !MachO.symbolIsExt(lhs.nlist); | |
| 374 | } else { | |
| 375 | return false; | |
| 376 | } | |
| 398 | 377 | } |
| 399 | 378 | |
| 400 | mem.reverse(*Symbol, self.initializers.items); | |
| 401 | } | |
| 379 | pub fn next(self: *TextBlockParser) !?*TextBlock { | |
| 380 | if (self.index == self.nlists.len) return null; | |
| 402 | 381 | |
| 403 | pub fn parseSymbols(self: *Object) !void { | |
| 404 | const index = self.symtab_cmd_index orelse return; | |
| 405 | const symtab_cmd = self.load_commands.items[index].Symtab; | |
| 382 | var aliases = std.ArrayList(NlistWithIndex).init(self.allocator); | |
| 383 | defer aliases.deinit(); | |
| 406 | 384 | |
| 407 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); | |
| 408 | defer self.allocator.free(symtab); | |
| 409 | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); | |
| 410 | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); | |
| 385 | const next_nlist: ?NlistWithIndex = blk: while (true) { | |
| 386 | const curr_nlist = self.nlists[self.index]; | |
| 387 | try aliases.append(curr_nlist); | |
| 411 | 388 | |
| 412 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); | |
| 413 | defer self.allocator.free(strtab); | |
| 414 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); | |
| 389 | if (self.peek()) |next_nlist| { | |
| 390 | if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) { | |
| 391 | self.index += 1; | |
| 392 | continue; | |
| 393 | } | |
| 394 | break :blk next_nlist; | |
| 395 | } | |
| 396 | break :blk null; | |
| 397 | } else null; | |
| 415 | 398 | |
| 416 | for (slice) |sym| { | |
| 417 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); | |
| 399 | for (aliases.items) |*nlist_with_index| { | |
| 400 | nlist_with_index.index = self.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable; | |
| 401 | } | |
| 418 | 402 | |
| 419 | if (Symbol.isStab(sym)) { | |
| 420 | log.err("unhandled symbol type: stab {s} in {s}", .{ sym_name, self.name.? }); | |
| 421 | return error.UnhandledSymbolType; | |
| 403 | if (aliases.items.len > 1) { | |
| 404 | // Bubble-up senior symbol as the main link to the text block. | |
| 405 | sort.sort( | |
| 406 | NlistWithIndex, | |
| 407 | aliases.items, | |
| 408 | SeniorityContext{ .object = self.object }, | |
| 409 | TextBlockParser.lessThanBySeniority, | |
| 410 | ); | |
| 422 | 411 | } |
| 423 | if (Symbol.isIndr(sym)) { | |
| 424 | log.err("unhandled symbol type: indirect {s} in {s}", .{ sym_name, self.name.? }); | |
| 425 | return error.UnhandledSymbolType; | |
| 412 | ||
| 413 | const senior_nlist = aliases.pop(); | |
| 414 | const senior_sym = &self.macho_file.locals.items[senior_nlist.index]; | |
| 415 | senior_sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable; | |
| 416 | ||
| 417 | const start_addr = senior_nlist.nlist.n_value - self.section.addr; | |
| 418 | const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; | |
| 419 | ||
| 420 | const code = self.code[start_addr..end_addr]; | |
| 421 | const size = code.len; | |
| 422 | ||
| 423 | const max_align = self.section.@"align"; | |
| 424 | const actual_align = if (senior_nlist.nlist.n_value > 0) | |
| 425 | math.min(@ctz(u64, senior_nlist.nlist.n_value), max_align) | |
| 426 | else | |
| 427 | max_align; | |
| 428 | ||
| 429 | const stab: ?TextBlock.Stab = if (self.object.debug_info) |di| blk: { | |
| 430 | // TODO there has to be a better to handle this. | |
| 431 | for (di.inner.func_list.items) |func| { | |
| 432 | if (func.pc_range) |range| { | |
| 433 | if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) { | |
| 434 | break :blk TextBlock.Stab{ | |
| 435 | .function = range.end - range.start, | |
| 436 | }; | |
| 437 | } | |
| 438 | } | |
| 439 | } | |
| 440 | // TODO | |
| 441 | // if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global; | |
| 442 | break :blk .static; | |
| 443 | } else null; | |
| 444 | ||
| 445 | const block = try self.macho_file.base.allocator.create(TextBlock); | |
| 446 | block.* = TextBlock.empty; | |
| 447 | block.local_sym_index = senior_nlist.index; | |
| 448 | block.stab = stab; | |
| 449 | block.size = size; | |
| 450 | block.alignment = actual_align; | |
| 451 | try self.macho_file.managed_blocks.append(self.macho_file.base.allocator, block); | |
| 452 | ||
| 453 | try block.code.appendSlice(self.macho_file.base.allocator, code); | |
| 454 | ||
| 455 | try block.aliases.ensureTotalCapacity(self.macho_file.base.allocator, aliases.items.len); | |
| 456 | for (aliases.items) |alias| { | |
| 457 | block.aliases.appendAssumeCapacity(alias.index); | |
| 458 | const sym = &self.macho_file.locals.items[alias.index]; | |
| 459 | sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable; | |
| 426 | 460 | } |
| 427 | if (Symbol.isAbs(sym)) { | |
| 428 | log.err("unhandled symbol type: absolute {s} in {s}", .{ sym_name, self.name.? }); | |
| 429 | return error.UnhandledSymbolType; | |
| 461 | ||
| 462 | try block.parseRelocsFromObject(self.macho_file.base.allocator, self.relocs, self.object, .{ | |
| 463 | .base_addr = start_addr, | |
| 464 | .macho_file = self.macho_file, | |
| 465 | }); | |
| 466 | ||
| 467 | if (self.macho_file.has_dices) { | |
| 468 | const dices = filterDice( | |
| 469 | self.object.data_in_code_entries.items, | |
| 470 | senior_nlist.nlist.n_value, | |
| 471 | senior_nlist.nlist.n_value + size, | |
| 472 | ); | |
| 473 | try block.dices.ensureTotalCapacity(self.macho_file.base.allocator, dices.len); | |
| 474 | ||
| 475 | for (dices) |dice| { | |
| 476 | block.dices.appendAssumeCapacity(.{ | |
| 477 | .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value), | |
| 478 | .length = dice.length, | |
| 479 | .kind = dice.kind, | |
| 480 | }); | |
| 481 | } | |
| 430 | 482 | } |
| 431 | 483 | |
| 432 | const name = try self.allocator.dupe(u8, sym_name); | |
| 433 | const symbol: *Symbol = symbol: { | |
| 434 | if (Symbol.isSect(sym)) { | |
| 435 | const linkage: Symbol.Regular.Linkage = linkage: { | |
| 436 | if (!Symbol.isExt(sym)) break :linkage .translation_unit; | |
| 437 | if (Symbol.isWeakDef(sym) or Symbol.isPext(sym)) break :linkage .linkage_unit; | |
| 438 | break :linkage .global; | |
| 439 | }; | |
| 440 | const regular = try self.allocator.create(Symbol.Regular); | |
| 441 | errdefer self.allocator.destroy(regular); | |
| 442 | regular.* = .{ | |
| 443 | .base = .{ | |
| 444 | .@"type" = .regular, | |
| 445 | .name = name, | |
| 446 | }, | |
| 447 | .linkage = linkage, | |
| 448 | .address = sym.n_value, | |
| 449 | .section = sym.n_sect - 1, | |
| 450 | .weak_ref = Symbol.isWeakRef(sym), | |
| 451 | .file = self, | |
| 452 | }; | |
| 453 | break :symbol &regular.base; | |
| 484 | self.index += 1; | |
| 485 | ||
| 486 | return block; | |
| 487 | } | |
| 488 | }; | |
| 489 | ||
| 490 | pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { | |
| 491 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | |
| 492 | ||
| 493 | log.debug("analysing {s}", .{self.name.?}); | |
| 494 | ||
| 495 | // You would expect that the symbol table is at least pre-sorted based on symbol's type: | |
| 496 | // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance, | |
| 497 | // the GO compiler does not necessarily respect that therefore we sort immediately by type | |
| 498 | // and address within. | |
| 499 | var sorted_all_nlists = std.ArrayList(NlistWithIndex).init(self.allocator); | |
| 500 | defer sorted_all_nlists.deinit(); | |
| 501 | try sorted_all_nlists.ensureTotalCapacity(self.symtab.items.len); | |
| 502 | ||
| 503 | for (self.symtab.items) |nlist, index| { | |
| 504 | sorted_all_nlists.appendAssumeCapacity(.{ | |
| 505 | .nlist = nlist, | |
| 506 | .index = @intCast(u32, index), | |
| 507 | }); | |
| 508 | } | |
| 509 | ||
| 510 | sort.sort(NlistWithIndex, sorted_all_nlists.items, {}, NlistWithIndex.lessThan); | |
| 511 | ||
| 512 | // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we | |
| 513 | // have to infer the start of undef section in the symtab ourselves. | |
| 514 | const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: { | |
| 515 | const dysymtab = self.load_commands.items[cmd_index].Dysymtab; | |
| 516 | break :blk dysymtab.iundefsym; | |
| 517 | } else blk: { | |
| 518 | var iundefsym: usize = sorted_all_nlists.items.len; | |
| 519 | while (iundefsym > 0) : (iundefsym -= 1) { | |
| 520 | const nlist = sorted_all_nlists.items[iundefsym]; | |
| 521 | if (MachO.symbolIsSect(nlist.nlist)) break; | |
| 522 | } | |
| 523 | break :blk iundefsym; | |
| 524 | }; | |
| 525 | ||
| 526 | // We only care about defined symbols, so filter every other out. | |
| 527 | const sorted_nlists = sorted_all_nlists.items[0..iundefsym]; | |
| 528 | ||
| 529 | for (seg.sections.items) |sect, id| { | |
| 530 | const sect_id = @intCast(u8, id); | |
| 531 | log.debug("putting section '{s},{s}' as a TextBlock", .{ | |
| 532 | segmentName(sect), | |
| 533 | sectionName(sect), | |
| 534 | }); | |
| 535 | ||
| 536 | // Get matching segment/section in the final artifact. | |
| 537 | const match = (try macho_file.getMatchingSection(sect)) orelse { | |
| 538 | log.debug("unhandled section", .{}); | |
| 539 | continue; | |
| 540 | }; | |
| 541 | ||
| 542 | // Read section's code | |
| 543 | var code = try self.allocator.alloc(u8, @intCast(usize, sect.size)); | |
| 544 | defer self.allocator.free(code); | |
| 545 | _ = try self.file.?.preadAll(code, sect.offset); | |
| 546 | ||
| 547 | // Read section's list of relocations | |
| 548 | var raw_relocs = try self.allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | |
| 549 | defer self.allocator.free(raw_relocs); | |
| 550 | _ = try self.file.?.preadAll(raw_relocs, sect.reloff); | |
| 551 | const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); | |
| 552 | ||
| 553 | // Symbols within this section only. | |
| 554 | const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect); | |
| 555 | ||
| 556 | // In release mode, if the object file was generated with dead code stripping optimisations, | |
| 557 | // note it now and parse sections as atoms. | |
| 558 | const is_splittable = blk: { | |
| 559 | if (macho_file.base.options.optimize_mode == .Debug) break :blk false; | |
| 560 | break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | |
| 561 | }; | |
| 562 | ||
| 563 | macho_file.has_dices = blk: { | |
| 564 | if (self.text_section_index) |index| { | |
| 565 | if (index != id) break :blk false; | |
| 566 | if (self.data_in_code_entries.items.len == 0) break :blk false; | |
| 567 | break :blk true; | |
| 454 | 568 | } |
| 569 | break :blk false; | |
| 570 | }; | |
| 571 | macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null; | |
| 572 | ||
| 573 | next: { | |
| 574 | if (is_splittable) blocks: { | |
| 575 | if (filtered_nlists.len == 0) break :blocks; | |
| 576 | ||
| 577 | // If the first nlist does not match the start of the section, | |
| 578 | // then we need to encapsulate the memory range [section start, first symbol) | |
| 579 | // as a temporary symbol and insert the matching TextBlock. | |
| 580 | const first_nlist = filtered_nlists[0].nlist; | |
| 581 | if (first_nlist.n_value > sect.addr) { | |
| 582 | const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 583 | self.name.?, | |
| 584 | segmentName(sect), | |
| 585 | sectionName(sect), | |
| 586 | }); | |
| 587 | defer self.allocator.free(sym_name); | |
| 588 | ||
| 589 | const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { | |
| 590 | const block_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 591 | try macho_file.locals.append(macho_file.base.allocator, .{ | |
| 592 | .n_strx = try macho_file.makeString(sym_name), | |
| 593 | .n_type = macho.N_SECT, | |
| 594 | .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable, | |
| 595 | .n_desc = 0, | |
| 596 | .n_value = sect.addr, | |
| 597 | }); | |
| 598 | try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index); | |
| 599 | break :blk block_local_sym_index; | |
| 600 | }; | |
| 601 | ||
| 602 | const block_code = code[0 .. first_nlist.n_value - sect.addr]; | |
| 603 | const block_size = block_code.len; | |
| 604 | ||
| 605 | const block = try macho_file.base.allocator.create(TextBlock); | |
| 606 | block.* = TextBlock.empty; | |
| 607 | block.local_sym_index = block_local_sym_index; | |
| 608 | block.size = block_size; | |
| 609 | block.alignment = sect.@"align"; | |
| 610 | try macho_file.managed_blocks.append(macho_file.base.allocator, block); | |
| 611 | ||
| 612 | try block.code.appendSlice(macho_file.base.allocator, block_code); | |
| 613 | ||
| 614 | try block.parseRelocsFromObject(self.allocator, relocs, self, .{ | |
| 615 | .base_addr = 0, | |
| 616 | .macho_file = macho_file, | |
| 617 | }); | |
| 618 | ||
| 619 | if (macho_file.has_dices) { | |
| 620 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size); | |
| 621 | try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len); | |
| 622 | ||
| 623 | for (dices) |dice| { | |
| 624 | block.dices.appendAssumeCapacity(.{ | |
| 625 | .offset = dice.offset - try math.cast(u32, sect.addr), | |
| 626 | .length = dice.length, | |
| 627 | .kind = dice.kind, | |
| 628 | }); | |
| 629 | } | |
| 630 | } | |
| 455 | 631 | |
| 456 | if (sym.n_value != 0) { | |
| 457 | const tentative = try self.allocator.create(Symbol.Tentative); | |
| 458 | errdefer self.allocator.destroy(tentative); | |
| 459 | tentative.* = .{ | |
| 460 | .base = .{ | |
| 461 | .@"type" = .tentative, | |
| 462 | .name = name, | |
| 463 | }, | |
| 464 | .size = sym.n_value, | |
| 465 | .alignment = (sym.n_desc >> 8) & 0x0f, | |
| 466 | .file = self, | |
| 632 | // Update target section's metadata | |
| 633 | // TODO should we update segment's size here too? | |
| 634 | // How does it tie with incremental space allocs? | |
| 635 | const tseg = &macho_file.load_commands.items[match.seg].Segment; | |
| 636 | const tsect = &tseg.sections.items[match.sect]; | |
| 637 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 638 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 639 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 640 | tsect.size = new_size; | |
| 641 | tsect.@"align" = new_alignment; | |
| 642 | ||
| 643 | if (macho_file.blocks.getPtr(match)) |last| { | |
| 644 | last.*.next = block; | |
| 645 | block.prev = last.*; | |
| 646 | last.* = block; | |
| 647 | } else { | |
| 648 | try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); | |
| 649 | } | |
| 650 | ||
| 651 | try self.text_blocks.append(self.allocator, block); | |
| 652 | } | |
| 653 | ||
| 654 | var parser = TextBlockParser{ | |
| 655 | .allocator = self.allocator, | |
| 656 | .section = sect, | |
| 657 | .code = code, | |
| 658 | .relocs = relocs, | |
| 659 | .object = self, | |
| 660 | .macho_file = macho_file, | |
| 661 | .nlists = filtered_nlists, | |
| 662 | .match = match, | |
| 467 | 663 | }; |
| 468 | break :symbol &tentative.base; | |
| 664 | ||
| 665 | while (try parser.next()) |block| { | |
| 666 | const sym = macho_file.locals.items[block.local_sym_index]; | |
| 667 | const is_ext = blk: { | |
| 668 | const orig_sym_id = self.reverse_symbol_mapping.get(block.local_sym_index) orelse unreachable; | |
| 669 | break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]); | |
| 670 | }; | |
| 671 | if (is_ext) { | |
| 672 | if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| { | |
| 673 | assert(resolv.where == .global); | |
| 674 | const global_object = macho_file.objects.items[resolv.file]; | |
| 675 | if (global_object != self) { | |
| 676 | log.debug("deduping definition of {s} in {s}", .{ | |
| 677 | macho_file.getString(sym.n_strx), | |
| 678 | self.name.?, | |
| 679 | }); | |
| 680 | log.debug(" already defined in {s}", .{global_object.name.?}); | |
| 681 | continue; | |
| 682 | } | |
| 683 | } | |
| 684 | } | |
| 685 | ||
| 686 | if (sym.n_value == sect.addr) { | |
| 687 | if (self.sections_as_symbols.get(sect_id)) |alias| { | |
| 688 | // In x86_64 relocs, it can so happen that the compiler refers to the same | |
| 689 | // atom by both the actual assigned symbol and the start of the section. In this | |
| 690 | // case, we need to link the two together so add an alias. | |
| 691 | try block.aliases.append(macho_file.base.allocator, alias); | |
| 692 | } | |
| 693 | } | |
| 694 | ||
| 695 | // Update target section's metadata | |
| 696 | // TODO should we update segment's size here too? | |
| 697 | // How does it tie with incremental space allocs? | |
| 698 | const tseg = &macho_file.load_commands.items[match.seg].Segment; | |
| 699 | const tsect = &tseg.sections.items[match.sect]; | |
| 700 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 701 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 702 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 703 | tsect.size = new_size; | |
| 704 | tsect.@"align" = new_alignment; | |
| 705 | ||
| 706 | if (macho_file.blocks.getPtr(match)) |last| { | |
| 707 | last.*.next = block; | |
| 708 | block.prev = last.*; | |
| 709 | last.* = block; | |
| 710 | } else { | |
| 711 | try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); | |
| 712 | } | |
| 713 | ||
| 714 | try self.text_blocks.append(self.allocator, block); | |
| 715 | } | |
| 716 | ||
| 717 | break :next; | |
| 469 | 718 | } |
| 470 | 719 | |
| 471 | const undef = try self.allocator.create(Symbol.Unresolved); | |
| 472 | errdefer self.allocator.destroy(undef); | |
| 473 | undef.* = .{ | |
| 474 | .base = .{ | |
| 475 | .@"type" = .unresolved, | |
| 476 | .name = name, | |
| 477 | }, | |
| 478 | .file = self, | |
| 720 | // Since there is no symbol to refer to this block, we create | |
| 721 | // a temp one, unless we already did that when working out the relocations | |
| 722 | // of other text blocks. | |
| 723 | const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 724 | self.name.?, | |
| 725 | segmentName(sect), | |
| 726 | sectionName(sect), | |
| 727 | }); | |
| 728 | defer self.allocator.free(sym_name); | |
| 729 | ||
| 730 | const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { | |
| 731 | const block_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 732 | try macho_file.locals.append(macho_file.base.allocator, .{ | |
| 733 | .n_strx = try macho_file.makeString(sym_name), | |
| 734 | .n_type = macho.N_SECT, | |
| 735 | .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable, | |
| 736 | .n_desc = 0, | |
| 737 | .n_value = sect.addr, | |
| 738 | }); | |
| 739 | try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index); | |
| 740 | break :blk block_local_sym_index; | |
| 479 | 741 | }; |
| 480 | break :symbol &undef.base; | |
| 481 | }; | |
| 482 | 742 | |
| 483 | try self.symbols.append(self.allocator, symbol); | |
| 743 | const block = try macho_file.base.allocator.create(TextBlock); | |
| 744 | block.* = TextBlock.empty; | |
| 745 | block.local_sym_index = block_local_sym_index; | |
| 746 | block.size = sect.size; | |
| 747 | block.alignment = sect.@"align"; | |
| 748 | try macho_file.managed_blocks.append(macho_file.base.allocator, block); | |
| 749 | ||
| 750 | try block.code.appendSlice(macho_file.base.allocator, code); | |
| 751 | ||
| 752 | try block.parseRelocsFromObject(self.allocator, relocs, self, .{ | |
| 753 | .base_addr = 0, | |
| 754 | .macho_file = macho_file, | |
| 755 | }); | |
| 756 | ||
| 757 | if (macho_file.has_dices) { | |
| 758 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); | |
| 759 | try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len); | |
| 760 | ||
| 761 | for (dices) |dice| { | |
| 762 | block.dices.appendAssumeCapacity(.{ | |
| 763 | .offset = dice.offset - try math.cast(u32, sect.addr), | |
| 764 | .length = dice.length, | |
| 765 | .kind = dice.kind, | |
| 766 | }); | |
| 767 | } | |
| 768 | } | |
| 769 | ||
| 770 | // Since this is block gets a helper local temporary symbol that didn't exist | |
| 771 | // in the object file which encompasses the entire section, we need traverse | |
| 772 | // the filtered symbols and note which symbol is contained within so that | |
| 773 | // we can properly allocate addresses down the line. | |
| 774 | // While we're at it, we need to update segment,section mapping of each symbol too. | |
| 775 | try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len); | |
| 776 | ||
| 777 | for (filtered_nlists) |nlist_with_index| { | |
| 778 | const nlist = nlist_with_index.nlist; | |
| 779 | const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; | |
| 780 | const local = &macho_file.locals.items[local_sym_index]; | |
| 781 | local.n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable; | |
| 782 | ||
| 783 | const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: { | |
| 784 | // TODO there has to be a better to handle this. | |
| 785 | for (di.inner.func_list.items) |func| { | |
| 786 | if (func.pc_range) |range| { | |
| 787 | if (nlist.n_value >= range.start and nlist.n_value < range.end) { | |
| 788 | break :blk TextBlock.Stab{ | |
| 789 | .function = range.end - range.start, | |
| 790 | }; | |
| 791 | } | |
| 792 | } | |
| 793 | } | |
| 794 | // TODO | |
| 795 | // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; | |
| 796 | break :blk .static; | |
| 797 | } else null; | |
| 798 | ||
| 799 | block.contained.appendAssumeCapacity(.{ | |
| 800 | .local_sym_index = local_sym_index, | |
| 801 | .offset = nlist.n_value - sect.addr, | |
| 802 | .stab = stab, | |
| 803 | }); | |
| 804 | } | |
| 805 | ||
| 806 | // Update target section's metadata | |
| 807 | // TODO should we update segment's size here too? | |
| 808 | // How does it tie with incremental space allocs? | |
| 809 | const tseg = &macho_file.load_commands.items[match.seg].Segment; | |
| 810 | const tsect = &tseg.sections.items[match.sect]; | |
| 811 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 812 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 813 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 814 | tsect.size = new_size; | |
| 815 | tsect.@"align" = new_alignment; | |
| 816 | ||
| 817 | if (macho_file.blocks.getPtr(match)) |last| { | |
| 818 | last.*.next = block; | |
| 819 | block.prev = last.*; | |
| 820 | last.* = block; | |
| 821 | } else { | |
| 822 | try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); | |
| 823 | } | |
| 824 | ||
| 825 | try self.text_blocks.append(self.allocator, block); | |
| 826 | } | |
| 484 | 827 | } |
| 485 | 828 | } |
| 486 | 829 | |
| 830 | fn parseSymtab(self: *Object) !void { | |
| 831 | const index = self.symtab_cmd_index orelse return; | |
| 832 | const symtab_cmd = self.load_commands.items[index].Symtab; | |
| 833 | ||
| 834 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); | |
| 835 | defer self.allocator.free(symtab); | |
| 836 | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); | |
| 837 | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); | |
| 838 | try self.symtab.appendSlice(self.allocator, slice); | |
| 839 | ||
| 840 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); | |
| 841 | defer self.allocator.free(strtab); | |
| 842 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); | |
| 843 | try self.strtab.appendSlice(self.allocator, strtab); | |
| 844 | } | |
| 845 | ||
| 487 | 846 | pub fn parseDebugInfo(self: *Object) !void { |
| 847 | log.debug("parsing debug info in '{s}'", .{self.name.?}); | |
| 848 | ||
| 488 | 849 | var debug_info = blk: { |
| 489 | 850 | var di = try DebugInfo.parseFromObject(self.allocator, self); |
| 490 | 851 | break :blk di orelse return; |
| 491 | 852 | }; |
| 492 | defer debug_info.deinit(self.allocator); | |
| 493 | ||
| 494 | log.debug("parsing debug info in '{s}'", .{self.name.?}); | |
| 495 | 853 | |
| 496 | 854 | // We assume there is only one CU. |
| 497 | 855 | const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) { |
| ... | ... | @@ -505,44 +863,19 @@ pub fn parseDebugInfo(self: *Object) !void { |
| 505 | 863 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); |
| 506 | 864 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); |
| 507 | 865 | |
| 508 | self.tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name }); | |
| 509 | self.tu_mtime = mtime: { | |
| 510 | const stat = try self.file.?.stat(); | |
| 511 | break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); | |
| 512 | }; | |
| 866 | self.debug_info = debug_info; | |
| 867 | self.tu_name = try self.allocator.dupe(u8, name); | |
| 868 | self.tu_comp_dir = try self.allocator.dupe(u8, comp_dir); | |
| 513 | 869 | |
| 514 | for (self.symbols.items) |sym| { | |
| 515 | if (sym.cast(Symbol.Regular)) |reg| { | |
| 516 | const size: u64 = blk: for (debug_info.inner.func_list.items) |func| { | |
| 517 | if (func.pc_range) |range| { | |
| 518 | if (reg.address >= range.start and reg.address < range.end) { | |
| 519 | break :blk range.end - range.start; | |
| 520 | } | |
| 521 | } | |
| 522 | } else 0; | |
| 523 | ||
| 524 | reg.stab = .{ | |
| 525 | .kind = kind: { | |
| 526 | if (size > 0) break :kind .function; | |
| 527 | switch (reg.linkage) { | |
| 528 | .translation_unit => break :kind .static, | |
| 529 | else => break :kind .global, | |
| 530 | } | |
| 531 | }, | |
| 532 | .size = size, | |
| 533 | }; | |
| 534 | } | |
| 870 | if (self.mtime == null) { | |
| 871 | self.mtime = mtime: { | |
| 872 | const file = self.file orelse break :mtime 0; | |
| 873 | const stat = file.stat() catch break :mtime 0; | |
| 874 | break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); | |
| 875 | }; | |
| 535 | 876 | } |
| 536 | 877 | } |
| 537 | 878 | |
| 538 | fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { | |
| 539 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | |
| 540 | const sect = seg.sections.items[index]; | |
| 541 | var buffer = try allocator.alloc(u8, @intCast(usize, sect.size)); | |
| 542 | _ = try self.file.?.preadAll(buffer, sect.offset); | |
| 543 | return buffer; | |
| 544 | } | |
| 545 | ||
| 546 | 879 | pub fn parseDataInCode(self: *Object) !void { |
| 547 | 880 | const index = self.data_in_code_cmd_index orelse return; |
| 548 | 881 | const data_in_code = self.load_commands.items[index].LinkeditData; |
| ... | ... | @@ -562,3 +895,16 @@ pub fn parseDataInCode(self: *Object) !void { |
| 562 | 895 | try self.data_in_code_entries.append(self.allocator, dice); |
| 563 | 896 | } |
| 564 | 897 | } |
| 898 | ||
| 899 | fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { | |
| 900 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | |
| 901 | const sect = seg.sections.items[index]; | |
| 902 | var buffer = try allocator.alloc(u8, @intCast(usize, sect.size)); | |
| 903 | _ = try self.file.?.preadAll(buffer, sect.offset); | |
| 904 | return buffer; | |
| 905 | } | |
| 906 | ||
| 907 | pub fn getString(self: Object, off: u32) []const u8 { | |
| 908 | assert(off < self.strtab.items.len); | |
| 909 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); | |
| 910 | } |
src/link/MachO/Symbol.zig deleted-195| ... | ... | @@ -1,195 +0,0 @@ |
| 1 | const Symbol = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const macho = std.macho; | |
| 5 | const mem = std.mem; | |
| 6 | ||
| 7 | const Allocator = mem.Allocator; | |
| 8 | const Dylib = @import("Dylib.zig"); | |
| 9 | const Object = @import("Object.zig"); | |
| 10 | ||
| 11 | pub const Type = enum { | |
| 12 | regular, | |
| 13 | proxy, | |
| 14 | unresolved, | |
| 15 | tentative, | |
| 16 | }; | |
| 17 | ||
| 18 | /// Symbol type. | |
| 19 | @"type": Type, | |
| 20 | ||
| 21 | /// Symbol name. Owned slice. | |
| 22 | name: []u8, | |
| 23 | ||
| 24 | /// Alias of. | |
| 25 | alias: ?*Symbol = null, | |
| 26 | ||
| 27 | /// Index in GOT table for indirection. | |
| 28 | got_index: ?u32 = null, | |
| 29 | ||
| 30 | /// Index in stubs table for late binding. | |
| 31 | stubs_index: ?u32 = null, | |
| 32 | ||
| 33 | pub const Regular = struct { | |
| 34 | base: Symbol, | |
| 35 | ||
| 36 | /// Linkage type. | |
| 37 | linkage: Linkage, | |
| 38 | ||
| 39 | /// Symbol address. | |
| 40 | address: u64, | |
| 41 | ||
| 42 | /// Section ID where the symbol resides. | |
| 43 | section: u8, | |
| 44 | ||
| 45 | /// Whether the symbol is a weak ref. | |
| 46 | weak_ref: bool, | |
| 47 | ||
| 48 | /// Object file where to locate this symbol. | |
| 49 | file: *Object, | |
| 50 | ||
| 51 | /// Debug stab if defined. | |
| 52 | stab: ?struct { | |
| 53 | /// Stab kind | |
| 54 | kind: enum { | |
| 55 | function, | |
| 56 | global, | |
| 57 | static, | |
| 58 | }, | |
| 59 | ||
| 60 | /// Size of the stab. | |
| 61 | size: u64, | |
| 62 | } = null, | |
| 63 | ||
| 64 | /// True if symbol was already committed into the final | |
| 65 | /// symbol table. | |
| 66 | visited: bool = false, | |
| 67 | ||
| 68 | pub const base_type: Symbol.Type = .regular; | |
| 69 | ||
| 70 | pub const Linkage = enum { | |
| 71 | translation_unit, | |
| 72 | linkage_unit, | |
| 73 | global, | |
| 74 | }; | |
| 75 | ||
| 76 | pub fn isTemp(regular: *Regular) bool { | |
| 77 | if (regular.linkage == .translation_unit) { | |
| 78 | return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L"); | |
| 79 | } | |
| 80 | return false; | |
| 81 | } | |
| 82 | }; | |
| 83 | ||
| 84 | pub const Proxy = struct { | |
| 85 | base: Symbol, | |
| 86 | ||
| 87 | /// Dynamic binding info - spots within the final | |
| 88 | /// executable where this proxy is referenced from. | |
| 89 | bind_info: std.ArrayListUnmanaged(struct { | |
| 90 | segment_id: u16, | |
| 91 | address: u64, | |
| 92 | }) = .{}, | |
| 93 | ||
| 94 | /// Dylib where to locate this symbol. | |
| 95 | /// null means self-reference. | |
| 96 | file: ?*Dylib = null, | |
| 97 | ||
| 98 | pub const base_type: Symbol.Type = .proxy; | |
| 99 | ||
| 100 | pub fn deinit(proxy: *Proxy, allocator: *Allocator) void { | |
| 101 | proxy.bind_info.deinit(allocator); | |
| 102 | } | |
| 103 | ||
| 104 | pub fn dylibOrdinal(proxy: *Proxy) u16 { | |
| 105 | const dylib = proxy.file orelse return 0; | |
| 106 | return dylib.ordinal.?; | |
| 107 | } | |
| 108 | }; | |
| 109 | ||
| 110 | pub const Unresolved = struct { | |
| 111 | base: Symbol, | |
| 112 | ||
| 113 | /// File where this symbol was referenced. | |
| 114 | /// null means synthetic, e.g., dyld_stub_binder. | |
| 115 | file: ?*Object = null, | |
| 116 | ||
| 117 | pub const base_type: Symbol.Type = .unresolved; | |
| 118 | }; | |
| 119 | ||
| 120 | pub const Tentative = struct { | |
| 121 | base: Symbol, | |
| 122 | ||
| 123 | /// Symbol size. | |
| 124 | size: u64, | |
| 125 | ||
| 126 | /// Symbol alignment as power of two. | |
| 127 | alignment: u16, | |
| 128 | ||
| 129 | /// File where this symbol was referenced. | |
| 130 | file: *Object, | |
| 131 | ||
| 132 | pub const base_type: Symbol.Type = .tentative; | |
| 133 | }; | |
| 134 | ||
| 135 | pub fn deinit(base: *Symbol, allocator: *Allocator) void { | |
| 136 | allocator.free(base.name); | |
| 137 | switch (base.@"type") { | |
| 138 | .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator), | |
| 139 | else => {}, | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | pub fn cast(base: *Symbol, comptime T: type) ?*T { | |
| 144 | if (base.@"type" != T.base_type) { | |
| 145 | return null; | |
| 146 | } | |
| 147 | return @fieldParentPtr(T, "base", base); | |
| 148 | } | |
| 149 | ||
| 150 | pub fn getTopmostAlias(base: *Symbol) *Symbol { | |
| 151 | if (base.alias) |alias| { | |
| 152 | return alias.getTopmostAlias(); | |
| 153 | } | |
| 154 | return base; | |
| 155 | } | |
| 156 | ||
| 157 | pub fn isStab(sym: macho.nlist_64) bool { | |
| 158 | return (macho.N_STAB & sym.n_type) != 0; | |
| 159 | } | |
| 160 | ||
| 161 | pub fn isPext(sym: macho.nlist_64) bool { | |
| 162 | return (macho.N_PEXT & sym.n_type) != 0; | |
| 163 | } | |
| 164 | ||
| 165 | pub fn isExt(sym: macho.nlist_64) bool { | |
| 166 | return (macho.N_EXT & sym.n_type) != 0; | |
| 167 | } | |
| 168 | ||
| 169 | pub fn isSect(sym: macho.nlist_64) bool { | |
| 170 | const type_ = macho.N_TYPE & sym.n_type; | |
| 171 | return type_ == macho.N_SECT; | |
| 172 | } | |
| 173 | ||
| 174 | pub fn isUndf(sym: macho.nlist_64) bool { | |
| 175 | const type_ = macho.N_TYPE & sym.n_type; | |
| 176 | return type_ == macho.N_UNDF; | |
| 177 | } | |
| 178 | ||
| 179 | pub fn isIndr(sym: macho.nlist_64) bool { | |
| 180 | const type_ = macho.N_TYPE & sym.n_type; | |
| 181 | return type_ == macho.N_INDR; | |
| 182 | } | |
| 183 | ||
| 184 | pub fn isAbs(sym: macho.nlist_64) bool { | |
| 185 | const type_ = macho.N_TYPE & sym.n_type; | |
| 186 | return type_ == macho.N_ABS; | |
| 187 | } | |
| 188 | ||
| 189 | pub fn isWeakDef(sym: macho.nlist_64) bool { | |
| 190 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; | |
| 191 | } | |
| 192 | ||
| 193 | pub fn isWeakRef(sym: macho.nlist_64) bool { | |
| 194 | return (sym.n_desc & macho.N_WEAK_REF) != 0; | |
| 195 | } |
src/link/MachO/TextBlock.zig created+1224| ... | ... | @@ -0,0 +1,1224 @@ |
| 1 | const TextBlock = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 5 | const assert = std.debug.assert; | |
| 6 | const commands = @import("commands.zig"); | |
| 7 | const log = std.log.scoped(.text_block); | |
| 8 | const macho = std.macho; | |
| 9 | const math = std.math; | |
| 10 | const mem = std.mem; | |
| 11 | const meta = std.meta; | |
| 12 | ||
| 13 | const Allocator = mem.Allocator; | |
| 14 | const Arch = std.Target.Cpu.Arch; | |
| 15 | const MachO = @import("../MachO.zig"); | |
| 16 | const Object = @import("Object.zig"); | |
| 17 | ||
| 18 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 19 | /// The vaddr and size are found here directly. | |
| 20 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 21 | /// the symbol references, and adding that to the file offset of the section. | |
| 22 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 23 | /// offset table entry. | |
| 24 | local_sym_index: u32, | |
| 25 | ||
| 26 | /// List of symbol aliases pointing to the same block via different nlists | |
| 27 | aliases: std.ArrayListUnmanaged(u32) = .{}, | |
| 28 | ||
| 29 | /// List of symbols contained within this block | |
| 30 | contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{}, | |
| 31 | ||
| 32 | /// Code (may be non-relocated) this block represents | |
| 33 | code: std.ArrayListUnmanaged(u8) = .{}, | |
| 34 | ||
| 35 | /// Size and alignment of this text block | |
| 36 | /// Unlike in Elf, we need to store the size of this symbol as part of | |
| 37 | /// the TextBlock since macho.nlist_64 lacks this information. | |
| 38 | size: u64, | |
| 39 | alignment: u32, | |
| 40 | ||
| 41 | relocs: std.ArrayListUnmanaged(Relocation) = .{}, | |
| 42 | ||
| 43 | /// List of offsets contained within this block that need rebasing by the dynamic | |
| 44 | /// loader in presence of ASLR | |
| 45 | rebases: std.ArrayListUnmanaged(u64) = .{}, | |
| 46 | ||
| 47 | /// List of offsets contained within this block that will be dynamically bound | |
| 48 | /// by the dynamic loader and contain pointers to resolved (at load time) extern | |
| 49 | /// symbols (aka proxies aka imports) | |
| 50 | bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{}, | |
| 51 | ||
| 52 | /// List of data-in-code entries. This is currently specific to x86_64 only. | |
| 53 | dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | |
| 54 | ||
| 55 | /// Stab entry for this block. This is currently specific to a binary created | |
| 56 | /// by linking object files in a traditional sense - in incremental sense, we | |
| 57 | /// bypass stabs altogether to produce dSYM bundle directly with fully relocated | |
| 58 | /// DWARF sections. | |
| 59 | stab: ?Stab = null, | |
| 60 | ||
| 61 | /// Points to the previous and next neighbours | |
| 62 | next: ?*TextBlock, | |
| 63 | prev: ?*TextBlock, | |
| 64 | ||
| 65 | /// Previous/next linked list pointers. | |
| 66 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | |
| 67 | dbg_info_prev: ?*TextBlock, | |
| 68 | dbg_info_next: ?*TextBlock, | |
| 69 | /// Offset into .debug_info pointing to the tag for this Decl. | |
| 70 | dbg_info_off: u32, | |
| 71 | /// Size of the .debug_info tag for this Decl, not including padding. | |
| 72 | dbg_info_len: u32, | |
| 73 | ||
| 74 | pub const SymbolAtOffset = struct { | |
| 75 | local_sym_index: u32, | |
| 76 | offset: u64, | |
| 77 | stab: ?Stab = null, | |
| 78 | ||
| 79 | pub fn format( | |
| 80 | self: SymbolAtOffset, | |
| 81 | comptime fmt: []const u8, | |
| 82 | options: std.fmt.FormatOptions, | |
| 83 | writer: anytype, | |
| 84 | ) !void { | |
| 85 | _ = fmt; | |
| 86 | _ = options; | |
| 87 | try std.fmt.format(writer, "{{ {d}: .offset = {d}", .{ self.local_sym_index, self.offset }); | |
| 88 | if (self.stab) |stab| { | |
| 89 | try std.fmt.format(writer, ", .stab = {any}", .{stab}); | |
| 90 | } | |
| 91 | try std.fmt.format(writer, " }}", .{}); | |
| 92 | } | |
| 93 | }; | |
| 94 | ||
| 95 | pub const Stab = union(enum) { | |
| 96 | function: u64, | |
| 97 | static, | |
| 98 | global, | |
| 99 | ||
| 100 | pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 { | |
| 101 | var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator); | |
| 102 | defer nlists.deinit(); | |
| 103 | ||
| 104 | const sym = macho_file.locals.items[local_sym_index]; | |
| 105 | switch (stab) { | |
| 106 | .function => |size| { | |
| 107 | try nlists.ensureUnusedCapacity(4); | |
| 108 | nlists.appendAssumeCapacity(.{ | |
| 109 | .n_strx = 0, | |
| 110 | .n_type = macho.N_BNSYM, | |
| 111 | .n_sect = sym.n_sect, | |
| 112 | .n_desc = 0, | |
| 113 | .n_value = sym.n_value, | |
| 114 | }); | |
| 115 | nlists.appendAssumeCapacity(.{ | |
| 116 | .n_strx = sym.n_strx, | |
| 117 | .n_type = macho.N_FUN, | |
| 118 | .n_sect = sym.n_sect, | |
| 119 | .n_desc = 0, | |
| 120 | .n_value = sym.n_value, | |
| 121 | }); | |
| 122 | nlists.appendAssumeCapacity(.{ | |
| 123 | .n_strx = 0, | |
| 124 | .n_type = macho.N_FUN, | |
| 125 | .n_sect = 0, | |
| 126 | .n_desc = 0, | |
| 127 | .n_value = size, | |
| 128 | }); | |
| 129 | nlists.appendAssumeCapacity(.{ | |
| 130 | .n_strx = 0, | |
| 131 | .n_type = macho.N_ENSYM, | |
| 132 | .n_sect = sym.n_sect, | |
| 133 | .n_desc = 0, | |
| 134 | .n_value = size, | |
| 135 | }); | |
| 136 | }, | |
| 137 | .global => { | |
| 138 | try nlists.append(.{ | |
| 139 | .n_strx = sym.n_strx, | |
| 140 | .n_type = macho.N_GSYM, | |
| 141 | .n_sect = 0, | |
| 142 | .n_desc = 0, | |
| 143 | .n_value = 0, | |
| 144 | }); | |
| 145 | }, | |
| 146 | .static => { | |
| 147 | try nlists.append(.{ | |
| 148 | .n_strx = sym.n_strx, | |
| 149 | .n_type = macho.N_STSYM, | |
| 150 | .n_sect = sym.n_sect, | |
| 151 | .n_desc = 0, | |
| 152 | .n_value = sym.n_value, | |
| 153 | }); | |
| 154 | }, | |
| 155 | } | |
| 156 | ||
| 157 | return nlists.toOwnedSlice(); | |
| 158 | } | |
| 159 | }; | |
| 160 | ||
| 161 | pub const Relocation = struct { | |
| 162 | /// Offset within the `block`s code buffer. | |
| 163 | /// Note relocation size can be inferred by relocation's kind. | |
| 164 | offset: u32, | |
| 165 | ||
| 166 | where: enum { | |
| 167 | local, | |
| 168 | import, | |
| 169 | }, | |
| 170 | ||
| 171 | where_index: u32, | |
| 172 | ||
| 173 | payload: union(enum) { | |
| 174 | unsigned: Unsigned, | |
| 175 | branch: Branch, | |
| 176 | page: Page, | |
| 177 | page_off: PageOff, | |
| 178 | pointer_to_got: PointerToGot, | |
| 179 | signed: Signed, | |
| 180 | load: Load, | |
| 181 | }, | |
| 182 | ||
| 183 | const ResolveArgs = struct { | |
| 184 | block: *TextBlock, | |
| 185 | offset: u32, | |
| 186 | source_addr: u64, | |
| 187 | target_addr: u64, | |
| 188 | macho_file: *MachO, | |
| 189 | }; | |
| 190 | ||
| 191 | pub const Unsigned = struct { | |
| 192 | subtractor: ?u32, | |
| 193 | ||
| 194 | /// Addend embedded directly in the relocation slot | |
| 195 | addend: i64, | |
| 196 | ||
| 197 | /// Extracted from r_length: | |
| 198 | /// => 3 implies true | |
| 199 | /// => 2 implies false | |
| 200 | /// => * is unreachable | |
| 201 | is_64bit: bool, | |
| 202 | ||
| 203 | pub fn resolve(self: Unsigned, args: ResolveArgs) !void { | |
| 204 | const result = blk: { | |
| 205 | if (self.subtractor) |subtractor| { | |
| 206 | const sym = args.macho_file.locals.items[subtractor]; | |
| 207 | break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend; | |
| 208 | } else { | |
| 209 | break :blk @intCast(i64, args.target_addr) + self.addend; | |
| 210 | } | |
| 211 | }; | |
| 212 | ||
| 213 | if (self.is_64bit) { | |
| 214 | mem.writeIntLittle(u64, args.block.code.items[args.offset..][0..8], @bitCast(u64, result)); | |
| 215 | } else { | |
| 216 | mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @truncate(u32, @bitCast(u64, result))); | |
| 217 | } | |
| 218 | } | |
| 219 | ||
| 220 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 221 | _ = fmt; | |
| 222 | _ = options; | |
| 223 | try std.fmt.format(writer, "Unsigned {{ ", .{}); | |
| 224 | if (self.subtractor) |sub| { | |
| 225 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 226 | } | |
| 227 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 228 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 229 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 230 | try std.fmt.format(writer, "}}", .{}); | |
| 231 | } | |
| 232 | }; | |
| 233 | ||
| 234 | pub const Branch = struct { | |
| 235 | arch: Arch, | |
| 236 | ||
| 237 | pub fn resolve(self: Branch, args: ResolveArgs) !void { | |
| 238 | switch (self.arch) { | |
| 239 | .aarch64 => { | |
| 240 | const displacement = try math.cast( | |
| 241 | i28, | |
| 242 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr), | |
| 243 | ); | |
| 244 | const code = args.block.code.items[args.offset..][0..4]; | |
| 245 | var inst = aarch64.Instruction{ | |
| 246 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( | |
| 247 | aarch64.Instruction, | |
| 248 | aarch64.Instruction.unconditional_branch_immediate, | |
| 249 | ), code), | |
| 250 | }; | |
| 251 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 252 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 253 | }, | |
| 254 | .x86_64 => { | |
| 255 | const displacement = try math.cast( | |
| 256 | i32, | |
| 257 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4, | |
| 258 | ); | |
| 259 | mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 260 | }, | |
| 261 | else => return error.UnsupportedCpuArchitecture, | |
| 262 | } | |
| 263 | } | |
| 264 | ||
| 265 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 266 | _ = self; | |
| 267 | _ = fmt; | |
| 268 | _ = options; | |
| 269 | try std.fmt.format(writer, "Branch {{}}", .{}); | |
| 270 | } | |
| 271 | }; | |
| 272 | ||
| 273 | pub const Page = struct { | |
| 274 | kind: enum { | |
| 275 | page, | |
| 276 | got, | |
| 277 | tlvp, | |
| 278 | }, | |
| 279 | addend: u32 = 0, | |
| 280 | ||
| 281 | pub fn resolve(self: Page, args: ResolveArgs) !void { | |
| 282 | const target_addr = args.target_addr + self.addend; | |
| 283 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 284 | const target_page = @intCast(i32, target_addr >> 12); | |
| 285 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 286 | ||
| 287 | const code = args.block.code.items[args.offset..][0..4]; | |
| 288 | var inst = aarch64.Instruction{ | |
| 289 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( | |
| 290 | aarch64.Instruction, | |
| 291 | aarch64.Instruction.pc_relative_address, | |
| 292 | ), code), | |
| 293 | }; | |
| 294 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 295 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 296 | ||
| 297 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 298 | } | |
| 299 | ||
| 300 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 301 | _ = fmt; | |
| 302 | _ = options; | |
| 303 | try std.fmt.format(writer, "Page {{ ", .{}); | |
| 304 | switch (self.kind) { | |
| 305 | .page => {}, | |
| 306 | .got => { | |
| 307 | try std.fmt.format(writer, ".got, ", .{}); | |
| 308 | }, | |
| 309 | .tlvp => { | |
| 310 | try std.fmt.format(writer, ".tlvp", .{}); | |
| 311 | }, | |
| 312 | } | |
| 313 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 314 | try std.fmt.format(writer, "}}", .{}); | |
| 315 | } | |
| 316 | }; | |
| 317 | ||
| 318 | pub const PageOff = struct { | |
| 319 | kind: enum { | |
| 320 | page, | |
| 321 | got, | |
| 322 | tlvp, | |
| 323 | }, | |
| 324 | addend: u32 = 0, | |
| 325 | op_kind: ?OpKind = null, | |
| 326 | ||
| 327 | pub const OpKind = enum { | |
| 328 | arithmetic, | |
| 329 | load, | |
| 330 | }; | |
| 331 | ||
| 332 | pub fn resolve(self: PageOff, args: ResolveArgs) !void { | |
| 333 | const code = args.block.code.items[args.offset..][0..4]; | |
| 334 | ||
| 335 | switch (self.kind) { | |
| 336 | .page => { | |
| 337 | const target_addr = args.target_addr + self.addend; | |
| 338 | const narrowed = @truncate(u12, target_addr); | |
| 339 | ||
| 340 | const op_kind = self.op_kind orelse unreachable; | |
| 341 | var inst: aarch64.Instruction = blk: { | |
| 342 | switch (op_kind) { | |
| 343 | .arithmetic => { | |
| 344 | break :blk .{ | |
| 345 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | |
| 346 | aarch64.Instruction, | |
| 347 | aarch64.Instruction.add_subtract_immediate, | |
| 348 | ), code), | |
| 349 | }; | |
| 350 | }, | |
| 351 | .load => { | |
| 352 | break :blk .{ | |
| 353 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 354 | aarch64.Instruction, | |
| 355 | aarch64.Instruction.load_store_register, | |
| 356 | ), code), | |
| 357 | }; | |
| 358 | }, | |
| 359 | } | |
| 360 | }; | |
| 361 | ||
| 362 | if (op_kind == .arithmetic) { | |
| 363 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 364 | } else { | |
| 365 | const offset: u12 = blk: { | |
| 366 | if (inst.load_store_register.size == 0) { | |
| 367 | if (inst.load_store_register.v == 1) { | |
| 368 | // 128-bit SIMD is scaled by 16. | |
| 369 | break :blk try math.divExact(u12, narrowed, 16); | |
| 370 | } | |
| 371 | // Otherwise, 8-bit SIMD or ldrb. | |
| 372 | break :blk narrowed; | |
| 373 | } else { | |
| 374 | const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 375 | break :blk try math.divExact(u12, narrowed, denom); | |
| 376 | } | |
| 377 | }; | |
| 378 | inst.load_store_register.offset = offset; | |
| 379 | } | |
| 380 | ||
| 381 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 382 | }, | |
| 383 | .got => { | |
| 384 | const narrowed = @truncate(u12, args.target_addr); | |
| 385 | var inst: aarch64.Instruction = .{ | |
| 386 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 387 | aarch64.Instruction, | |
| 388 | aarch64.Instruction.load_store_register, | |
| 389 | ), code), | |
| 390 | }; | |
| 391 | const offset = try math.divExact(u12, narrowed, 8); | |
| 392 | inst.load_store_register.offset = offset; | |
| 393 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 394 | }, | |
| 395 | .tlvp => { | |
| 396 | const RegInfo = struct { | |
| 397 | rd: u5, | |
| 398 | rn: u5, | |
| 399 | size: u1, | |
| 400 | }; | |
| 401 | const reg_info: RegInfo = blk: { | |
| 402 | if (isArithmeticOp(code)) { | |
| 403 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 404 | aarch64.Instruction, | |
| 405 | aarch64.Instruction.add_subtract_immediate, | |
| 406 | ), code); | |
| 407 | break :blk .{ | |
| 408 | .rd = inst.rd, | |
| 409 | .rn = inst.rn, | |
| 410 | .size = inst.sf, | |
| 411 | }; | |
| 412 | } else { | |
| 413 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 414 | aarch64.Instruction, | |
| 415 | aarch64.Instruction.load_store_register, | |
| 416 | ), code); | |
| 417 | break :blk .{ | |
| 418 | .rd = inst.rt, | |
| 419 | .rn = inst.rn, | |
| 420 | .size = @truncate(u1, inst.size), | |
| 421 | }; | |
| 422 | } | |
| 423 | }; | |
| 424 | const narrowed = @truncate(u12, args.target_addr); | |
| 425 | var inst = aarch64.Instruction{ | |
| 426 | .add_subtract_immediate = .{ | |
| 427 | .rd = reg_info.rd, | |
| 428 | .rn = reg_info.rn, | |
| 429 | .imm12 = narrowed, | |
| 430 | .sh = 0, | |
| 431 | .s = 0, | |
| 432 | .op = 0, | |
| 433 | .sf = reg_info.size, | |
| 434 | }, | |
| 435 | }; | |
| 436 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 437 | }, | |
| 438 | } | |
| 439 | } | |
| 440 | ||
| 441 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 442 | _ = fmt; | |
| 443 | _ = options; | |
| 444 | try std.fmt.format(writer, "PageOff {{ ", .{}); | |
| 445 | switch (self.kind) { | |
| 446 | .page => {}, | |
| 447 | .got => { | |
| 448 | try std.fmt.format(writer, ".got, ", .{}); | |
| 449 | }, | |
| 450 | .tlvp => { | |
| 451 | try std.fmt.format(writer, ".tlvp, ", .{}); | |
| 452 | }, | |
| 453 | } | |
| 454 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 455 | try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind}); | |
| 456 | try std.fmt.format(writer, "}}", .{}); | |
| 457 | } | |
| 458 | }; | |
| 459 | ||
| 460 | pub const PointerToGot = struct { | |
| 461 | pub fn resolve(_: PointerToGot, args: ResolveArgs) !void { | |
| 462 | const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 463 | mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, result)); | |
| 464 | } | |
| 465 | ||
| 466 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 467 | _ = self; | |
| 468 | _ = fmt; | |
| 469 | _ = options; | |
| 470 | try std.fmt.format(writer, "PointerToGot {{}}", .{}); | |
| 471 | } | |
| 472 | }; | |
| 473 | ||
| 474 | pub const Signed = struct { | |
| 475 | addend: i64, | |
| 476 | correction: i4, | |
| 477 | ||
| 478 | pub fn resolve(self: Signed, args: ResolveArgs) !void { | |
| 479 | const target_addr = @intCast(i64, args.target_addr) + self.addend; | |
| 480 | const displacement = try math.cast( | |
| 481 | i32, | |
| 482 | target_addr - @intCast(i64, args.source_addr) - self.correction - 4, | |
| 483 | ); | |
| 484 | mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 485 | } | |
| 486 | ||
| 487 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 488 | _ = fmt; | |
| 489 | _ = options; | |
| 490 | try std.fmt.format(writer, "Signed {{ ", .{}); | |
| 491 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 492 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 493 | try std.fmt.format(writer, "}}", .{}); | |
| 494 | } | |
| 495 | }; | |
| 496 | ||
| 497 | pub const Load = struct { | |
| 498 | kind: enum { | |
| 499 | got, | |
| 500 | tlvp, | |
| 501 | }, | |
| 502 | addend: i32 = 0, | |
| 503 | ||
| 504 | pub fn resolve(self: Load, args: ResolveArgs) !void { | |
| 505 | if (self.kind == .tlvp) { | |
| 506 | // We need to rewrite the opcode from movq to leaq. | |
| 507 | args.block.code.items[args.offset - 2] = 0x8d; | |
| 508 | } | |
| 509 | const displacement = try math.cast( | |
| 510 | i32, | |
| 511 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend, | |
| 512 | ); | |
| 513 | mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 514 | } | |
| 515 | ||
| 516 | pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 517 | _ = fmt; | |
| 518 | _ = options; | |
| 519 | try std.fmt.format(writer, "Load {{ ", .{}); | |
| 520 | try std.fmt.format(writer, "{s}, ", .{self.kind}); | |
| 521 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 522 | try std.fmt.format(writer, "}}", .{}); | |
| 523 | } | |
| 524 | }; | |
| 525 | ||
| 526 | pub fn resolve(self: Relocation, args: ResolveArgs) !void { | |
| 527 | switch (self.payload) { | |
| 528 | .unsigned => |unsigned| try unsigned.resolve(args), | |
| 529 | .branch => |branch| try branch.resolve(args), | |
| 530 | .page => |page| try page.resolve(args), | |
| 531 | .page_off => |page_off| try page_off.resolve(args), | |
| 532 | .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args), | |
| 533 | .signed => |signed| try signed.resolve(args), | |
| 534 | .load => |load| try load.resolve(args), | |
| 535 | } | |
| 536 | } | |
| 537 | ||
| 538 | pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 539 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 540 | try std.fmt.format(writer, ".offset = {}, ", .{self.offset}); | |
| 541 | try std.fmt.format(writer, ".where = {}, ", .{self.where}); | |
| 542 | try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index}); | |
| 543 | ||
| 544 | switch (self.payload) { | |
| 545 | .unsigned => |unsigned| try unsigned.format(fmt, options, writer), | |
| 546 | .branch => |branch| try branch.format(fmt, options, writer), | |
| 547 | .page => |page| try page.format(fmt, options, writer), | |
| 548 | .page_off => |page_off| try page_off.format(fmt, options, writer), | |
| 549 | .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer), | |
| 550 | .signed => |signed| try signed.format(fmt, options, writer), | |
| 551 | .load => |load| try load.format(fmt, options, writer), | |
| 552 | } | |
| 553 | ||
| 554 | try std.fmt.format(writer, "}}", .{}); | |
| 555 | } | |
| 556 | }; | |
| 557 | ||
| 558 | pub const empty = TextBlock{ | |
| 559 | .local_sym_index = 0, | |
| 560 | .size = 0, | |
| 561 | .alignment = 0, | |
| 562 | .prev = null, | |
| 563 | .next = null, | |
| 564 | .dbg_info_prev = null, | |
| 565 | .dbg_info_next = null, | |
| 566 | .dbg_info_off = undefined, | |
| 567 | .dbg_info_len = undefined, | |
| 568 | }; | |
| 569 | ||
| 570 | pub fn deinit(self: *TextBlock, allocator: *Allocator) void { | |
| 571 | self.dices.deinit(allocator); | |
| 572 | self.bindings.deinit(allocator); | |
| 573 | self.rebases.deinit(allocator); | |
| 574 | self.relocs.deinit(allocator); | |
| 575 | self.contained.deinit(allocator); | |
| 576 | self.aliases.deinit(allocator); | |
| 577 | self.code.deinit(allocator); | |
| 578 | } | |
| 579 | ||
| 580 | /// Returns how much room there is to grow in virtual address space. | |
| 581 | /// File offset relocation happens transparently, so it is not included in | |
| 582 | /// this calculation. | |
| 583 | pub fn capacity(self: TextBlock, macho_file: MachO) u64 { | |
| 584 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 585 | if (self.next) |next| { | |
| 586 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 587 | return next_sym.n_value - self_sym.n_value; | |
| 588 | } else { | |
| 589 | // We are the last block. | |
| 590 | // The capacity is limited only by virtual address space. | |
| 591 | return std.math.maxInt(u64) - self_sym.n_value; | |
| 592 | } | |
| 593 | } | |
| 594 | ||
| 595 | pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool { | |
| 596 | // No need to keep a free list node for the last block. | |
| 597 | const next = self.next orelse return false; | |
| 598 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 599 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 600 | const cap = next_sym.n_value - self_sym.n_value; | |
| 601 | const ideal_cap = MachO.padToIdeal(self.size); | |
| 602 | if (cap <= ideal_cap) return false; | |
| 603 | const surplus = cap - ideal_cap; | |
| 604 | return surplus >= MachO.min_text_capacity; | |
| 605 | } | |
| 606 | ||
| 607 | const RelocContext = struct { | |
| 608 | base_addr: u64 = 0, | |
| 609 | macho_file: *MachO, | |
| 610 | }; | |
| 611 | ||
| 612 | fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation { | |
| 613 | var parsed_rel = Relocation{ | |
| 614 | .offset = @intCast(u32, @intCast(u64, rel.r_address) - ctx.base_addr), | |
| 615 | .where = undefined, | |
| 616 | .where_index = undefined, | |
| 617 | .payload = undefined, | |
| 618 | }; | |
| 619 | ||
| 620 | if (rel.r_extern == 0) { | |
| 621 | const sect_id = @intCast(u16, rel.r_symbolnum - 1); | |
| 622 | ||
| 623 | const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: { | |
| 624 | const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; | |
| 625 | const sect = seg.sections.items[sect_id]; | |
| 626 | const match = (try ctx.macho_file.getMatchingSection(sect)) orelse unreachable; | |
| 627 | const local_sym_index = @intCast(u32, ctx.macho_file.locals.items.len); | |
| 628 | const sym_name = try std.fmt.allocPrint(ctx.macho_file.base.allocator, "l_{s}_{s}_{s}", .{ | |
| 629 | object.name.?, | |
| 630 | commands.segmentName(sect), | |
| 631 | commands.sectionName(sect), | |
| 632 | }); | |
| 633 | defer ctx.macho_file.base.allocator.free(sym_name); | |
| 634 | ||
| 635 | try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{ | |
| 636 | .n_strx = try ctx.macho_file.makeString(sym_name), | |
| 637 | .n_type = macho.N_SECT, | |
| 638 | .n_sect = ctx.macho_file.section_to_ordinal.get(match) orelse unreachable, | |
| 639 | .n_desc = 0, | |
| 640 | .n_value = sect.addr, | |
| 641 | }); | |
| 642 | try object.sections_as_symbols.putNoClobber(object.allocator, sect_id, local_sym_index); | |
| 643 | break :blk local_sym_index; | |
| 644 | }; | |
| 645 | ||
| 646 | parsed_rel.where = .local; | |
| 647 | parsed_rel.where_index = local_sym_index; | |
| 648 | } else { | |
| 649 | const sym = object.symtab.items[rel.r_symbolnum]; | |
| 650 | const sym_name = object.getString(sym.n_strx); | |
| 651 | ||
| 652 | if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { | |
| 653 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; | |
| 654 | parsed_rel.where = .local; | |
| 655 | parsed_rel.where_index = where_index; | |
| 656 | } else { | |
| 657 | const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ | |
| 658 | .strtab = &ctx.macho_file.strtab, | |
| 659 | }) orelse unreachable; | |
| 660 | const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable; | |
| 661 | switch (resolv.where) { | |
| 662 | .global => { | |
| 663 | parsed_rel.where = .local; | |
| 664 | parsed_rel.where_index = resolv.local_sym_index; | |
| 665 | }, | |
| 666 | .import => { | |
| 667 | parsed_rel.where = .import; | |
| 668 | parsed_rel.where_index = resolv.where_index; | |
| 669 | }, | |
| 670 | else => unreachable, | |
| 671 | } | |
| 672 | } | |
| 673 | } | |
| 674 | ||
| 675 | return parsed_rel; | |
| 676 | } | |
| 677 | ||
| 678 | pub fn parseRelocsFromObject( | |
| 679 | self: *TextBlock, | |
| 680 | allocator: *Allocator, | |
| 681 | relocs: []macho.relocation_info, | |
| 682 | object: *Object, | |
| 683 | ctx: RelocContext, | |
| 684 | ) !void { | |
| 685 | const filtered_relocs = filterRelocs(relocs, ctx.base_addr, ctx.base_addr + self.size); | |
| 686 | var it = RelocIterator{ | |
| 687 | .buffer = filtered_relocs, | |
| 688 | }; | |
| 689 | ||
| 690 | var addend: u32 = 0; | |
| 691 | var subtractor: ?u32 = null; | |
| 692 | ||
| 693 | while (it.next()) |rel| { | |
| 694 | if (isAddend(rel, object.arch.?)) { | |
| 695 | // Addend is not a relocation with effect on the TextBlock, so | |
| 696 | // parse it and carry on. | |
| 697 | assert(addend == 0); // Oh no, addend was not reset! | |
| 698 | addend = rel.r_symbolnum; | |
| 699 | ||
| 700 | // Verify ADDEND is followed by a load. | |
| 701 | const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); | |
| 702 | switch (next) { | |
| 703 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 704 | else => { | |
| 705 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 706 | return error.UnexpectedRelocationType; | |
| 707 | }, | |
| 708 | } | |
| 709 | continue; | |
| 710 | } | |
| 711 | ||
| 712 | if (isSubtractor(rel, object.arch.?)) { | |
| 713 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 714 | // parse it and carry on. | |
| 715 | assert(subtractor == null); // Oh no, subtractor was not reset! | |
| 716 | assert(rel.r_extern == 1); | |
| 717 | const sym = object.symtab.items[rel.r_symbolnum]; | |
| 718 | const sym_name = object.getString(sym.n_strx); | |
| 719 | ||
| 720 | if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { | |
| 721 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; | |
| 722 | subtractor = where_index; | |
| 723 | } else { | |
| 724 | const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ | |
| 725 | .strtab = &ctx.macho_file.strtab, | |
| 726 | }) orelse unreachable; | |
| 727 | const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable; | |
| 728 | assert(resolv.where == .global); | |
| 729 | subtractor = resolv.local_sym_index; | |
| 730 | } | |
| 731 | ||
| 732 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 733 | switch (object.arch.?) { | |
| 734 | .aarch64 => { | |
| 735 | const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); | |
| 736 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 737 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 738 | return error.UnexpectedRelocationType; | |
| 739 | } | |
| 740 | }, | |
| 741 | .x86_64 => { | |
| 742 | const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type); | |
| 743 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 744 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 745 | return error.UnexpectedRelocationType; | |
| 746 | } | |
| 747 | }, | |
| 748 | else => unreachable, | |
| 749 | } | |
| 750 | continue; | |
| 751 | } | |
| 752 | ||
| 753 | var parsed_rel = try initRelocFromObject(rel, object, ctx); | |
| 754 | ||
| 755 | switch (object.arch.?) { | |
| 756 | .aarch64 => { | |
| 757 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 758 | switch (rel_type) { | |
| 759 | .ARM64_RELOC_ADDEND => unreachable, | |
| 760 | .ARM64_RELOC_SUBTRACTOR => unreachable, | |
| 761 | .ARM64_RELOC_BRANCH26 => { | |
| 762 | self.parseBranch(rel, &parsed_rel, ctx); | |
| 763 | }, | |
| 764 | .ARM64_RELOC_UNSIGNED => { | |
| 765 | self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); | |
| 766 | subtractor = null; | |
| 767 | }, | |
| 768 | .ARM64_RELOC_PAGE21, | |
| 769 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 770 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 771 | => { | |
| 772 | self.parsePage(rel, &parsed_rel, addend); | |
| 773 | if (rel_type == .ARM64_RELOC_PAGE21) | |
| 774 | addend = 0; | |
| 775 | }, | |
| 776 | .ARM64_RELOC_PAGEOFF12, | |
| 777 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | |
| 778 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | |
| 779 | => { | |
| 780 | self.parsePageOff(rel, &parsed_rel, addend); | |
| 781 | if (rel_type == .ARM64_RELOC_PAGEOFF12) | |
| 782 | addend = 0; | |
| 783 | }, | |
| 784 | .ARM64_RELOC_POINTER_TO_GOT => { | |
| 785 | self.parsePointerToGot(rel, &parsed_rel); | |
| 786 | }, | |
| 787 | } | |
| 788 | }, | |
| 789 | .x86_64 => { | |
| 790 | switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 791 | .X86_64_RELOC_SUBTRACTOR => unreachable, | |
| 792 | .X86_64_RELOC_BRANCH => { | |
| 793 | self.parseBranch(rel, &parsed_rel, ctx); | |
| 794 | }, | |
| 795 | .X86_64_RELOC_UNSIGNED => { | |
| 796 | self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); | |
| 797 | subtractor = null; | |
| 798 | }, | |
| 799 | .X86_64_RELOC_SIGNED, | |
| 800 | .X86_64_RELOC_SIGNED_1, | |
| 801 | .X86_64_RELOC_SIGNED_2, | |
| 802 | .X86_64_RELOC_SIGNED_4, | |
| 803 | => { | |
| 804 | self.parseSigned(rel, &parsed_rel, ctx); | |
| 805 | }, | |
| 806 | .X86_64_RELOC_GOT_LOAD, | |
| 807 | .X86_64_RELOC_GOT, | |
| 808 | .X86_64_RELOC_TLV, | |
| 809 | => { | |
| 810 | self.parseLoad(rel, &parsed_rel); | |
| 811 | }, | |
| 812 | } | |
| 813 | }, | |
| 814 | else => unreachable, | |
| 815 | } | |
| 816 | ||
| 817 | try self.relocs.append(allocator, parsed_rel); | |
| 818 | ||
| 819 | const is_via_got = switch (parsed_rel.payload) { | |
| 820 | .pointer_to_got => true, | |
| 821 | .load => |load| load.kind == .got, | |
| 822 | .page => |page| page.kind == .got, | |
| 823 | .page_off => |page_off| page_off.kind == .got, | |
| 824 | else => false, | |
| 825 | }; | |
| 826 | ||
| 827 | if (is_via_got) blk: { | |
| 828 | const key = MachO.GotIndirectionKey{ | |
| 829 | .where = switch (parsed_rel.where) { | |
| 830 | .local => .local, | |
| 831 | .import => .import, | |
| 832 | }, | |
| 833 | .where_index = parsed_rel.where_index, | |
| 834 | }; | |
| 835 | if (ctx.macho_file.got_entries_map.contains(key)) break :blk; | |
| 836 | ||
| 837 | const got_index = @intCast(u32, ctx.macho_file.got_entries.items.len); | |
| 838 | try ctx.macho_file.got_entries.append(ctx.macho_file.base.allocator, key); | |
| 839 | try ctx.macho_file.got_entries_map.putNoClobber(ctx.macho_file.base.allocator, key, got_index); | |
| 840 | } else if (parsed_rel.payload == .unsigned) { | |
| 841 | switch (parsed_rel.where) { | |
| 842 | .import => { | |
| 843 | try self.bindings.append(allocator, .{ | |
| 844 | .local_sym_index = parsed_rel.where_index, | |
| 845 | .offset = parsed_rel.offset, | |
| 846 | }); | |
| 847 | }, | |
| 848 | .local => { | |
| 849 | const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; | |
| 850 | const match = ctx.macho_file.section_ordinals.items[source_sym.n_sect]; | |
| 851 | const seg = ctx.macho_file.load_commands.items[match.seg].Segment; | |
| 852 | const sect = seg.sections.items[match.sect]; | |
| 853 | const sect_type = commands.sectionType(sect); | |
| 854 | ||
| 855 | const should_rebase = rebase: { | |
| 856 | if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false; | |
| 857 | ||
| 858 | // TODO actually, a check similar to what dyld is doing, that is, verifying | |
| 859 | // that the segment is writable should be enough here. | |
| 860 | const is_right_segment = blk: { | |
| 861 | if (ctx.macho_file.data_segment_cmd_index) |idx| { | |
| 862 | if (match.seg == idx) { | |
| 863 | break :blk true; | |
| 864 | } | |
| 865 | } | |
| 866 | if (ctx.macho_file.data_const_segment_cmd_index) |idx| { | |
| 867 | if (match.seg == idx) { | |
| 868 | break :blk true; | |
| 869 | } | |
| 870 | } | |
| 871 | break :blk false; | |
| 872 | }; | |
| 873 | ||
| 874 | if (!is_right_segment) break :rebase false; | |
| 875 | if (sect_type != macho.S_LITERAL_POINTERS and | |
| 876 | sect_type != macho.S_REGULAR and | |
| 877 | sect_type != macho.S_MOD_INIT_FUNC_POINTERS and | |
| 878 | sect_type != macho.S_MOD_TERM_FUNC_POINTERS) | |
| 879 | { | |
| 880 | break :rebase false; | |
| 881 | } | |
| 882 | ||
| 883 | break :rebase true; | |
| 884 | }; | |
| 885 | ||
| 886 | if (should_rebase) { | |
| 887 | try self.rebases.append(allocator, parsed_rel.offset); | |
| 888 | } | |
| 889 | }, | |
| 890 | } | |
| 891 | } else if (parsed_rel.payload == .branch) blk: { | |
| 892 | if (parsed_rel.where != .import) break :blk; | |
| 893 | if (ctx.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk; | |
| 894 | ||
| 895 | const stubs_index = @intCast(u32, ctx.macho_file.stubs.items.len); | |
| 896 | try ctx.macho_file.stubs.append(ctx.macho_file.base.allocator, parsed_rel.where_index); | |
| 897 | try ctx.macho_file.stubs_map.putNoClobber(ctx.macho_file.base.allocator, parsed_rel.where_index, stubs_index); | |
| 898 | } | |
| 899 | } | |
| 900 | } | |
| 901 | ||
| 902 | fn isAddend(rel: macho.relocation_info, arch: Arch) bool { | |
| 903 | if (arch != .aarch64) return false; | |
| 904 | return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND; | |
| 905 | } | |
| 906 | ||
| 907 | fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool { | |
| 908 | return switch (arch) { | |
| 909 | .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR, | |
| 910 | .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR, | |
| 911 | else => unreachable, | |
| 912 | }; | |
| 913 | } | |
| 914 | ||
| 915 | fn parseUnsigned( | |
| 916 | self: TextBlock, | |
| 917 | rel: macho.relocation_info, | |
| 918 | out: *Relocation, | |
| 919 | subtractor: ?u32, | |
| 920 | ctx: RelocContext, | |
| 921 | ) void { | |
| 922 | assert(rel.r_pcrel == 0); | |
| 923 | ||
| 924 | const is_64bit: bool = switch (rel.r_length) { | |
| 925 | 3 => true, | |
| 926 | 2 => false, | |
| 927 | else => unreachable, | |
| 928 | }; | |
| 929 | ||
| 930 | var addend: i64 = if (is_64bit) | |
| 931 | mem.readIntLittle(i64, self.code.items[out.offset..][0..8]) | |
| 932 | else | |
| 933 | mem.readIntLittle(i32, self.code.items[out.offset..][0..4]); | |
| 934 | ||
| 935 | if (rel.r_extern == 0) { | |
| 936 | assert(out.where == .local); | |
| 937 | const target_sym = ctx.macho_file.locals.items[out.where_index]; | |
| 938 | addend -= @intCast(i64, target_sym.n_value); | |
| 939 | } | |
| 940 | ||
| 941 | out.payload = .{ | |
| 942 | .unsigned = .{ | |
| 943 | .subtractor = subtractor, | |
| 944 | .is_64bit = is_64bit, | |
| 945 | .addend = addend, | |
| 946 | }, | |
| 947 | }; | |
| 948 | } | |
| 949 | ||
| 950 | fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { | |
| 951 | _ = self; | |
| 952 | assert(rel.r_pcrel == 1); | |
| 953 | assert(rel.r_length == 2); | |
| 954 | ||
| 955 | out.payload = .{ | |
| 956 | .branch = .{ | |
| 957 | .arch = ctx.macho_file.base.options.target.cpu.arch, | |
| 958 | }, | |
| 959 | }; | |
| 960 | } | |
| 961 | ||
| 962 | fn parsePage(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void { | |
| 963 | _ = self; | |
| 964 | assert(rel.r_pcrel == 1); | |
| 965 | assert(rel.r_length == 2); | |
| 966 | ||
| 967 | out.payload = .{ | |
| 968 | .page = .{ | |
| 969 | .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 970 | .ARM64_RELOC_PAGE21 => .page, | |
| 971 | .ARM64_RELOC_GOT_LOAD_PAGE21 => .got, | |
| 972 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp, | |
| 973 | else => unreachable, | |
| 974 | }, | |
| 975 | .addend = addend, | |
| 976 | }, | |
| 977 | }; | |
| 978 | } | |
| 979 | ||
| 980 | fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void { | |
| 981 | assert(rel.r_pcrel == 0); | |
| 982 | assert(rel.r_length == 2); | |
| 983 | ||
| 984 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 985 | const op_kind: ?Relocation.PageOff.OpKind = blk: { | |
| 986 | if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null; | |
| 987 | const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code.items[out.offset..][0..4])) | |
| 988 | .arithmetic | |
| 989 | else | |
| 990 | .load; | |
| 991 | break :blk op_kind; | |
| 992 | }; | |
| 993 | ||
| 994 | out.payload = .{ | |
| 995 | .page_off = .{ | |
| 996 | .kind = switch (rel_type) { | |
| 997 | .ARM64_RELOC_PAGEOFF12 => .page, | |
| 998 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got, | |
| 999 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp, | |
| 1000 | else => unreachable, | |
| 1001 | }, | |
| 1002 | .addend = addend, | |
| 1003 | .op_kind = op_kind, | |
| 1004 | }, | |
| 1005 | }; | |
| 1006 | } | |
| 1007 | ||
| 1008 | fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void { | |
| 1009 | _ = self; | |
| 1010 | assert(rel.r_pcrel == 1); | |
| 1011 | assert(rel.r_length == 2); | |
| 1012 | ||
| 1013 | out.payload = .{ | |
| 1014 | .pointer_to_got = .{}, | |
| 1015 | }; | |
| 1016 | } | |
| 1017 | ||
| 1018 | fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { | |
| 1019 | assert(rel.r_pcrel == 1); | |
| 1020 | assert(rel.r_length == 2); | |
| 1021 | ||
| 1022 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 1023 | const correction: i4 = switch (rel_type) { | |
| 1024 | .X86_64_RELOC_SIGNED => 0, | |
| 1025 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 1026 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 1027 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 1028 | else => unreachable, | |
| 1029 | }; | |
| 1030 | var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction; | |
| 1031 | ||
| 1032 | if (rel.r_extern == 0) { | |
| 1033 | const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; | |
| 1034 | const target_sym = switch (out.where) { | |
| 1035 | .local => ctx.macho_file.locals.items[out.where_index], | |
| 1036 | .import => ctx.macho_file.imports.items[out.where_index], | |
| 1037 | }; | |
| 1038 | addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value); | |
| 1039 | } | |
| 1040 | ||
| 1041 | out.payload = .{ | |
| 1042 | .signed = .{ | |
| 1043 | .correction = correction, | |
| 1044 | .addend = addend, | |
| 1045 | }, | |
| 1046 | }; | |
| 1047 | } | |
| 1048 | ||
| 1049 | fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void { | |
| 1050 | assert(rel.r_pcrel == 1); | |
| 1051 | assert(rel.r_length == 2); | |
| 1052 | ||
| 1053 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 1054 | const addend: i32 = if (rel_type == .X86_64_RELOC_GOT) | |
| 1055 | mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) | |
| 1056 | else | |
| 1057 | 0; | |
| 1058 | ||
| 1059 | out.payload = .{ | |
| 1060 | .load = .{ | |
| 1061 | .kind = switch (rel_type) { | |
| 1062 | .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got, | |
| 1063 | .X86_64_RELOC_TLV => .tlvp, | |
| 1064 | else => unreachable, | |
| 1065 | }, | |
| 1066 | .addend = addend, | |
| 1067 | }, | |
| 1068 | }; | |
| 1069 | } | |
| 1070 | ||
| 1071 | pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void { | |
| 1072 | for (self.relocs.items) |rel| { | |
| 1073 | log.debug("relocating {}", .{rel}); | |
| 1074 | ||
| 1075 | const source_addr = blk: { | |
| 1076 | const sym = macho_file.locals.items[self.local_sym_index]; | |
| 1077 | break :blk sym.n_value + rel.offset; | |
| 1078 | }; | |
| 1079 | const target_addr = blk: { | |
| 1080 | const is_via_got = switch (rel.payload) { | |
| 1081 | .pointer_to_got => true, | |
| 1082 | .page => |page| page.kind == .got, | |
| 1083 | .page_off => |page_off| page_off.kind == .got, | |
| 1084 | .load => |load| load.kind == .got, | |
| 1085 | else => false, | |
| 1086 | }; | |
| 1087 | ||
| 1088 | if (is_via_got) { | |
| 1089 | const dc_seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | |
| 1090 | const got = dc_seg.sections.items[macho_file.got_section_index.?]; | |
| 1091 | const got_index = macho_file.got_entries_map.get(.{ | |
| 1092 | .where = switch (rel.where) { | |
| 1093 | .local => .local, | |
| 1094 | .import => .import, | |
| 1095 | }, | |
| 1096 | .where_index = rel.where_index, | |
| 1097 | }) orelse { | |
| 1098 | const sym = switch (rel.where) { | |
| 1099 | .local => macho_file.locals.items[rel.where_index], | |
| 1100 | .import => macho_file.imports.items[rel.where_index], | |
| 1101 | }; | |
| 1102 | log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)}); | |
| 1103 | log.err(" this is an internal linker error", .{}); | |
| 1104 | return error.FailedToResolveRelocationTarget; | |
| 1105 | }; | |
| 1106 | break :blk got.addr + got_index * @sizeOf(u64); | |
| 1107 | } | |
| 1108 | ||
| 1109 | switch (rel.where) { | |
| 1110 | .local => { | |
| 1111 | const sym = macho_file.locals.items[rel.where_index]; | |
| 1112 | const is_tlv = is_tlv: { | |
| 1113 | const source_sym = macho_file.locals.items[self.local_sym_index]; | |
| 1114 | const match = macho_file.section_ordinals.items[source_sym.n_sect]; | |
| 1115 | const seg = macho_file.load_commands.items[match.seg].Segment; | |
| 1116 | const sect = seg.sections.items[match.sect]; | |
| 1117 | break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES; | |
| 1118 | }; | |
| 1119 | if (is_tlv) { | |
| 1120 | // For TLV relocations, the value specified as a relocation is the displacement from the | |
| 1121 | // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first | |
| 1122 | // defined TLV template init section in the following order: | |
| 1123 | // * wrt to __thread_data if defined, then | |
| 1124 | // * wrt to __thread_bss | |
| 1125 | const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment; | |
| 1126 | const base_address = inner: { | |
| 1127 | if (macho_file.tlv_data_section_index) |i| { | |
| 1128 | break :inner seg.sections.items[i].addr; | |
| 1129 | } else if (macho_file.tlv_bss_section_index) |i| { | |
| 1130 | break :inner seg.sections.items[i].addr; | |
| 1131 | } else { | |
| 1132 | log.err("threadlocal variables present but no initializer sections found", .{}); | |
| 1133 | log.err(" __thread_data not found", .{}); | |
| 1134 | log.err(" __thread_bss not found", .{}); | |
| 1135 | return error.FailedToResolveRelocationTarget; | |
| 1136 | } | |
| 1137 | }; | |
| 1138 | break :blk sym.n_value - base_address; | |
| 1139 | } | |
| 1140 | ||
| 1141 | break :blk sym.n_value; | |
| 1142 | }, | |
| 1143 | .import => { | |
| 1144 | const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse { | |
| 1145 | // TODO verify in TextBlock that the symbol is indeed dynamically bound. | |
| 1146 | break :blk 0; // Dynamically bound by dyld. | |
| 1147 | }; | |
| 1148 | const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment; | |
| 1149 | const stubs = segment.sections.items[macho_file.stubs_section_index.?]; | |
| 1150 | break :blk stubs.addr + stubs_index * stubs.reserved2; | |
| 1151 | }, | |
| 1152 | } | |
| 1153 | }; | |
| 1154 | ||
| 1155 | log.debug(" | source_addr = 0x{x}", .{source_addr}); | |
| 1156 | log.debug(" | target_addr = 0x{x}", .{target_addr}); | |
| 1157 | ||
| 1158 | try rel.resolve(.{ | |
| 1159 | .block = self, | |
| 1160 | .offset = rel.offset, | |
| 1161 | .source_addr = source_addr, | |
| 1162 | .target_addr = target_addr, | |
| 1163 | .macho_file = macho_file, | |
| 1164 | }); | |
| 1165 | } | |
| 1166 | } | |
| 1167 | ||
| 1168 | pub fn format(self: TextBlock, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 1169 | _ = fmt; | |
| 1170 | _ = options; | |
| 1171 | try std.fmt.format(writer, "TextBlock {{ ", .{}); | |
| 1172 | try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index}); | |
| 1173 | try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items}); | |
| 1174 | try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items}); | |
| 1175 | try std.fmt.format(writer, ".code = {*}, ", .{self.code.items}); | |
| 1176 | try std.fmt.format(writer, ".size = {d}, ", .{self.size}); | |
| 1177 | try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment}); | |
| 1178 | try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items}); | |
| 1179 | try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items}); | |
| 1180 | try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items}); | |
| 1181 | try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items}); | |
| 1182 | if (self.stab) |stab| { | |
| 1183 | try std.fmt.format(writer, ".stab = {any}, ", .{stab}); | |
| 1184 | } | |
| 1185 | try std.fmt.format(writer, "}}", .{}); | |
| 1186 | } | |
| 1187 | ||
| 1188 | const RelocIterator = struct { | |
| 1189 | buffer: []const macho.relocation_info, | |
| 1190 | index: i32 = -1, | |
| 1191 | ||
| 1192 | pub fn next(self: *RelocIterator) ?macho.relocation_info { | |
| 1193 | self.index += 1; | |
| 1194 | if (self.index < self.buffer.len) { | |
| 1195 | return self.buffer[@intCast(u32, self.index)]; | |
| 1196 | } | |
| 1197 | return null; | |
| 1198 | } | |
| 1199 | ||
| 1200 | pub fn peek(self: RelocIterator) macho.relocation_info { | |
| 1201 | assert(self.index + 1 < self.buffer.len); | |
| 1202 | return self.buffer[@intCast(u32, self.index + 1)]; | |
| 1203 | } | |
| 1204 | }; | |
| 1205 | ||
| 1206 | fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info { | |
| 1207 | const Predicate = struct { | |
| 1208 | addr: u64, | |
| 1209 | ||
| 1210 | pub fn predicate(self: @This(), rel: macho.relocation_info) bool { | |
| 1211 | return rel.r_address < self.addr; | |
| 1212 | } | |
| 1213 | }; | |
| 1214 | ||
| 1215 | const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr }); | |
| 1216 | const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr }); | |
| 1217 | ||
| 1218 | return relocs[start..end]; | |
| 1219 | } | |
| 1220 | ||
| 1221 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 1222 | const group_decode = @truncate(u5, inst[3]); | |
| 1223 | return ((group_decode >> 2) == 4); | |
| 1224 | } |
src/link/MachO/Zld.zig deleted-3209| ... | ... | @@ -1,3209 +0,0 @@ |
| 1 | const Zld = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const leb = std.leb; | |
| 6 | const mem = std.mem; | |
| 7 | const meta = std.meta; | |
| 8 | const fs = std.fs; | |
| 9 | const macho = std.macho; | |
| 10 | const math = std.math; | |
| 11 | const log = std.log.scoped(.zld); | |
| 12 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 13 | const reloc = @import("reloc.zig"); | |
| 14 | ||
| 15 | const Allocator = mem.Allocator; | |
| 16 | const Archive = @import("Archive.zig"); | |
| 17 | const CodeSignature = @import("CodeSignature.zig"); | |
| 18 | const Dylib = @import("Dylib.zig"); | |
| 19 | const Object = @import("Object.zig"); | |
| 20 | const Symbol = @import("Symbol.zig"); | |
| 21 | const Trie = @import("Trie.zig"); | |
| 22 | ||
| 23 | usingnamespace @import("commands.zig"); | |
| 24 | usingnamespace @import("bind.zig"); | |
| 25 | ||
| 26 | allocator: *Allocator, | |
| 27 | ||
| 28 | target: ?std.Target = null, | |
| 29 | page_size: ?u16 = null, | |
| 30 | file: ?fs.File = null, | |
| 31 | output: ?Output = null, | |
| 32 | ||
| 33 | // TODO these args will become obselete once Zld is coalesced with incremental | |
| 34 | // linker. | |
| 35 | stack_size: u64 = 0, | |
| 36 | ||
| 37 | objects: std.ArrayListUnmanaged(*Object) = .{}, | |
| 38 | archives: std.ArrayListUnmanaged(*Archive) = .{}, | |
| 39 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 40 | ||
| 41 | next_dylib_ordinal: u16 = 1, | |
| 42 | ||
| 43 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | |
| 44 | ||
| 45 | pagezero_segment_cmd_index: ?u16 = null, | |
| 46 | text_segment_cmd_index: ?u16 = null, | |
| 47 | data_const_segment_cmd_index: ?u16 = null, | |
| 48 | data_segment_cmd_index: ?u16 = null, | |
| 49 | linkedit_segment_cmd_index: ?u16 = null, | |
| 50 | dyld_info_cmd_index: ?u16 = null, | |
| 51 | symtab_cmd_index: ?u16 = null, | |
| 52 | dysymtab_cmd_index: ?u16 = null, | |
| 53 | dylinker_cmd_index: ?u16 = null, | |
| 54 | data_in_code_cmd_index: ?u16 = null, | |
| 55 | function_starts_cmd_index: ?u16 = null, | |
| 56 | main_cmd_index: ?u16 = null, | |
| 57 | dylib_id_cmd_index: ?u16 = null, | |
| 58 | version_min_cmd_index: ?u16 = null, | |
| 59 | source_version_cmd_index: ?u16 = null, | |
| 60 | uuid_cmd_index: ?u16 = null, | |
| 61 | code_signature_cmd_index: ?u16 = null, | |
| 62 | ||
| 63 | // __TEXT segment sections | |
| 64 | text_section_index: ?u16 = null, | |
| 65 | stubs_section_index: ?u16 = null, | |
| 66 | stub_helper_section_index: ?u16 = null, | |
| 67 | text_const_section_index: ?u16 = null, | |
| 68 | cstring_section_index: ?u16 = null, | |
| 69 | ustring_section_index: ?u16 = null, | |
| 70 | gcc_except_tab_section_index: ?u16 = null, | |
| 71 | unwind_info_section_index: ?u16 = null, | |
| 72 | eh_frame_section_index: ?u16 = null, | |
| 73 | ||
| 74 | objc_methlist_section_index: ?u16 = null, | |
| 75 | objc_methname_section_index: ?u16 = null, | |
| 76 | objc_methtype_section_index: ?u16 = null, | |
| 77 | objc_classname_section_index: ?u16 = null, | |
| 78 | ||
| 79 | // __DATA_CONST segment sections | |
| 80 | got_section_index: ?u16 = null, | |
| 81 | mod_init_func_section_index: ?u16 = null, | |
| 82 | mod_term_func_section_index: ?u16 = null, | |
| 83 | data_const_section_index: ?u16 = null, | |
| 84 | ||
| 85 | objc_cfstring_section_index: ?u16 = null, | |
| 86 | objc_classlist_section_index: ?u16 = null, | |
| 87 | objc_imageinfo_section_index: ?u16 = null, | |
| 88 | ||
| 89 | // __DATA segment sections | |
| 90 | tlv_section_index: ?u16 = null, | |
| 91 | tlv_data_section_index: ?u16 = null, | |
| 92 | tlv_bss_section_index: ?u16 = null, | |
| 93 | la_symbol_ptr_section_index: ?u16 = null, | |
| 94 | data_section_index: ?u16 = null, | |
| 95 | bss_section_index: ?u16 = null, | |
| 96 | common_section_index: ?u16 = null, | |
| 97 | ||
| 98 | objc_const_section_index: ?u16 = null, | |
| 99 | objc_selrefs_section_index: ?u16 = null, | |
| 100 | objc_classrefs_section_index: ?u16 = null, | |
| 101 | objc_data_section_index: ?u16 = null, | |
| 102 | ||
| 103 | globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | |
| 104 | imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | |
| 105 | unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | |
| 106 | tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | |
| 107 | ||
| 108 | /// Offset into __DATA,__common section. | |
| 109 | /// Set if the linker found tentative definitions in any of the objects. | |
| 110 | tentative_defs_offset: u64 = 0, | |
| 111 | ||
| 112 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 113 | strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, | |
| 114 | ||
| 115 | threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction | |
| 116 | local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, | |
| 117 | stubs: std.ArrayListUnmanaged(*Symbol) = .{}, | |
| 118 | got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, | |
| 119 | ||
| 120 | stub_helper_stubs_start_off: ?u64 = null, | |
| 121 | ||
| 122 | pub const Output = struct { | |
| 123 | tag: enum { exe, dylib }, | |
| 124 | path: []const u8, | |
| 125 | install_name: ?[]const u8 = null, | |
| 126 | }; | |
| 127 | ||
| 128 | const TlvOffset = struct { | |
| 129 | source_addr: u64, | |
| 130 | offset: u64, | |
| 131 | ||
| 132 | fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool { | |
| 133 | _ = context; | |
| 134 | return a.source_addr < b.source_addr; | |
| 135 | } | |
| 136 | }; | |
| 137 | ||
| 138 | /// Default path to dyld | |
| 139 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; | |
| 140 | ||
| 141 | pub fn init(allocator: *Allocator) Zld { | |
| 142 | return .{ .allocator = allocator }; | |
| 143 | } | |
| 144 | ||
| 145 | pub fn deinit(self: *Zld) void { | |
| 146 | self.threadlocal_offsets.deinit(self.allocator); | |
| 147 | self.local_rebases.deinit(self.allocator); | |
| 148 | self.stubs.deinit(self.allocator); | |
| 149 | self.got_entries.deinit(self.allocator); | |
| 150 | ||
| 151 | for (self.load_commands.items) |*lc| { | |
| 152 | lc.deinit(self.allocator); | |
| 153 | } | |
| 154 | self.load_commands.deinit(self.allocator); | |
| 155 | ||
| 156 | for (self.objects.items) |object| { | |
| 157 | object.deinit(); | |
| 158 | self.allocator.destroy(object); | |
| 159 | } | |
| 160 | self.objects.deinit(self.allocator); | |
| 161 | ||
| 162 | for (self.archives.items) |archive| { | |
| 163 | archive.deinit(); | |
| 164 | self.allocator.destroy(archive); | |
| 165 | } | |
| 166 | self.archives.deinit(self.allocator); | |
| 167 | ||
| 168 | for (self.dylibs.items) |dylib| { | |
| 169 | dylib.deinit(); | |
| 170 | self.allocator.destroy(dylib); | |
| 171 | } | |
| 172 | self.dylibs.deinit(self.allocator); | |
| 173 | ||
| 174 | for (self.imports.values()) |proxy| { | |
| 175 | proxy.deinit(self.allocator); | |
| 176 | self.allocator.destroy(proxy); | |
| 177 | } | |
| 178 | self.imports.deinit(self.allocator); | |
| 179 | ||
| 180 | self.tentatives.deinit(self.allocator); | |
| 181 | self.globals.deinit(self.allocator); | |
| 182 | self.unresolved.deinit(self.allocator); | |
| 183 | self.strtab.deinit(self.allocator); | |
| 184 | ||
| 185 | { | |
| 186 | var it = self.strtab_dir.keyIterator(); | |
| 187 | while (it.next()) |key| { | |
| 188 | self.allocator.free(key.*); | |
| 189 | } | |
| 190 | } | |
| 191 | self.strtab_dir.deinit(self.allocator); | |
| 192 | } | |
| 193 | ||
| 194 | pub fn closeFiles(self: Zld) void { | |
| 195 | for (self.objects.items) |object| { | |
| 196 | object.closeFile(); | |
| 197 | } | |
| 198 | for (self.archives.items) |archive| { | |
| 199 | archive.closeFile(); | |
| 200 | } | |
| 201 | if (self.file) |f| f.close(); | |
| 202 | } | |
| 203 | ||
| 204 | const LinkArgs = struct { | |
| 205 | syslibroot: ?[]const u8, | |
| 206 | libs: []const []const u8, | |
| 207 | rpaths: []const []const u8, | |
| 208 | }; | |
| 209 | ||
| 210 | pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArgs) !void { | |
| 211 | if (files.len == 0) return error.NoInputFiles; | |
| 212 | if (output.path.len == 0) return error.EmptyOutputPath; | |
| 213 | ||
| 214 | self.page_size = switch (self.target.?.cpu.arch) { | |
| 215 | .aarch64 => 0x4000, | |
| 216 | .x86_64 => 0x1000, | |
| 217 | else => unreachable, | |
| 218 | }; | |
| 219 | self.output = output; | |
| 220 | self.file = try fs.cwd().createFile(self.output.?.path, .{ | |
| 221 | .truncate = true, | |
| 222 | .read = true, | |
| 223 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | |
| 224 | }); | |
| 225 | ||
| 226 | try self.populateMetadata(); | |
| 227 | try self.parseInputFiles(files, args.syslibroot); | |
| 228 | try self.parseLibs(args.libs, args.syslibroot); | |
| 229 | try self.resolveSymbols(); | |
| 230 | try self.resolveStubsAndGotEntries(); | |
| 231 | try self.updateMetadata(); | |
| 232 | try self.sortSections(); | |
| 233 | try self.addRpaths(args.rpaths); | |
| 234 | try self.addDataInCodeLC(); | |
| 235 | try self.addCodeSignatureLC(); | |
| 236 | try self.allocateTextSegment(); | |
| 237 | try self.allocateDataConstSegment(); | |
| 238 | try self.allocateDataSegment(); | |
| 239 | self.allocateLinkeditSegment(); | |
| 240 | try self.allocateSymbols(); | |
| 241 | try self.allocateTentativeSymbols(); | |
| 242 | try self.allocateProxyBindAddresses(); | |
| 243 | try self.flush(); | |
| 244 | } | |
| 245 | ||
| 246 | fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 247 | for (files) |file_name| { | |
| 248 | const full_path = full_path: { | |
| 249 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 250 | const path = try std.fs.realpath(file_name, &buffer); | |
| 251 | break :full_path try self.allocator.dupe(u8, path); | |
| 252 | }; | |
| 253 | ||
| 254 | if (try Object.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |object| { | |
| 255 | try self.objects.append(self.allocator, object); | |
| 256 | continue; | |
| 257 | } | |
| 258 | ||
| 259 | if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |archive| { | |
| 260 | try self.archives.append(self.allocator, archive); | |
| 261 | continue; | |
| 262 | } | |
| 263 | ||
| 264 | if (try Dylib.createAndParseFromPath( | |
| 265 | self.allocator, | |
| 266 | self.target.?.cpu.arch, | |
| 267 | full_path, | |
| 268 | .{ .syslibroot = syslibroot }, | |
| 269 | )) |dylibs| { | |
| 270 | defer self.allocator.free(dylibs); | |
| 271 | try self.dylibs.appendSlice(self.allocator, dylibs); | |
| 272 | continue; | |
| 273 | } | |
| 274 | ||
| 275 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | |
| 276 | } | |
| 277 | } | |
| 278 | ||
| 279 | fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 280 | for (libs) |lib| { | |
| 281 | if (try Dylib.createAndParseFromPath( | |
| 282 | self.allocator, | |
| 283 | self.target.?.cpu.arch, | |
| 284 | lib, | |
| 285 | .{ .syslibroot = syslibroot }, | |
| 286 | )) |dylibs| { | |
| 287 | defer self.allocator.free(dylibs); | |
| 288 | try self.dylibs.appendSlice(self.allocator, dylibs); | |
| 289 | continue; | |
| 290 | } | |
| 291 | ||
| 292 | if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, lib)) |archive| { | |
| 293 | try self.archives.append(self.allocator, archive); | |
| 294 | continue; | |
| 295 | } | |
| 296 | ||
| 297 | log.warn("unknown filetype for a library: '{s}'", .{lib}); | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | fn mapAndUpdateSections( | |
| 302 | self: *Zld, | |
| 303 | object: *Object, | |
| 304 | source_sect_id: u16, | |
| 305 | target_seg_id: u16, | |
| 306 | target_sect_id: u16, | |
| 307 | ) !void { | |
| 308 | const source_sect = &object.sections.items[source_sect_id]; | |
| 309 | const target_seg = &self.load_commands.items[target_seg_id].Segment; | |
| 310 | const target_sect = &target_seg.sections.items[target_sect_id]; | |
| 311 | ||
| 312 | const alignment = try math.powi(u32, 2, target_sect.@"align"); | |
| 313 | const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment); | |
| 314 | const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment); | |
| 315 | ||
| 316 | log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{ | |
| 317 | object.name.?, | |
| 318 | parseName(&source_sect.inner.segname), | |
| 319 | parseName(&source_sect.inner.sectname), | |
| 320 | parseName(&target_sect.segname), | |
| 321 | parseName(&target_sect.sectname), | |
| 322 | offset, | |
| 323 | offset + size, | |
| 324 | }); | |
| 325 | log.debug(" | flags 0x{x}", .{source_sect.inner.flags}); | |
| 326 | ||
| 327 | source_sect.target_map = .{ | |
| 328 | .segment_id = target_seg_id, | |
| 329 | .section_id = target_sect_id, | |
| 330 | .offset = @intCast(u32, offset), | |
| 331 | }; | |
| 332 | target_sect.size = offset + size; | |
| 333 | } | |
| 334 | ||
| 335 | fn updateMetadata(self: *Zld) !void { | |
| 336 | for (self.objects.items) |object| { | |
| 337 | // Find ideal section alignment and update section mappings | |
| 338 | for (object.sections.items) |sect, sect_id| { | |
| 339 | const match = (try self.getMatchingSection(sect)) orelse { | |
| 340 | log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{ | |
| 341 | object.name.?, | |
| 342 | sect.flags(), | |
| 343 | sect.segname(), | |
| 344 | sect.sectname(), | |
| 345 | }); | |
| 346 | continue; | |
| 347 | }; | |
| 348 | const target_seg = &self.load_commands.items[match.seg].Segment; | |
| 349 | const target_sect = &target_seg.sections.items[match.sect]; | |
| 350 | target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align"); | |
| 351 | ||
| 352 | try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect); | |
| 353 | } | |
| 354 | } | |
| 355 | ||
| 356 | // Ensure we have __DATA,__common section if we have tentative definitions. | |
| 357 | // Update size and alignment of __DATA,__common section. | |
| 358 | if (self.tentatives.values().len > 0) { | |
| 359 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 360 | const common_section_index = self.common_section_index orelse ind: { | |
| 361 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 362 | try data_seg.addSection(self.allocator, "__common", .{ | |
| 363 | .flags = macho.S_ZEROFILL, | |
| 364 | }); | |
| 365 | break :ind self.common_section_index.?; | |
| 366 | }; | |
| 367 | const common_sect = &data_seg.sections.items[common_section_index]; | |
| 368 | ||
| 369 | var max_align: u16 = 0; | |
| 370 | var added_size: u64 = 0; | |
| 371 | for (self.tentatives.values()) |sym| { | |
| 372 | const tent = sym.cast(Symbol.Tentative) orelse unreachable; | |
| 373 | max_align = math.max(max_align, tent.alignment); | |
| 374 | added_size += tent.size; | |
| 375 | } | |
| 376 | ||
| 377 | common_sect.@"align" = math.max(common_sect.@"align", max_align); | |
| 378 | ||
| 379 | const alignment = try math.powi(u32, 2, common_sect.@"align"); | |
| 380 | const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment); | |
| 381 | const size = mem.alignForwardGeneric(u64, added_size, alignment); | |
| 382 | ||
| 383 | common_sect.size = offset + size; | |
| 384 | self.tentative_defs_offset = offset; | |
| 385 | } | |
| 386 | ||
| 387 | tlv_align: { | |
| 388 | const has_tlv = | |
| 389 | self.tlv_section_index != null or | |
| 390 | self.tlv_data_section_index != null or | |
| 391 | self.tlv_bss_section_index != null; | |
| 392 | ||
| 393 | if (!has_tlv) break :tlv_align; | |
| 394 | ||
| 395 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 396 | ||
| 397 | if (self.tlv_section_index) |index| { | |
| 398 | const sect = &seg.sections.items[index]; | |
| 399 | sect.@"align" = 3; // __thread_vars is always 8byte aligned | |
| 400 | } | |
| 401 | ||
| 402 | // Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up. | |
| 403 | // <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment | |
| 404 | // https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172 | |
| 405 | const data_align: u32 = data: { | |
| 406 | if (self.tlv_data_section_index) |index| { | |
| 407 | const sect = &seg.sections.items[index]; | |
| 408 | break :data sect.@"align"; | |
| 409 | } | |
| 410 | break :tlv_align; | |
| 411 | }; | |
| 412 | const bss_align: u32 = bss: { | |
| 413 | if (self.tlv_bss_section_index) |index| { | |
| 414 | const sect = &seg.sections.items[index]; | |
| 415 | break :bss sect.@"align"; | |
| 416 | } | |
| 417 | break :tlv_align; | |
| 418 | }; | |
| 419 | const max_align = math.max(data_align, bss_align); | |
| 420 | ||
| 421 | if (self.tlv_data_section_index) |index| { | |
| 422 | const sect = &seg.sections.items[index]; | |
| 423 | sect.@"align" = max_align; | |
| 424 | } | |
| 425 | if (self.tlv_bss_section_index) |index| { | |
| 426 | const sect = &seg.sections.items[index]; | |
| 427 | sect.@"align" = max_align; | |
| 428 | } | |
| 429 | } | |
| 430 | } | |
| 431 | ||
| 432 | const MatchingSection = struct { | |
| 433 | seg: u16, | |
| 434 | sect: u16, | |
| 435 | }; | |
| 436 | ||
| 437 | fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { | |
| 438 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 439 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 440 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 441 | const segname = sect.segname(); | |
| 442 | const sectname = sect.sectname(); | |
| 443 | ||
| 444 | const res: ?MatchingSection = blk: { | |
| 445 | switch (sect.sectionType()) { | |
| 446 | macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { | |
| 447 | if (self.text_const_section_index == null) { | |
| 448 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 449 | try text_seg.addSection(self.allocator, "__const", .{}); | |
| 450 | } | |
| 451 | ||
| 452 | break :blk .{ | |
| 453 | .seg = self.text_segment_cmd_index.?, | |
| 454 | .sect = self.text_const_section_index.?, | |
| 455 | }; | |
| 456 | }, | |
| 457 | macho.S_CSTRING_LITERALS => { | |
| 458 | if (mem.eql(u8, sectname, "__objc_methname")) { | |
| 459 | // TODO it seems the common values within the sections in objects are deduplicated/merged | |
| 460 | // on merging the sections' contents. | |
| 461 | if (self.objc_methname_section_index == null) { | |
| 462 | self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 463 | try text_seg.addSection(self.allocator, "__objc_methname", .{ | |
| 464 | .flags = macho.S_CSTRING_LITERALS, | |
| 465 | }); | |
| 466 | } | |
| 467 | ||
| 468 | break :blk .{ | |
| 469 | .seg = self.text_segment_cmd_index.?, | |
| 470 | .sect = self.objc_methname_section_index.?, | |
| 471 | }; | |
| 472 | } else if (mem.eql(u8, sectname, "__objc_methtype")) { | |
| 473 | if (self.objc_methtype_section_index == null) { | |
| 474 | self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 475 | try text_seg.addSection(self.allocator, "__objc_methtype", .{ | |
| 476 | .flags = macho.S_CSTRING_LITERALS, | |
| 477 | }); | |
| 478 | } | |
| 479 | ||
| 480 | break :blk .{ | |
| 481 | .seg = self.text_segment_cmd_index.?, | |
| 482 | .sect = self.objc_methtype_section_index.?, | |
| 483 | }; | |
| 484 | } else if (mem.eql(u8, sectname, "__objc_classname")) { | |
| 485 | if (self.objc_classname_section_index == null) { | |
| 486 | self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 487 | try text_seg.addSection(self.allocator, "__objc_classname", .{}); | |
| 488 | } | |
| 489 | ||
| 490 | break :blk .{ | |
| 491 | .seg = self.text_segment_cmd_index.?, | |
| 492 | .sect = self.objc_classname_section_index.?, | |
| 493 | }; | |
| 494 | } | |
| 495 | ||
| 496 | if (self.cstring_section_index == null) { | |
| 497 | self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 498 | try text_seg.addSection(self.allocator, "__cstring", .{ | |
| 499 | .flags = macho.S_CSTRING_LITERALS, | |
| 500 | }); | |
| 501 | } | |
| 502 | ||
| 503 | break :blk .{ | |
| 504 | .seg = self.text_segment_cmd_index.?, | |
| 505 | .sect = self.cstring_section_index.?, | |
| 506 | }; | |
| 507 | }, | |
| 508 | macho.S_LITERAL_POINTERS => { | |
| 509 | if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { | |
| 510 | if (self.objc_selrefs_section_index == null) { | |
| 511 | self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 512 | try data_seg.addSection(self.allocator, "__objc_selrefs", .{ | |
| 513 | .flags = macho.S_LITERAL_POINTERS, | |
| 514 | }); | |
| 515 | } | |
| 516 | ||
| 517 | break :blk .{ | |
| 518 | .seg = self.data_segment_cmd_index.?, | |
| 519 | .sect = self.objc_selrefs_section_index.?, | |
| 520 | }; | |
| 521 | } | |
| 522 | ||
| 523 | // TODO investigate | |
| 524 | break :blk null; | |
| 525 | }, | |
| 526 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 527 | if (self.mod_init_func_section_index == null) { | |
| 528 | self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 529 | try data_const_seg.addSection(self.allocator, "__mod_init_func", .{ | |
| 530 | .flags = macho.S_MOD_INIT_FUNC_POINTERS, | |
| 531 | }); | |
| 532 | } | |
| 533 | ||
| 534 | break :blk .{ | |
| 535 | .seg = self.data_const_segment_cmd_index.?, | |
| 536 | .sect = self.mod_init_func_section_index.?, | |
| 537 | }; | |
| 538 | }, | |
| 539 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 540 | if (self.mod_term_func_section_index == null) { | |
| 541 | self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 542 | try data_const_seg.addSection(self.allocator, "__mod_term_func", .{ | |
| 543 | .flags = macho.S_MOD_TERM_FUNC_POINTERS, | |
| 544 | }); | |
| 545 | } | |
| 546 | ||
| 547 | break :blk .{ | |
| 548 | .seg = self.data_const_segment_cmd_index.?, | |
| 549 | .sect = self.mod_term_func_section_index.?, | |
| 550 | }; | |
| 551 | }, | |
| 552 | macho.S_ZEROFILL => { | |
| 553 | if (mem.eql(u8, sectname, "__common")) { | |
| 554 | if (self.common_section_index == null) { | |
| 555 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 556 | try data_seg.addSection(self.allocator, "__common", .{ | |
| 557 | .flags = macho.S_ZEROFILL, | |
| 558 | }); | |
| 559 | } | |
| 560 | ||
| 561 | break :blk .{ | |
| 562 | .seg = self.data_segment_cmd_index.?, | |
| 563 | .sect = self.common_section_index.?, | |
| 564 | }; | |
| 565 | } else { | |
| 566 | if (self.bss_section_index == null) { | |
| 567 | self.bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 568 | try data_seg.addSection(self.allocator, "__bss", .{ | |
| 569 | .flags = macho.S_ZEROFILL, | |
| 570 | }); | |
| 571 | } | |
| 572 | ||
| 573 | break :blk .{ | |
| 574 | .seg = self.data_segment_cmd_index.?, | |
| 575 | .sect = self.bss_section_index.?, | |
| 576 | }; | |
| 577 | } | |
| 578 | }, | |
| 579 | macho.S_THREAD_LOCAL_VARIABLES => { | |
| 580 | if (self.tlv_section_index == null) { | |
| 581 | self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 582 | try data_seg.addSection(self.allocator, "__thread_vars", .{ | |
| 583 | .flags = macho.S_THREAD_LOCAL_VARIABLES, | |
| 584 | }); | |
| 585 | } | |
| 586 | ||
| 587 | break :blk .{ | |
| 588 | .seg = self.data_segment_cmd_index.?, | |
| 589 | .sect = self.tlv_section_index.?, | |
| 590 | }; | |
| 591 | }, | |
| 592 | macho.S_THREAD_LOCAL_REGULAR => { | |
| 593 | if (self.tlv_data_section_index == null) { | |
| 594 | self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 595 | try data_seg.addSection(self.allocator, "__thread_data", .{ | |
| 596 | .flags = macho.S_THREAD_LOCAL_REGULAR, | |
| 597 | }); | |
| 598 | } | |
| 599 | ||
| 600 | break :blk .{ | |
| 601 | .seg = self.data_segment_cmd_index.?, | |
| 602 | .sect = self.tlv_data_section_index.?, | |
| 603 | }; | |
| 604 | }, | |
| 605 | macho.S_THREAD_LOCAL_ZEROFILL => { | |
| 606 | if (self.tlv_bss_section_index == null) { | |
| 607 | self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 608 | try data_seg.addSection(self.allocator, "__thread_bss", .{ | |
| 609 | .flags = macho.S_THREAD_LOCAL_ZEROFILL, | |
| 610 | }); | |
| 611 | } | |
| 612 | ||
| 613 | break :blk .{ | |
| 614 | .seg = self.data_segment_cmd_index.?, | |
| 615 | .sect = self.tlv_bss_section_index.?, | |
| 616 | }; | |
| 617 | }, | |
| 618 | macho.S_COALESCED => { | |
| 619 | if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { | |
| 620 | // TODO I believe __eh_frame is currently part of __unwind_info section | |
| 621 | // in the latest ld64 output. | |
| 622 | if (self.eh_frame_section_index == null) { | |
| 623 | self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 624 | try text_seg.addSection(self.allocator, "__eh_frame", .{}); | |
| 625 | } | |
| 626 | ||
| 627 | break :blk .{ | |
| 628 | .seg = self.text_segment_cmd_index.?, | |
| 629 | .sect = self.eh_frame_section_index.?, | |
| 630 | }; | |
| 631 | } | |
| 632 | ||
| 633 | // TODO audit this: is this the right mapping? | |
| 634 | if (self.data_const_section_index == null) { | |
| 635 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 636 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 637 | } | |
| 638 | ||
| 639 | break :blk .{ | |
| 640 | .seg = self.data_const_segment_cmd_index.?, | |
| 641 | .sect = self.data_const_section_index.?, | |
| 642 | }; | |
| 643 | }, | |
| 644 | macho.S_REGULAR => { | |
| 645 | if (sect.isCode()) { | |
| 646 | if (self.text_section_index == null) { | |
| 647 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 648 | try text_seg.addSection(self.allocator, "__text", .{ | |
| 649 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 650 | }); | |
| 651 | } | |
| 652 | ||
| 653 | break :blk .{ | |
| 654 | .seg = self.text_segment_cmd_index.?, | |
| 655 | .sect = self.text_section_index.?, | |
| 656 | }; | |
| 657 | } | |
| 658 | if (sect.isDebug()) { | |
| 659 | // TODO debug attributes | |
| 660 | if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { | |
| 661 | log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ | |
| 662 | sect.flags(), segname, sectname, | |
| 663 | }); | |
| 664 | } | |
| 665 | break :blk null; | |
| 666 | } | |
| 667 | ||
| 668 | if (mem.eql(u8, segname, "__TEXT")) { | |
| 669 | if (mem.eql(u8, sectname, "__ustring")) { | |
| 670 | if (self.ustring_section_index == null) { | |
| 671 | self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 672 | try text_seg.addSection(self.allocator, "__ustring", .{}); | |
| 673 | } | |
| 674 | ||
| 675 | break :blk .{ | |
| 676 | .seg = self.text_segment_cmd_index.?, | |
| 677 | .sect = self.ustring_section_index.?, | |
| 678 | }; | |
| 679 | } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { | |
| 680 | if (self.gcc_except_tab_section_index == null) { | |
| 681 | self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 682 | try text_seg.addSection(self.allocator, "__gcc_except_tab", .{}); | |
| 683 | } | |
| 684 | ||
| 685 | break :blk .{ | |
| 686 | .seg = self.text_segment_cmd_index.?, | |
| 687 | .sect = self.gcc_except_tab_section_index.?, | |
| 688 | }; | |
| 689 | } else if (mem.eql(u8, sectname, "__objc_methlist")) { | |
| 690 | if (self.objc_methlist_section_index == null) { | |
| 691 | self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 692 | try text_seg.addSection(self.allocator, "__objc_methlist", .{}); | |
| 693 | } | |
| 694 | ||
| 695 | break :blk .{ | |
| 696 | .seg = self.text_segment_cmd_index.?, | |
| 697 | .sect = self.objc_methlist_section_index.?, | |
| 698 | }; | |
| 699 | } else if (mem.eql(u8, sectname, "__rodata") or | |
| 700 | mem.eql(u8, sectname, "__typelink") or | |
| 701 | mem.eql(u8, sectname, "__itablink") or | |
| 702 | mem.eql(u8, sectname, "__gosymtab") or | |
| 703 | mem.eql(u8, sectname, "__gopclntab")) | |
| 704 | { | |
| 705 | if (self.data_const_section_index == null) { | |
| 706 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 707 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 708 | } | |
| 709 | ||
| 710 | break :blk .{ | |
| 711 | .seg = self.data_const_segment_cmd_index.?, | |
| 712 | .sect = self.data_const_section_index.?, | |
| 713 | }; | |
| 714 | } else { | |
| 715 | if (self.text_const_section_index == null) { | |
| 716 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 717 | try text_seg.addSection(self.allocator, "__const", .{}); | |
| 718 | } | |
| 719 | ||
| 720 | break :blk .{ | |
| 721 | .seg = self.text_segment_cmd_index.?, | |
| 722 | .sect = self.text_const_section_index.?, | |
| 723 | }; | |
| 724 | } | |
| 725 | } | |
| 726 | ||
| 727 | if (mem.eql(u8, segname, "__DATA_CONST")) { | |
| 728 | if (self.data_const_section_index == null) { | |
| 729 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 730 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 731 | } | |
| 732 | ||
| 733 | break :blk .{ | |
| 734 | .seg = self.data_const_segment_cmd_index.?, | |
| 735 | .sect = self.data_const_section_index.?, | |
| 736 | }; | |
| 737 | } | |
| 738 | ||
| 739 | if (mem.eql(u8, segname, "__DATA")) { | |
| 740 | if (mem.eql(u8, sectname, "__const")) { | |
| 741 | if (self.data_const_section_index == null) { | |
| 742 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 743 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 744 | } | |
| 745 | ||
| 746 | break :blk .{ | |
| 747 | .seg = self.data_const_segment_cmd_index.?, | |
| 748 | .sect = self.data_const_section_index.?, | |
| 749 | }; | |
| 750 | } else if (mem.eql(u8, sectname, "__cfstring")) { | |
| 751 | if (self.objc_cfstring_section_index == null) { | |
| 752 | self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 753 | try data_const_seg.addSection(self.allocator, "__cfstring", .{}); | |
| 754 | } | |
| 755 | ||
| 756 | break :blk .{ | |
| 757 | .seg = self.data_const_segment_cmd_index.?, | |
| 758 | .sect = self.objc_cfstring_section_index.?, | |
| 759 | }; | |
| 760 | } else if (mem.eql(u8, sectname, "__objc_classlist")) { | |
| 761 | if (self.objc_classlist_section_index == null) { | |
| 762 | self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 763 | try data_const_seg.addSection(self.allocator, "__objc_classlist", .{}); | |
| 764 | } | |
| 765 | ||
| 766 | break :blk .{ | |
| 767 | .seg = self.data_const_segment_cmd_index.?, | |
| 768 | .sect = self.objc_classlist_section_index.?, | |
| 769 | }; | |
| 770 | } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { | |
| 771 | if (self.objc_imageinfo_section_index == null) { | |
| 772 | self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 773 | try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{}); | |
| 774 | } | |
| 775 | ||
| 776 | break :blk .{ | |
| 777 | .seg = self.data_const_segment_cmd_index.?, | |
| 778 | .sect = self.objc_imageinfo_section_index.?, | |
| 779 | }; | |
| 780 | } else if (mem.eql(u8, sectname, "__objc_const")) { | |
| 781 | if (self.objc_const_section_index == null) { | |
| 782 | self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 783 | try data_seg.addSection(self.allocator, "__objc_const", .{}); | |
| 784 | } | |
| 785 | ||
| 786 | break :blk .{ | |
| 787 | .seg = self.data_segment_cmd_index.?, | |
| 788 | .sect = self.objc_const_section_index.?, | |
| 789 | }; | |
| 790 | } else if (mem.eql(u8, sectname, "__objc_classrefs")) { | |
| 791 | if (self.objc_classrefs_section_index == null) { | |
| 792 | self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 793 | try data_seg.addSection(self.allocator, "__objc_classrefs", .{}); | |
| 794 | } | |
| 795 | ||
| 796 | break :blk .{ | |
| 797 | .seg = self.data_segment_cmd_index.?, | |
| 798 | .sect = self.objc_classrefs_section_index.?, | |
| 799 | }; | |
| 800 | } else if (mem.eql(u8, sectname, "__objc_data")) { | |
| 801 | if (self.objc_data_section_index == null) { | |
| 802 | self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 803 | try data_seg.addSection(self.allocator, "__objc_data", .{}); | |
| 804 | } | |
| 805 | ||
| 806 | break :blk .{ | |
| 807 | .seg = self.data_segment_cmd_index.?, | |
| 808 | .sect = self.objc_data_section_index.?, | |
| 809 | }; | |
| 810 | } else { | |
| 811 | if (self.data_section_index == null) { | |
| 812 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 813 | try data_seg.addSection(self.allocator, "__data", .{}); | |
| 814 | } | |
| 815 | ||
| 816 | break :blk .{ | |
| 817 | .seg = self.data_segment_cmd_index.?, | |
| 818 | .sect = self.data_section_index.?, | |
| 819 | }; | |
| 820 | } | |
| 821 | } | |
| 822 | ||
| 823 | if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { | |
| 824 | log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ | |
| 825 | sect.flags(), segname, sectname, | |
| 826 | }); | |
| 827 | } | |
| 828 | ||
| 829 | break :blk null; | |
| 830 | }, | |
| 831 | else => break :blk null, | |
| 832 | } | |
| 833 | }; | |
| 834 | ||
| 835 | return res; | |
| 836 | } | |
| 837 | ||
| 838 | fn sortSections(self: *Zld) !void { | |
| 839 | var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 840 | defer text_index_mapping.deinit(); | |
| 841 | var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 842 | defer data_const_index_mapping.deinit(); | |
| 843 | var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 844 | defer data_index_mapping.deinit(); | |
| 845 | ||
| 846 | { | |
| 847 | // __TEXT segment | |
| 848 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 849 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 850 | defer self.allocator.free(sections); | |
| 851 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 852 | ||
| 853 | const indices = &[_]*?u16{ | |
| 854 | &self.text_section_index, | |
| 855 | &self.stubs_section_index, | |
| 856 | &self.stub_helper_section_index, | |
| 857 | &self.gcc_except_tab_section_index, | |
| 858 | &self.cstring_section_index, | |
| 859 | &self.ustring_section_index, | |
| 860 | &self.text_const_section_index, | |
| 861 | &self.objc_methname_section_index, | |
| 862 | &self.objc_methtype_section_index, | |
| 863 | &self.objc_classname_section_index, | |
| 864 | &self.eh_frame_section_index, | |
| 865 | }; | |
| 866 | for (indices) |maybe_index| { | |
| 867 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 868 | const idx = @intCast(u16, seg.sections.items.len); | |
| 869 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 870 | try text_index_mapping.putNoClobber(index, idx); | |
| 871 | break :blk idx; | |
| 872 | } else continue; | |
| 873 | maybe_index.* = new_index; | |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 877 | { | |
| 878 | // __DATA_CONST segment | |
| 879 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 880 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 881 | defer self.allocator.free(sections); | |
| 882 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 883 | ||
| 884 | const indices = &[_]*?u16{ | |
| 885 | &self.got_section_index, | |
| 886 | &self.mod_init_func_section_index, | |
| 887 | &self.mod_term_func_section_index, | |
| 888 | &self.data_const_section_index, | |
| 889 | &self.objc_cfstring_section_index, | |
| 890 | &self.objc_classlist_section_index, | |
| 891 | &self.objc_imageinfo_section_index, | |
| 892 | }; | |
| 893 | for (indices) |maybe_index| { | |
| 894 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 895 | const idx = @intCast(u16, seg.sections.items.len); | |
| 896 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 897 | try data_const_index_mapping.putNoClobber(index, idx); | |
| 898 | break :blk idx; | |
| 899 | } else continue; | |
| 900 | maybe_index.* = new_index; | |
| 901 | } | |
| 902 | } | |
| 903 | ||
| 904 | { | |
| 905 | // __DATA segment | |
| 906 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 907 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 908 | defer self.allocator.free(sections); | |
| 909 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 910 | ||
| 911 | // __DATA segment | |
| 912 | const indices = &[_]*?u16{ | |
| 913 | &self.la_symbol_ptr_section_index, | |
| 914 | &self.objc_const_section_index, | |
| 915 | &self.objc_selrefs_section_index, | |
| 916 | &self.objc_classrefs_section_index, | |
| 917 | &self.objc_data_section_index, | |
| 918 | &self.data_section_index, | |
| 919 | &self.tlv_section_index, | |
| 920 | &self.tlv_data_section_index, | |
| 921 | &self.tlv_bss_section_index, | |
| 922 | &self.bss_section_index, | |
| 923 | &self.common_section_index, | |
| 924 | }; | |
| 925 | for (indices) |maybe_index| { | |
| 926 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 927 | const idx = @intCast(u16, seg.sections.items.len); | |
| 928 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 929 | try data_index_mapping.putNoClobber(index, idx); | |
| 930 | break :blk idx; | |
| 931 | } else continue; | |
| 932 | maybe_index.* = new_index; | |
| 933 | } | |
| 934 | } | |
| 935 | ||
| 936 | for (self.objects.items) |object| { | |
| 937 | for (object.sections.items) |*sect| { | |
| 938 | const target_map = sect.target_map orelse continue; | |
| 939 | ||
| 940 | const new_index = blk: { | |
| 941 | if (self.text_segment_cmd_index.? == target_map.segment_id) { | |
| 942 | break :blk text_index_mapping.get(target_map.section_id) orelse unreachable; | |
| 943 | } else if (self.data_const_segment_cmd_index.? == target_map.segment_id) { | |
| 944 | break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable; | |
| 945 | } else if (self.data_segment_cmd_index.? == target_map.segment_id) { | |
| 946 | break :blk data_index_mapping.get(target_map.section_id) orelse unreachable; | |
| 947 | } else unreachable; | |
| 948 | }; | |
| 949 | ||
| 950 | log.debug("remapping in {s}: '{s},{s}': {} => {}", .{ | |
| 951 | object.name.?, | |
| 952 | parseName(&sect.inner.segname), | |
| 953 | parseName(&sect.inner.sectname), | |
| 954 | target_map.section_id, | |
| 955 | new_index, | |
| 956 | }); | |
| 957 | ||
| 958 | sect.target_map = .{ | |
| 959 | .segment_id = target_map.segment_id, | |
| 960 | .section_id = new_index, | |
| 961 | .offset = target_map.offset, | |
| 962 | }; | |
| 963 | } | |
| 964 | } | |
| 965 | } | |
| 966 | ||
| 967 | fn allocateTextSegment(self: *Zld) !void { | |
| 968 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 969 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 970 | ||
| 971 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; | |
| 972 | seg.inner.fileoff = 0; | |
| 973 | seg.inner.vmaddr = base_vmaddr; | |
| 974 | ||
| 975 | // Set stubs and stub_helper sizes | |
| 976 | const stubs = &seg.sections.items[self.stubs_section_index.?]; | |
| 977 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; | |
| 978 | stubs.size += nstubs * stubs.reserved2; | |
| 979 | ||
| 980 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 981 | .x86_64 => 10, | |
| 982 | .aarch64 => 3 * @sizeOf(u32), | |
| 983 | else => unreachable, | |
| 984 | }; | |
| 985 | stub_helper.size += nstubs * stub_size; | |
| 986 | ||
| 987 | var sizeofcmds: u64 = 0; | |
| 988 | for (self.load_commands.items) |lc| { | |
| 989 | sizeofcmds += lc.cmdsize(); | |
| 990 | } | |
| 991 | ||
| 992 | try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); | |
| 993 | ||
| 994 | // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. | |
| 995 | var min_alignment: u32 = 0; | |
| 996 | for (seg.sections.items) |sect| { | |
| 997 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 998 | min_alignment = math.max(min_alignment, alignment); | |
| 999 | } | |
| 1000 | ||
| 1001 | assert(min_alignment > 0); | |
| 1002 | const last_sect_idx = seg.sections.items.len - 1; | |
| 1003 | const last_sect = seg.sections.items[last_sect_idx]; | |
| 1004 | const shift: u32 = blk: { | |
| 1005 | const diff = seg.inner.filesize - last_sect.offset - last_sect.size; | |
| 1006 | const factor = @divTrunc(diff, min_alignment); | |
| 1007 | break :blk @intCast(u32, factor * min_alignment); | |
| 1008 | }; | |
| 1009 | ||
| 1010 | if (shift > 0) { | |
| 1011 | for (seg.sections.items) |*sect| { | |
| 1012 | sect.offset += shift; | |
| 1013 | sect.addr += shift; | |
| 1014 | } | |
| 1015 | } | |
| 1016 | } | |
| 1017 | ||
| 1018 | fn allocateDataConstSegment(self: *Zld) !void { | |
| 1019 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1020 | const nentries = @intCast(u32, self.got_entries.items.len); | |
| 1021 | ||
| 1022 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1023 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; | |
| 1024 | seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; | |
| 1025 | ||
| 1026 | // Set got size | |
| 1027 | const got = &seg.sections.items[self.got_section_index.?]; | |
| 1028 | got.size += nentries * @sizeOf(u64); | |
| 1029 | ||
| 1030 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); | |
| 1031 | } | |
| 1032 | ||
| 1033 | fn allocateDataSegment(self: *Zld) !void { | |
| 1034 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1035 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 1036 | ||
| 1037 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1038 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; | |
| 1039 | seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; | |
| 1040 | ||
| 1041 | // Set la_symbol_ptr and data size | |
| 1042 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1043 | const data = &seg.sections.items[self.data_section_index.?]; | |
| 1044 | la_symbol_ptr.size += nstubs * @sizeOf(u64); | |
| 1045 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder | |
| 1046 | ||
| 1047 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); | |
| 1048 | } | |
| 1049 | ||
| 1050 | fn allocateLinkeditSegment(self: *Zld) void { | |
| 1051 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 1052 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1053 | seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; | |
| 1054 | seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; | |
| 1055 | } | |
| 1056 | ||
| 1057 | fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { | |
| 1058 | const seg = &self.load_commands.items[index].Segment; | |
| 1059 | ||
| 1060 | // Allocate the sections according to their alignment at the beginning of the segment. | |
| 1061 | var start: u64 = offset; | |
| 1062 | for (seg.sections.items) |*sect| { | |
| 1063 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 1064 | const start_aligned = mem.alignForwardGeneric(u64, start, alignment); | |
| 1065 | const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment); | |
| 1066 | sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned); | |
| 1067 | sect.addr = seg.inner.vmaddr + start_aligned; | |
| 1068 | start = end_aligned; | |
| 1069 | } | |
| 1070 | ||
| 1071 | const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?); | |
| 1072 | seg.inner.filesize = seg_size_aligned; | |
| 1073 | seg.inner.vmsize = seg_size_aligned; | |
| 1074 | } | |
| 1075 | ||
| 1076 | fn allocateSymbols(self: *Zld) !void { | |
| 1077 | for (self.objects.items) |object| { | |
| 1078 | for (object.symbols.items) |sym| { | |
| 1079 | const reg = sym.cast(Symbol.Regular) orelse continue; | |
| 1080 | ||
| 1081 | const source_sect = &object.sections.items[reg.section]; | |
| 1082 | const target_map = source_sect.target_map orelse { | |
| 1083 | log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{ | |
| 1084 | parseName(&source_sect.inner.segname), | |
| 1085 | parseName(&source_sect.inner.sectname), | |
| 1086 | sym.name, | |
| 1087 | }); | |
| 1088 | continue; | |
| 1089 | }; | |
| 1090 | ||
| 1091 | const target_seg = self.load_commands.items[target_map.segment_id].Segment; | |
| 1092 | const target_sect = target_seg.sections.items[target_map.section_id]; | |
| 1093 | const target_addr = target_sect.addr + target_map.offset; | |
| 1094 | const address = reg.address - source_sect.inner.addr + target_addr; | |
| 1095 | ||
| 1096 | log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address }); | |
| 1097 | ||
| 1098 | // TODO there might be a more generic way of doing this. | |
| 1099 | var section: u8 = 0; | |
| 1100 | for (self.load_commands.items) |cmd, cmd_id| { | |
| 1101 | if (cmd != .Segment) break; | |
| 1102 | if (cmd_id == target_map.segment_id) { | |
| 1103 | section += @intCast(u8, target_map.section_id) + 1; | |
| 1104 | break; | |
| 1105 | } | |
| 1106 | section += @intCast(u8, cmd.Segment.sections.items.len); | |
| 1107 | } | |
| 1108 | ||
| 1109 | reg.address = address; | |
| 1110 | reg.section = section; | |
| 1111 | } | |
| 1112 | } | |
| 1113 | } | |
| 1114 | ||
| 1115 | fn allocateTentativeSymbols(self: *Zld) !void { | |
| 1116 | if (self.tentatives.values().len == 0) return; | |
| 1117 | ||
| 1118 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1119 | const common_sect = &data_seg.sections.items[self.common_section_index.?]; | |
| 1120 | ||
| 1121 | const alignment = try math.powi(u32, 2, common_sect.@"align"); | |
| 1122 | var base_address: u64 = common_sect.addr + self.tentative_defs_offset; | |
| 1123 | ||
| 1124 | log.debug("base address for tentative definitions 0x{x}", .{base_address}); | |
| 1125 | ||
| 1126 | // TODO there might be a more generic way of doing this. | |
| 1127 | var section: u8 = 0; | |
| 1128 | for (self.load_commands.items) |cmd, cmd_id| { | |
| 1129 | if (cmd != .Segment) break; | |
| 1130 | if (cmd_id == self.data_segment_cmd_index.?) { | |
| 1131 | section += @intCast(u8, self.common_section_index.?) + 1; | |
| 1132 | break; | |
| 1133 | } | |
| 1134 | section += @intCast(u8, cmd.Segment.sections.items.len); | |
| 1135 | } | |
| 1136 | ||
| 1137 | // Convert tentative definitions into regular symbols. | |
| 1138 | for (self.tentatives.values()) |sym| { | |
| 1139 | const tent = sym.cast(Symbol.Tentative) orelse unreachable; | |
| 1140 | const reg = try self.allocator.create(Symbol.Regular); | |
| 1141 | errdefer self.allocator.destroy(reg); | |
| 1142 | ||
| 1143 | reg.* = .{ | |
| 1144 | .base = .{ | |
| 1145 | .@"type" = .regular, | |
| 1146 | .name = try self.allocator.dupe(u8, tent.base.name), | |
| 1147 | .got_index = tent.base.got_index, | |
| 1148 | .stubs_index = tent.base.stubs_index, | |
| 1149 | }, | |
| 1150 | .linkage = .global, | |
| 1151 | .address = base_address, | |
| 1152 | .section = section, | |
| 1153 | .weak_ref = false, | |
| 1154 | .file = tent.file, | |
| 1155 | .stab = .{ | |
| 1156 | .kind = .global, | |
| 1157 | .size = 0, | |
| 1158 | }, | |
| 1159 | }; | |
| 1160 | ||
| 1161 | try self.globals.putNoClobber(self.allocator, reg.base.name, &reg.base); | |
| 1162 | tent.base.alias = &reg.base; | |
| 1163 | ||
| 1164 | if (tent.base.got_index) |idx| { | |
| 1165 | self.got_entries.items[idx] = &reg.base; | |
| 1166 | } | |
| 1167 | if (tent.base.stubs_index) |idx| { | |
| 1168 | self.stubs.items[idx] = &reg.base; | |
| 1169 | } | |
| 1170 | ||
| 1171 | const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment); | |
| 1172 | ||
| 1173 | log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ | |
| 1174 | tent.base.name, | |
| 1175 | base_address, | |
| 1176 | address, | |
| 1177 | }); | |
| 1178 | ||
| 1179 | base_address = address; | |
| 1180 | } | |
| 1181 | } | |
| 1182 | ||
| 1183 | fn allocateProxyBindAddresses(self: *Zld) !void { | |
| 1184 | for (self.objects.items) |object| { | |
| 1185 | for (object.sections.items) |sect| { | |
| 1186 | const relocs = sect.relocs orelse continue; | |
| 1187 | ||
| 1188 | for (relocs) |rel| { | |
| 1189 | if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased | |
| 1190 | if (rel.target != .symbol) continue; | |
| 1191 | ||
| 1192 | const sym = rel.target.symbol.getTopmostAlias(); | |
| 1193 | if (sym.cast(Symbol.Proxy)) |proxy| { | |
| 1194 | const target_map = sect.target_map orelse continue; | |
| 1195 | const target_seg = self.load_commands.items[target_map.segment_id].Segment; | |
| 1196 | const target_sect = target_seg.sections.items[target_map.section_id]; | |
| 1197 | ||
| 1198 | try proxy.bind_info.append(self.allocator, .{ | |
| 1199 | .segment_id = target_map.segment_id, | |
| 1200 | .address = target_sect.addr + target_map.offset + rel.offset, | |
| 1201 | }); | |
| 1202 | } | |
| 1203 | } | |
| 1204 | } | |
| 1205 | } | |
| 1206 | } | |
| 1207 | ||
| 1208 | fn writeStubHelperCommon(self: *Zld) !void { | |
| 1209 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1210 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1211 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1212 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 1213 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1214 | const data = &data_segment.sections.items[self.data_section_index.?]; | |
| 1215 | ||
| 1216 | self.stub_helper_stubs_start_off = blk: { | |
| 1217 | switch (self.target.?.cpu.arch) { | |
| 1218 | .x86_64 => { | |
| 1219 | const code_size = 15; | |
| 1220 | var code: [code_size]u8 = undefined; | |
| 1221 | // lea %r11, [rip + disp] | |
| 1222 | code[0] = 0x4c; | |
| 1223 | code[1] = 0x8d; | |
| 1224 | code[2] = 0x1d; | |
| 1225 | { | |
| 1226 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1227 | const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); | |
| 1228 | mem.writeIntLittle(u32, code[3..7], displacement); | |
| 1229 | } | |
| 1230 | // push %r11 | |
| 1231 | code[7] = 0x41; | |
| 1232 | code[8] = 0x53; | |
| 1233 | // jmp [rip + disp] | |
| 1234 | code[9] = 0xff; | |
| 1235 | code[10] = 0x25; | |
| 1236 | { | |
| 1237 | const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; | |
| 1238 | const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); | |
| 1239 | const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); | |
| 1240 | mem.writeIntLittle(u32, code[11..], displacement); | |
| 1241 | } | |
| 1242 | try self.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1243 | break :blk stub_helper.offset + code_size; | |
| 1244 | }, | |
| 1245 | .aarch64 => { | |
| 1246 | var code: [6 * @sizeOf(u32)]u8 = undefined; | |
| 1247 | data_blk_outer: { | |
| 1248 | const this_addr = stub_helper.addr; | |
| 1249 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1250 | data_blk: { | |
| 1251 | const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk; | |
| 1252 | // adr x17, disp | |
| 1253 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1254 | // nop | |
| 1255 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1256 | break :data_blk_outer; | |
| 1257 | } | |
| 1258 | data_blk: { | |
| 1259 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1260 | const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk; | |
| 1261 | // nop | |
| 1262 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1263 | // adr x17, disp | |
| 1264 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1265 | break :data_blk_outer; | |
| 1266 | } | |
| 1267 | // Jump is too big, replace adr with adrp and add. | |
| 1268 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1269 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1270 | const pages = @intCast(i21, target_page - this_page); | |
| 1271 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); | |
| 1272 | const narrowed = @truncate(u12, target_addr); | |
| 1273 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); | |
| 1274 | } | |
| 1275 | // stp x16, x17, [sp, #-16]! | |
| 1276 | code[8] = 0xf0; | |
| 1277 | code[9] = 0x47; | |
| 1278 | code[10] = 0xbf; | |
| 1279 | code[11] = 0xa9; | |
| 1280 | binder_blk_outer: { | |
| 1281 | const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; | |
| 1282 | const this_addr = stub_helper.addr + 3 * @sizeOf(u32); | |
| 1283 | const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); | |
| 1284 | binder_blk: { | |
| 1285 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk; | |
| 1286 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1287 | // ldr x16, label | |
| 1288 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ | |
| 1289 | .literal = literal, | |
| 1290 | }).toU32()); | |
| 1291 | // nop | |
| 1292 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); | |
| 1293 | break :binder_blk_outer; | |
| 1294 | } | |
| 1295 | binder_blk: { | |
| 1296 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1297 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk; | |
| 1298 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1299 | // Pad with nop to please division. | |
| 1300 | // nop | |
| 1301 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); | |
| 1302 | // ldr x16, label | |
| 1303 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1304 | .literal = literal, | |
| 1305 | }).toU32()); | |
| 1306 | break :binder_blk_outer; | |
| 1307 | } | |
| 1308 | // Use adrp followed by ldr(immediate). | |
| 1309 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1310 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1311 | const pages = @intCast(i21, target_page - this_page); | |
| 1312 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1313 | const narrowed = @truncate(u12, target_addr); | |
| 1314 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1315 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1316 | .register = .{ | |
| 1317 | .rn = .x16, | |
| 1318 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1319 | }, | |
| 1320 | }).toU32()); | |
| 1321 | } | |
| 1322 | // br x16 | |
| 1323 | code[20] = 0x00; | |
| 1324 | code[21] = 0x02; | |
| 1325 | code[22] = 0x1f; | |
| 1326 | code[23] = 0xd6; | |
| 1327 | try self.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1328 | break :blk stub_helper.offset + 6 * @sizeOf(u32); | |
| 1329 | }, | |
| 1330 | else => unreachable, | |
| 1331 | } | |
| 1332 | }; | |
| 1333 | ||
| 1334 | for (self.stubs.items) |sym| { | |
| 1335 | // TODO weak bound pointers | |
| 1336 | const index = sym.stubs_index orelse unreachable; | |
| 1337 | try self.writeLazySymbolPointer(index); | |
| 1338 | try self.writeStub(index); | |
| 1339 | try self.writeStubInStubHelper(index); | |
| 1340 | } | |
| 1341 | } | |
| 1342 | ||
| 1343 | fn writeLazySymbolPointer(self: *Zld, index: u32) !void { | |
| 1344 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1345 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1346 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1347 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1348 | ||
| 1349 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 1350 | .x86_64 => 10, | |
| 1351 | .aarch64 => 3 * @sizeOf(u32), | |
| 1352 | else => unreachable, | |
| 1353 | }; | |
| 1354 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; | |
| 1355 | const end = stub_helper.addr + stub_off - stub_helper.offset; | |
| 1356 | var buf: [@sizeOf(u64)]u8 = undefined; | |
| 1357 | mem.writeIntLittle(u64, &buf, end); | |
| 1358 | const off = la_symbol_ptr.offset + index * @sizeOf(u64); | |
| 1359 | log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off }); | |
| 1360 | try self.file.?.pwriteAll(&buf, off); | |
| 1361 | } | |
| 1362 | ||
| 1363 | fn writeStub(self: *Zld, index: u32) !void { | |
| 1364 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1365 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | |
| 1366 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1367 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1368 | ||
| 1369 | const stub_off = stubs.offset + index * stubs.reserved2; | |
| 1370 | const stub_addr = stubs.addr + index * stubs.reserved2; | |
| 1371 | const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); | |
| 1372 | log.debug("writing stub at 0x{x}", .{stub_off}); | |
| 1373 | var code = try self.allocator.alloc(u8, stubs.reserved2); | |
| 1374 | defer self.allocator.free(code); | |
| 1375 | switch (self.target.?.cpu.arch) { | |
| 1376 | .x86_64 => { | |
| 1377 | assert(la_ptr_addr >= stub_addr + stubs.reserved2); | |
| 1378 | const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2); | |
| 1379 | // jmp | |
| 1380 | code[0] = 0xff; | |
| 1381 | code[1] = 0x25; | |
| 1382 | mem.writeIntLittle(u32, code[2..][0..4], displacement); | |
| 1383 | }, | |
| 1384 | .aarch64 => { | |
| 1385 | assert(la_ptr_addr >= stub_addr); | |
| 1386 | outer: { | |
| 1387 | const this_addr = stub_addr; | |
| 1388 | const target_addr = la_ptr_addr; | |
| 1389 | inner: { | |
| 1390 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :inner; | |
| 1391 | const literal = math.cast(u18, displacement) catch break :inner; | |
| 1392 | // ldr x16, literal | |
| 1393 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ | |
| 1394 | .literal = literal, | |
| 1395 | }).toU32()); | |
| 1396 | // nop | |
| 1397 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1398 | break :outer; | |
| 1399 | } | |
| 1400 | inner: { | |
| 1401 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1402 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :inner; | |
| 1403 | const literal = math.cast(u18, displacement) catch break :inner; | |
| 1404 | // nop | |
| 1405 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1406 | // ldr x16, literal | |
| 1407 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ | |
| 1408 | .literal = literal, | |
| 1409 | }).toU32()); | |
| 1410 | break :outer; | |
| 1411 | } | |
| 1412 | // Use adrp followed by ldr(immediate). | |
| 1413 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1414 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1415 | const pages = @intCast(i21, target_page - this_page); | |
| 1416 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1417 | const narrowed = @truncate(u12, target_addr); | |
| 1418 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1419 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ | |
| 1420 | .register = .{ | |
| 1421 | .rn = .x16, | |
| 1422 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1423 | }, | |
| 1424 | }).toU32()); | |
| 1425 | } | |
| 1426 | // br x16 | |
| 1427 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); | |
| 1428 | }, | |
| 1429 | else => unreachable, | |
| 1430 | } | |
| 1431 | try self.file.?.pwriteAll(code, stub_off); | |
| 1432 | } | |
| 1433 | ||
| 1434 | fn writeStubInStubHelper(self: *Zld, index: u32) !void { | |
| 1435 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1436 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1437 | ||
| 1438 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 1439 | .x86_64 => 10, | |
| 1440 | .aarch64 => 3 * @sizeOf(u32), | |
| 1441 | else => unreachable, | |
| 1442 | }; | |
| 1443 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; | |
| 1444 | var code = try self.allocator.alloc(u8, stub_size); | |
| 1445 | defer self.allocator.free(code); | |
| 1446 | switch (self.target.?.cpu.arch) { | |
| 1447 | .x86_64 => { | |
| 1448 | const displacement = try math.cast( | |
| 1449 | i32, | |
| 1450 | @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size, | |
| 1451 | ); | |
| 1452 | // pushq | |
| 1453 | code[0] = 0x68; | |
| 1454 | mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 1455 | // jmpq | |
| 1456 | code[5] = 0xe9; | |
| 1457 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); | |
| 1458 | }, | |
| 1459 | .aarch64 => { | |
| 1460 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); | |
| 1461 | const literal = @divExact(stub_size - @sizeOf(u32), 4); | |
| 1462 | // ldr w16, literal | |
| 1463 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ | |
| 1464 | .literal = literal, | |
| 1465 | }).toU32()); | |
| 1466 | // b disp | |
| 1467 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); | |
| 1468 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 1469 | }, | |
| 1470 | else => unreachable, | |
| 1471 | } | |
| 1472 | try self.file.?.pwriteAll(code, stub_off); | |
| 1473 | } | |
| 1474 | ||
| 1475 | fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { | |
| 1476 | log.debug("resolving symbols in '{s}'", .{object.name}); | |
| 1477 | ||
| 1478 | for (object.symbols.items) |sym| { | |
| 1479 | if (sym.cast(Symbol.Regular)) |reg| { | |
| 1480 | if (reg.linkage == .translation_unit) continue; // Symbol local to TU. | |
| 1481 | ||
| 1482 | if (self.tentatives.fetchSwapRemove(sym.name)) |kv| { | |
| 1483 | // Create link to the global. | |
| 1484 | kv.value.alias = sym; | |
| 1485 | } | |
| 1486 | if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { | |
| 1487 | // Create link to the global. | |
| 1488 | kv.value.alias = sym; | |
| 1489 | } | |
| 1490 | const sym_ptr = self.globals.getPtr(sym.name) orelse { | |
| 1491 | // Put new global symbol into the symbol table. | |
| 1492 | try self.globals.putNoClobber(self.allocator, sym.name, sym); | |
| 1493 | continue; | |
| 1494 | }; | |
| 1495 | const g_sym = sym_ptr.*; | |
| 1496 | const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable; | |
| 1497 | ||
| 1498 | switch (g_reg.linkage) { | |
| 1499 | .translation_unit => unreachable, | |
| 1500 | .linkage_unit => { | |
| 1501 | if (reg.linkage == .linkage_unit) { | |
| 1502 | // Create link to the first encountered linkage_unit symbol. | |
| 1503 | sym.alias = g_sym; | |
| 1504 | continue; | |
| 1505 | } | |
| 1506 | }, | |
| 1507 | .global => { | |
| 1508 | if (reg.linkage == .global) { | |
| 1509 | log.debug("symbol '{s}' defined multiple times", .{reg.base.name}); | |
| 1510 | return error.MultipleSymbolDefinitions; | |
| 1511 | } | |
| 1512 | sym.alias = g_sym; | |
| 1513 | continue; | |
| 1514 | }, | |
| 1515 | } | |
| 1516 | ||
| 1517 | g_sym.alias = sym; | |
| 1518 | sym_ptr.* = sym; | |
| 1519 | } else if (sym.cast(Symbol.Tentative)) |tent| { | |
| 1520 | if (self.globals.get(sym.name)) |g_sym| { | |
| 1521 | sym.alias = g_sym; | |
| 1522 | continue; | |
| 1523 | } | |
| 1524 | ||
| 1525 | if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { | |
| 1526 | kv.value.alias = sym; | |
| 1527 | } | |
| 1528 | ||
| 1529 | const sym_ptr = self.tentatives.getPtr(sym.name) orelse { | |
| 1530 | // Put new tentative definition symbol into symbol table. | |
| 1531 | try self.tentatives.putNoClobber(self.allocator, sym.name, sym); | |
| 1532 | continue; | |
| 1533 | }; | |
| 1534 | ||
| 1535 | // Compare by size and pick the largest tentative definition. | |
| 1536 | // We model this like a heap where the tentative definition with the | |
| 1537 | // largest size always washes up on top. | |
| 1538 | const t_sym = sym_ptr.*; | |
| 1539 | const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable; | |
| 1540 | ||
| 1541 | if (tent.size < t_tent.size) { | |
| 1542 | sym.alias = t_sym; | |
| 1543 | continue; | |
| 1544 | } | |
| 1545 | ||
| 1546 | t_sym.alias = sym; | |
| 1547 | sym_ptr.* = sym; | |
| 1548 | } else if (sym.cast(Symbol.Unresolved)) |_| { | |
| 1549 | if (self.globals.get(sym.name)) |g_sym| { | |
| 1550 | sym.alias = g_sym; | |
| 1551 | continue; | |
| 1552 | } | |
| 1553 | if (self.tentatives.get(sym.name)) |t_sym| { | |
| 1554 | sym.alias = t_sym; | |
| 1555 | continue; | |
| 1556 | } | |
| 1557 | if (self.unresolved.get(sym.name)) |u_sym| { | |
| 1558 | sym.alias = u_sym; | |
| 1559 | continue; | |
| 1560 | } | |
| 1561 | ||
| 1562 | try self.unresolved.putNoClobber(self.allocator, sym.name, sym); | |
| 1563 | } else unreachable; | |
| 1564 | } | |
| 1565 | } | |
| 1566 | ||
| 1567 | fn resolveSymbols(self: *Zld) !void { | |
| 1568 | // First pass, resolve symbols in provided objects. | |
| 1569 | for (self.objects.items) |object| { | |
| 1570 | try self.resolveSymbolsInObject(object); | |
| 1571 | } | |
| 1572 | ||
| 1573 | // Second pass, resolve symbols in static libraries. | |
| 1574 | var next_sym: usize = 0; | |
| 1575 | while (true) { | |
| 1576 | if (next_sym == self.unresolved.count()) break; | |
| 1577 | ||
| 1578 | const sym = self.unresolved.values()[next_sym]; | |
| 1579 | ||
| 1580 | var reset: bool = false; | |
| 1581 | for (self.archives.items) |archive| { | |
| 1582 | // Check if the entry exists in a static archive. | |
| 1583 | const offsets = archive.toc.get(sym.name) orelse { | |
| 1584 | // No hit. | |
| 1585 | continue; | |
| 1586 | }; | |
| 1587 | assert(offsets.items.len > 0); | |
| 1588 | ||
| 1589 | const object = try archive.parseObject(offsets.items[0]); | |
| 1590 | try self.objects.append(self.allocator, object); | |
| 1591 | try self.resolveSymbolsInObject(object); | |
| 1592 | ||
| 1593 | reset = true; | |
| 1594 | break; | |
| 1595 | } | |
| 1596 | ||
| 1597 | if (reset) { | |
| 1598 | next_sym = 0; | |
| 1599 | } else { | |
| 1600 | next_sym += 1; | |
| 1601 | } | |
| 1602 | } | |
| 1603 | ||
| 1604 | // Third pass, resolve symbols in dynamic libraries. | |
| 1605 | var unresolved = std.ArrayList(*Symbol).init(self.allocator); | |
| 1606 | defer unresolved.deinit(); | |
| 1607 | ||
| 1608 | try unresolved.ensureCapacity(self.unresolved.count()); | |
| 1609 | for (self.unresolved.values()) |value| { | |
| 1610 | unresolved.appendAssumeCapacity(value); | |
| 1611 | } | |
| 1612 | self.unresolved.clearRetainingCapacity(); | |
| 1613 | ||
| 1614 | // Put dyld_stub_binder as an unresolved special symbol. | |
| 1615 | { | |
| 1616 | const name = try self.allocator.dupe(u8, "dyld_stub_binder"); | |
| 1617 | errdefer self.allocator.free(name); | |
| 1618 | const undef = try self.allocator.create(Symbol.Unresolved); | |
| 1619 | errdefer self.allocator.destroy(undef); | |
| 1620 | undef.* = .{ | |
| 1621 | .base = .{ | |
| 1622 | .@"type" = .unresolved, | |
| 1623 | .name = name, | |
| 1624 | }, | |
| 1625 | }; | |
| 1626 | try unresolved.append(&undef.base); | |
| 1627 | } | |
| 1628 | ||
| 1629 | var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); | |
| 1630 | defer referenced.deinit(); | |
| 1631 | ||
| 1632 | loop: while (unresolved.popOrNull()) |undef| { | |
| 1633 | const proxy = self.imports.get(undef.name) orelse outer: { | |
| 1634 | const proxy = inner: { | |
| 1635 | for (self.dylibs.items) |dylib| { | |
| 1636 | const proxy = (try dylib.createProxy(undef.name)) orelse continue; | |
| 1637 | try referenced.put(dylib, {}); | |
| 1638 | break :inner proxy; | |
| 1639 | } | |
| 1640 | if (mem.eql(u8, undef.name, "___dso_handle")) { | |
| 1641 | // TODO this is just a temp patch until I work out what to actually | |
| 1642 | // do with ___dso_handle and __mh_execute_header symbols which are | |
| 1643 | // synthetically created by the linker on macOS. | |
| 1644 | const name = try self.allocator.dupe(u8, undef.name); | |
| 1645 | const proxy = try self.allocator.create(Symbol.Proxy); | |
| 1646 | errdefer self.allocator.destroy(proxy); | |
| 1647 | proxy.* = .{ | |
| 1648 | .base = .{ | |
| 1649 | .@"type" = .proxy, | |
| 1650 | .name = name, | |
| 1651 | }, | |
| 1652 | }; | |
| 1653 | break :inner &proxy.base; | |
| 1654 | } | |
| 1655 | ||
| 1656 | self.unresolved.putAssumeCapacityNoClobber(undef.name, undef); | |
| 1657 | continue :loop; | |
| 1658 | }; | |
| 1659 | ||
| 1660 | try self.imports.putNoClobber(self.allocator, proxy.name, proxy); | |
| 1661 | break :outer proxy; | |
| 1662 | }; | |
| 1663 | undef.alias = proxy; | |
| 1664 | } | |
| 1665 | ||
| 1666 | // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. | |
| 1667 | var it = referenced.iterator(); | |
| 1668 | while (it.next()) |entry| { | |
| 1669 | const dylib = entry.key_ptr.*; | |
| 1670 | dylib.ordinal = self.next_dylib_ordinal; | |
| 1671 | const dylib_id = dylib.id orelse unreachable; | |
| 1672 | var dylib_cmd = try createLoadDylibCommand( | |
| 1673 | self.allocator, | |
| 1674 | dylib_id.name, | |
| 1675 | dylib_id.timestamp, | |
| 1676 | dylib_id.current_version, | |
| 1677 | dylib_id.compatibility_version, | |
| 1678 | ); | |
| 1679 | errdefer dylib_cmd.deinit(self.allocator); | |
| 1680 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | |
| 1681 | self.next_dylib_ordinal += 1; | |
| 1682 | } | |
| 1683 | ||
| 1684 | if (self.unresolved.count() > 0) { | |
| 1685 | for (self.unresolved.values()) |undef| { | |
| 1686 | log.err("undefined reference to symbol '{s}'", .{undef.name}); | |
| 1687 | if (undef.cast(Symbol.Unresolved).?.file) |file| { | |
| 1688 | log.err(" | referenced in {s}", .{file.name.?}); | |
| 1689 | } | |
| 1690 | } | |
| 1691 | ||
| 1692 | return error.UndefinedSymbolReference; | |
| 1693 | } | |
| 1694 | } | |
| 1695 | ||
| 1696 | fn resolveStubsAndGotEntries(self: *Zld) !void { | |
| 1697 | for (self.objects.items) |object| { | |
| 1698 | log.debug("resolving stubs and got entries from {s}", .{object.name}); | |
| 1699 | ||
| 1700 | for (object.sections.items) |sect| { | |
| 1701 | const relocs = sect.relocs orelse continue; | |
| 1702 | for (relocs) |rel| { | |
| 1703 | switch (rel.@"type") { | |
| 1704 | .unsigned => continue, | |
| 1705 | .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { | |
| 1706 | const sym = rel.target.symbol.getTopmostAlias(); | |
| 1707 | if (sym.got_index != null) continue; | |
| 1708 | ||
| 1709 | const index = @intCast(u32, self.got_entries.items.len); | |
| 1710 | sym.got_index = index; | |
| 1711 | try self.got_entries.append(self.allocator, sym); | |
| 1712 | ||
| 1713 | log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); | |
| 1714 | }, | |
| 1715 | else => { | |
| 1716 | if (rel.target != .symbol) continue; | |
| 1717 | ||
| 1718 | const sym = rel.target.symbol.getTopmostAlias(); | |
| 1719 | assert(sym.@"type" != .unresolved); | |
| 1720 | ||
| 1721 | if (sym.stubs_index != null) continue; | |
| 1722 | if (sym.@"type" != .proxy) continue; | |
| 1723 | ||
| 1724 | const index = @intCast(u32, self.stubs.items.len); | |
| 1725 | sym.stubs_index = index; | |
| 1726 | try self.stubs.append(self.allocator, sym); | |
| 1727 | ||
| 1728 | log.debug(" | found stub {s}: {*}", .{ sym.name, sym }); | |
| 1729 | }, | |
| 1730 | } | |
| 1731 | } | |
| 1732 | } | |
| 1733 | } | |
| 1734 | ||
| 1735 | // Finally, put dyld_stub_binder as the final GOT entry | |
| 1736 | const sym = self.imports.get("dyld_stub_binder") orelse unreachable; | |
| 1737 | const index = @intCast(u32, self.got_entries.items.len); | |
| 1738 | sym.got_index = index; | |
| 1739 | try self.got_entries.append(self.allocator, sym); | |
| 1740 | ||
| 1741 | log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); | |
| 1742 | } | |
| 1743 | ||
| 1744 | fn resolveRelocsAndWriteSections(self: *Zld) !void { | |
| 1745 | for (self.objects.items) |object| { | |
| 1746 | log.debug("relocating object {s}", .{object.name}); | |
| 1747 | ||
| 1748 | for (object.sections.items) |sect| { | |
| 1749 | if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or | |
| 1750 | sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; | |
| 1751 | ||
| 1752 | const segname = parseName(&sect.inner.segname); | |
| 1753 | const sectname = parseName(&sect.inner.sectname); | |
| 1754 | ||
| 1755 | log.debug("relocating section '{s},{s}'", .{ segname, sectname }); | |
| 1756 | ||
| 1757 | // Get target mapping | |
| 1758 | const target_map = sect.target_map orelse { | |
| 1759 | log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname }); | |
| 1760 | continue; | |
| 1761 | }; | |
| 1762 | const target_seg = self.load_commands.items[target_map.segment_id].Segment; | |
| 1763 | const target_sect = target_seg.sections.items[target_map.section_id]; | |
| 1764 | const target_sect_addr = target_sect.addr + target_map.offset; | |
| 1765 | const target_sect_off = target_sect.offset + target_map.offset; | |
| 1766 | ||
| 1767 | if (sect.relocs) |relocs| { | |
| 1768 | for (relocs) |rel| { | |
| 1769 | const source_addr = target_sect_addr + rel.offset; | |
| 1770 | ||
| 1771 | var args: reloc.Relocation.ResolveArgs = .{ | |
| 1772 | .source_addr = source_addr, | |
| 1773 | .target_addr = undefined, | |
| 1774 | }; | |
| 1775 | ||
| 1776 | switch (rel.@"type") { | |
| 1777 | .unsigned => { | |
| 1778 | args.target_addr = try self.relocTargetAddr(object, rel.target); | |
| 1779 | ||
| 1780 | const unsigned = rel.cast(reloc.Unsigned) orelse unreachable; | |
| 1781 | if (unsigned.subtractor) |subtractor| { | |
| 1782 | args.subtractor = try self.relocTargetAddr(object, subtractor); | |
| 1783 | } | |
| 1784 | if (rel.target == .section) { | |
| 1785 | const source_sect = object.sections.items[rel.target.section]; | |
| 1786 | args.source_source_sect_addr = sect.inner.addr; | |
| 1787 | args.source_target_sect_addr = source_sect.inner.addr; | |
| 1788 | } | |
| 1789 | ||
| 1790 | const flags = @truncate(u8, target_sect.flags & 0xff); | |
| 1791 | const should_rebase = rebase: { | |
| 1792 | if (!unsigned.is_64bit) break :rebase false; | |
| 1793 | ||
| 1794 | // TODO actually, a check similar to what dyld is doing, that is, verifying | |
| 1795 | // that the segment is writable should be enough here. | |
| 1796 | const is_right_segment = blk: { | |
| 1797 | if (self.data_segment_cmd_index) |idx| { | |
| 1798 | if (target_map.segment_id == idx) { | |
| 1799 | break :blk true; | |
| 1800 | } | |
| 1801 | } | |
| 1802 | if (self.data_const_segment_cmd_index) |idx| { | |
| 1803 | if (target_map.segment_id == idx) { | |
| 1804 | break :blk true; | |
| 1805 | } | |
| 1806 | } | |
| 1807 | break :blk false; | |
| 1808 | }; | |
| 1809 | ||
| 1810 | if (!is_right_segment) break :rebase false; | |
| 1811 | if (flags != macho.S_LITERAL_POINTERS and | |
| 1812 | flags != macho.S_REGULAR) | |
| 1813 | { | |
| 1814 | break :rebase false; | |
| 1815 | } | |
| 1816 | if (rel.target == .symbol) { | |
| 1817 | const final = rel.target.symbol.getTopmostAlias(); | |
| 1818 | if (final.cast(Symbol.Proxy)) |_| { | |
| 1819 | break :rebase false; | |
| 1820 | } | |
| 1821 | } | |
| 1822 | ||
| 1823 | break :rebase true; | |
| 1824 | }; | |
| 1825 | ||
| 1826 | if (should_rebase) { | |
| 1827 | try self.local_rebases.append(self.allocator, .{ | |
| 1828 | .offset = source_addr - target_seg.inner.vmaddr, | |
| 1829 | .segment_id = target_map.segment_id, | |
| 1830 | }); | |
| 1831 | } | |
| 1832 | ||
| 1833 | // TLV is handled via a separate offset mechanism. | |
| 1834 | // Calculate the offset to the initializer. | |
| 1835 | if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { | |
| 1836 | // TODO we don't want to save offset to tlv_bootstrap | |
| 1837 | if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; | |
| 1838 | ||
| 1839 | const base_addr = blk: { | |
| 1840 | if (self.tlv_data_section_index) |index| { | |
| 1841 | const tlv_data = target_seg.sections.items[index]; | |
| 1842 | break :blk tlv_data.addr; | |
| 1843 | } else { | |
| 1844 | const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; | |
| 1845 | break :blk tlv_bss.addr; | |
| 1846 | } | |
| 1847 | }; | |
| 1848 | // Since we require TLV data to always preceed TLV bss section, we calculate | |
| 1849 | // offsets wrt to the former if it is defined; otherwise, wrt to the latter. | |
| 1850 | try self.threadlocal_offsets.append(self.allocator, .{ | |
| 1851 | .source_addr = args.source_addr, | |
| 1852 | .offset = args.target_addr - base_addr, | |
| 1853 | }); | |
| 1854 | } | |
| 1855 | }, | |
| 1856 | .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { | |
| 1857 | const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1858 | const got = dc_seg.sections.items[self.got_section_index.?]; | |
| 1859 | const final = rel.target.symbol.getTopmostAlias(); | |
| 1860 | const got_index = final.got_index orelse { | |
| 1861 | log.err("expected GOT index relocating symbol '{s}'", .{final.name}); | |
| 1862 | log.err("this is an internal linker error", .{}); | |
| 1863 | return error.FailedToResolveRelocationTarget; | |
| 1864 | }; | |
| 1865 | args.target_addr = got.addr + got_index * @sizeOf(u64); | |
| 1866 | }, | |
| 1867 | else => |tt| { | |
| 1868 | if (tt == .signed and rel.target == .section) { | |
| 1869 | const source_sect = object.sections.items[rel.target.section]; | |
| 1870 | args.source_source_sect_addr = sect.inner.addr; | |
| 1871 | args.source_target_sect_addr = source_sect.inner.addr; | |
| 1872 | } | |
| 1873 | args.target_addr = try self.relocTargetAddr(object, rel.target); | |
| 1874 | }, | |
| 1875 | } | |
| 1876 | ||
| 1877 | try rel.resolve(args); | |
| 1878 | } | |
| 1879 | } | |
| 1880 | ||
| 1881 | log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{ | |
| 1882 | segname, | |
| 1883 | sectname, | |
| 1884 | object.name, | |
| 1885 | target_sect_off, | |
| 1886 | target_sect_off + sect.code.len, | |
| 1887 | }); | |
| 1888 | ||
| 1889 | if (target_sect.flags == macho.S_ZEROFILL or | |
| 1890 | target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or | |
| 1891 | target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) | |
| 1892 | { | |
| 1893 | log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{ | |
| 1894 | parseName(&target_sect.segname), | |
| 1895 | parseName(&target_sect.sectname), | |
| 1896 | target_sect_off, | |
| 1897 | target_sect_off + sect.code.len, | |
| 1898 | }); | |
| 1899 | ||
| 1900 | // Zero-out the space | |
| 1901 | var zeroes = try self.allocator.alloc(u8, sect.code.len); | |
| 1902 | defer self.allocator.free(zeroes); | |
| 1903 | mem.set(u8, zeroes, 0); | |
| 1904 | try self.file.?.pwriteAll(zeroes, target_sect_off); | |
| 1905 | } else { | |
| 1906 | try self.file.?.pwriteAll(sect.code, target_sect_off); | |
| 1907 | } | |
| 1908 | } | |
| 1909 | } | |
| 1910 | } | |
| 1911 | ||
| 1912 | fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 { | |
| 1913 | const target_addr = blk: { | |
| 1914 | switch (target) { | |
| 1915 | .symbol => |sym| { | |
| 1916 | const final = sym.getTopmostAlias(); | |
| 1917 | if (final.cast(Symbol.Regular)) |reg| { | |
| 1918 | log.debug(" | regular '{s}'", .{sym.name}); | |
| 1919 | break :blk reg.address; | |
| 1920 | } else if (final.cast(Symbol.Proxy)) |proxy| { | |
| 1921 | if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { | |
| 1922 | log.debug(" | symbol '__tlv_bootstrap'", .{}); | |
| 1923 | const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1924 | const tlv = segment.sections.items[self.tlv_section_index.?]; | |
| 1925 | break :blk tlv.addr; | |
| 1926 | } | |
| 1927 | ||
| 1928 | log.debug(" | symbol stub '{s}'", .{sym.name}); | |
| 1929 | const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1930 | const stubs = segment.sections.items[self.stubs_section_index.?]; | |
| 1931 | const stubs_index = proxy.base.stubs_index orelse { | |
| 1932 | if (proxy.bind_info.items.len > 0) { | |
| 1933 | break :blk 0; // Dynamically bound by dyld. | |
| 1934 | } | |
| 1935 | log.err( | |
| 1936 | "expected stubs index or dynamic bind address when relocating symbol '{s}'", | |
| 1937 | .{final.name}, | |
| 1938 | ); | |
| 1939 | log.err("this is an internal linker error", .{}); | |
| 1940 | return error.FailedToResolveRelocationTarget; | |
| 1941 | }; | |
| 1942 | break :blk stubs.addr + stubs_index * stubs.reserved2; | |
| 1943 | } else { | |
| 1944 | log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); | |
| 1945 | log.err("this is an internal linker error", .{}); | |
| 1946 | return error.FailedToResolveRelocationTarget; | |
| 1947 | } | |
| 1948 | }, | |
| 1949 | .section => |sect_id| { | |
| 1950 | log.debug(" | section offset", .{}); | |
| 1951 | const source_sect = object.sections.items[sect_id]; | |
| 1952 | log.debug(" | section '{s},{s}'", .{ | |
| 1953 | parseName(&source_sect.inner.segname), | |
| 1954 | parseName(&source_sect.inner.sectname), | |
| 1955 | }); | |
| 1956 | const target_map = source_sect.target_map orelse unreachable; | |
| 1957 | const target_seg = self.load_commands.items[target_map.segment_id].Segment; | |
| 1958 | const target_sect = target_seg.sections.items[target_map.section_id]; | |
| 1959 | break :blk target_sect.addr + target_map.offset; | |
| 1960 | }, | |
| 1961 | } | |
| 1962 | }; | |
| 1963 | return target_addr; | |
| 1964 | } | |
| 1965 | ||
| 1966 | fn populateMetadata(self: *Zld) !void { | |
| 1967 | if (self.pagezero_segment_cmd_index == null) { | |
| 1968 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1969 | try self.load_commands.append(self.allocator, .{ | |
| 1970 | .Segment = SegmentCommand.empty("__PAGEZERO", .{ | |
| 1971 | .vmsize = 0x100000000, // size always set to 4GB | |
| 1972 | }), | |
| 1973 | }); | |
| 1974 | } | |
| 1975 | ||
| 1976 | if (self.text_segment_cmd_index == null) { | |
| 1977 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1978 | try self.load_commands.append(self.allocator, .{ | |
| 1979 | .Segment = SegmentCommand.empty("__TEXT", .{ | |
| 1980 | .vmaddr = 0x100000000, // always starts at 4GB | |
| 1981 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 1982 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 1983 | }), | |
| 1984 | }); | |
| 1985 | } | |
| 1986 | ||
| 1987 | if (self.text_section_index == null) { | |
| 1988 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1989 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1990 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 1991 | .x86_64 => 0, | |
| 1992 | .aarch64 => 2, | |
| 1993 | else => unreachable, // unhandled architecture type | |
| 1994 | }; | |
| 1995 | try text_seg.addSection(self.allocator, "__text", .{ | |
| 1996 | .@"align" = alignment, | |
| 1997 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1998 | }); | |
| 1999 | } | |
| 2000 | ||
| 2001 | if (self.stubs_section_index == null) { | |
| 2002 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2003 | self.stubs_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2004 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 2005 | .x86_64 => 0, | |
| 2006 | .aarch64 => 2, | |
| 2007 | else => unreachable, // unhandled architecture type | |
| 2008 | }; | |
| 2009 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 2010 | .x86_64 => 6, | |
| 2011 | .aarch64 => 3 * @sizeOf(u32), | |
| 2012 | else => unreachable, // unhandled architecture type | |
| 2013 | }; | |
| 2014 | try text_seg.addSection(self.allocator, "__stubs", .{ | |
| 2015 | .@"align" = alignment, | |
| 2016 | .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2017 | .reserved2 = stub_size, | |
| 2018 | }); | |
| 2019 | } | |
| 2020 | ||
| 2021 | if (self.stub_helper_section_index == null) { | |
| 2022 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2023 | self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2024 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 2025 | .x86_64 => 0, | |
| 2026 | .aarch64 => 2, | |
| 2027 | else => unreachable, // unhandled architecture type | |
| 2028 | }; | |
| 2029 | const stub_helper_size: u6 = switch (self.target.?.cpu.arch) { | |
| 2030 | .x86_64 => 15, | |
| 2031 | .aarch64 => 6 * @sizeOf(u32), | |
| 2032 | else => unreachable, | |
| 2033 | }; | |
| 2034 | try text_seg.addSection(self.allocator, "__stub_helper", .{ | |
| 2035 | .size = stub_helper_size, | |
| 2036 | .@"align" = alignment, | |
| 2037 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2038 | }); | |
| 2039 | } | |
| 2040 | ||
| 2041 | if (self.data_const_segment_cmd_index == null) { | |
| 2042 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2043 | try self.load_commands.append(self.allocator, .{ | |
| 2044 | .Segment = SegmentCommand.empty("__DATA_CONST", .{ | |
| 2045 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2046 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2047 | }), | |
| 2048 | }); | |
| 2049 | } | |
| 2050 | ||
| 2051 | if (self.got_section_index == null) { | |
| 2052 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2053 | self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 2054 | try data_const_seg.addSection(self.allocator, "__got", .{ | |
| 2055 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2056 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, | |
| 2057 | }); | |
| 2058 | } | |
| 2059 | ||
| 2060 | if (self.data_segment_cmd_index == null) { | |
| 2061 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2062 | try self.load_commands.append(self.allocator, .{ | |
| 2063 | .Segment = SegmentCommand.empty("__DATA", .{ | |
| 2064 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2065 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2066 | }), | |
| 2067 | }); | |
| 2068 | } | |
| 2069 | ||
| 2070 | if (self.la_symbol_ptr_section_index == null) { | |
| 2071 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2072 | self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2073 | try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{ | |
| 2074 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2075 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | |
| 2076 | }); | |
| 2077 | } | |
| 2078 | ||
| 2079 | if (self.data_section_index == null) { | |
| 2080 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2081 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2082 | try data_seg.addSection(self.allocator, "__data", .{ | |
| 2083 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2084 | }); | |
| 2085 | } | |
| 2086 | ||
| 2087 | if (self.linkedit_segment_cmd_index == null) { | |
| 2088 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2089 | try self.load_commands.append(self.allocator, .{ | |
| 2090 | .Segment = SegmentCommand.empty("__LINKEDIT", .{ | |
| 2091 | .maxprot = macho.VM_PROT_READ, | |
| 2092 | .initprot = macho.VM_PROT_READ, | |
| 2093 | }), | |
| 2094 | }); | |
| 2095 | } | |
| 2096 | ||
| 2097 | if (self.dyld_info_cmd_index == null) { | |
| 2098 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2099 | try self.load_commands.append(self.allocator, .{ | |
| 2100 | .DyldInfoOnly = .{ | |
| 2101 | .cmd = macho.LC_DYLD_INFO_ONLY, | |
| 2102 | .cmdsize = @sizeOf(macho.dyld_info_command), | |
| 2103 | .rebase_off = 0, | |
| 2104 | .rebase_size = 0, | |
| 2105 | .bind_off = 0, | |
| 2106 | .bind_size = 0, | |
| 2107 | .weak_bind_off = 0, | |
| 2108 | .weak_bind_size = 0, | |
| 2109 | .lazy_bind_off = 0, | |
| 2110 | .lazy_bind_size = 0, | |
| 2111 | .export_off = 0, | |
| 2112 | .export_size = 0, | |
| 2113 | }, | |
| 2114 | }); | |
| 2115 | } | |
| 2116 | ||
| 2117 | if (self.symtab_cmd_index == null) { | |
| 2118 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2119 | try self.load_commands.append(self.allocator, .{ | |
| 2120 | .Symtab = .{ | |
| 2121 | .cmd = macho.LC_SYMTAB, | |
| 2122 | .cmdsize = @sizeOf(macho.symtab_command), | |
| 2123 | .symoff = 0, | |
| 2124 | .nsyms = 0, | |
| 2125 | .stroff = 0, | |
| 2126 | .strsize = 0, | |
| 2127 | }, | |
| 2128 | }); | |
| 2129 | try self.strtab.append(self.allocator, 0); | |
| 2130 | } | |
| 2131 | ||
| 2132 | if (self.dysymtab_cmd_index == null) { | |
| 2133 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2134 | try self.load_commands.append(self.allocator, .{ | |
| 2135 | .Dysymtab = .{ | |
| 2136 | .cmd = macho.LC_DYSYMTAB, | |
| 2137 | .cmdsize = @sizeOf(macho.dysymtab_command), | |
| 2138 | .ilocalsym = 0, | |
| 2139 | .nlocalsym = 0, | |
| 2140 | .iextdefsym = 0, | |
| 2141 | .nextdefsym = 0, | |
| 2142 | .iundefsym = 0, | |
| 2143 | .nundefsym = 0, | |
| 2144 | .tocoff = 0, | |
| 2145 | .ntoc = 0, | |
| 2146 | .modtaboff = 0, | |
| 2147 | .nmodtab = 0, | |
| 2148 | .extrefsymoff = 0, | |
| 2149 | .nextrefsyms = 0, | |
| 2150 | .indirectsymoff = 0, | |
| 2151 | .nindirectsyms = 0, | |
| 2152 | .extreloff = 0, | |
| 2153 | .nextrel = 0, | |
| 2154 | .locreloff = 0, | |
| 2155 | .nlocrel = 0, | |
| 2156 | }, | |
| 2157 | }); | |
| 2158 | } | |
| 2159 | ||
| 2160 | if (self.dylinker_cmd_index == null) { | |
| 2161 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2162 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2163 | u64, | |
| 2164 | @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), | |
| 2165 | @sizeOf(u64), | |
| 2166 | )); | |
| 2167 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ | |
| 2168 | .cmd = macho.LC_LOAD_DYLINKER, | |
| 2169 | .cmdsize = cmdsize, | |
| 2170 | .name = @sizeOf(macho.dylinker_command), | |
| 2171 | }); | |
| 2172 | dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); | |
| 2173 | mem.set(u8, dylinker_cmd.data, 0); | |
| 2174 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); | |
| 2175 | try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd }); | |
| 2176 | } | |
| 2177 | ||
| 2178 | if (self.main_cmd_index == null and self.output.?.tag == .exe) { | |
| 2179 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2180 | try self.load_commands.append(self.allocator, .{ | |
| 2181 | .Main = .{ | |
| 2182 | .cmd = macho.LC_MAIN, | |
| 2183 | .cmdsize = @sizeOf(macho.entry_point_command), | |
| 2184 | .entryoff = 0x0, | |
| 2185 | .stacksize = 0, | |
| 2186 | }, | |
| 2187 | }); | |
| 2188 | } | |
| 2189 | ||
| 2190 | if (self.dylib_id_cmd_index == null and self.output.?.tag == .dylib) { | |
| 2191 | self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2192 | var dylib_cmd = try createLoadDylibCommand( | |
| 2193 | self.allocator, | |
| 2194 | self.output.?.install_name.?, | |
| 2195 | 2, | |
| 2196 | 0x10000, // TODO forward user-provided versions | |
| 2197 | 0x10000, | |
| 2198 | ); | |
| 2199 | errdefer dylib_cmd.deinit(self.allocator); | |
| 2200 | dylib_cmd.inner.cmd = macho.LC_ID_DYLIB; | |
| 2201 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | |
| 2202 | } | |
| 2203 | ||
| 2204 | if (self.version_min_cmd_index == null) { | |
| 2205 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2206 | const cmd: u32 = switch (self.target.?.os.tag) { | |
| 2207 | .macos => macho.LC_VERSION_MIN_MACOSX, | |
| 2208 | .ios => macho.LC_VERSION_MIN_IPHONEOS, | |
| 2209 | .tvos => macho.LC_VERSION_MIN_TVOS, | |
| 2210 | .watchos => macho.LC_VERSION_MIN_WATCHOS, | |
| 2211 | else => unreachable, // wrong OS | |
| 2212 | }; | |
| 2213 | const ver = self.target.?.os.version_range.semver.min; | |
| 2214 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; | |
| 2215 | try self.load_commands.append(self.allocator, .{ | |
| 2216 | .VersionMin = .{ | |
| 2217 | .cmd = cmd, | |
| 2218 | .cmdsize = @sizeOf(macho.version_min_command), | |
| 2219 | .version = version, | |
| 2220 | .sdk = version, | |
| 2221 | }, | |
| 2222 | }); | |
| 2223 | } | |
| 2224 | ||
| 2225 | if (self.source_version_cmd_index == null) { | |
| 2226 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2227 | try self.load_commands.append(self.allocator, .{ | |
| 2228 | .SourceVersion = .{ | |
| 2229 | .cmd = macho.LC_SOURCE_VERSION, | |
| 2230 | .cmdsize = @sizeOf(macho.source_version_command), | |
| 2231 | .version = 0x0, | |
| 2232 | }, | |
| 2233 | }); | |
| 2234 | } | |
| 2235 | ||
| 2236 | if (self.uuid_cmd_index == null) { | |
| 2237 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2238 | var uuid_cmd: macho.uuid_command = .{ | |
| 2239 | .cmd = macho.LC_UUID, | |
| 2240 | .cmdsize = @sizeOf(macho.uuid_command), | |
| 2241 | .uuid = undefined, | |
| 2242 | }; | |
| 2243 | std.crypto.random.bytes(&uuid_cmd.uuid); | |
| 2244 | try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd }); | |
| 2245 | } | |
| 2246 | } | |
| 2247 | ||
| 2248 | fn addDataInCodeLC(self: *Zld) !void { | |
| 2249 | if (self.data_in_code_cmd_index == null) { | |
| 2250 | self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2251 | try self.load_commands.append(self.allocator, .{ | |
| 2252 | .LinkeditData = .{ | |
| 2253 | .cmd = macho.LC_DATA_IN_CODE, | |
| 2254 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2255 | .dataoff = 0, | |
| 2256 | .datasize = 0, | |
| 2257 | }, | |
| 2258 | }); | |
| 2259 | } | |
| 2260 | } | |
| 2261 | ||
| 2262 | fn addCodeSignatureLC(self: *Zld) !void { | |
| 2263 | if (self.code_signature_cmd_index == null and self.target.?.cpu.arch == .aarch64) { | |
| 2264 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2265 | try self.load_commands.append(self.allocator, .{ | |
| 2266 | .LinkeditData = .{ | |
| 2267 | .cmd = macho.LC_CODE_SIGNATURE, | |
| 2268 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2269 | .dataoff = 0, | |
| 2270 | .datasize = 0, | |
| 2271 | }, | |
| 2272 | }); | |
| 2273 | } | |
| 2274 | } | |
| 2275 | ||
| 2276 | fn addRpaths(self: *Zld, rpaths: []const []const u8) !void { | |
| 2277 | for (rpaths) |rpath| { | |
| 2278 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2279 | u64, | |
| 2280 | @sizeOf(macho.rpath_command) + rpath.len + 1, | |
| 2281 | @sizeOf(u64), | |
| 2282 | )); | |
| 2283 | var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{ | |
| 2284 | .cmd = macho.LC_RPATH, | |
| 2285 | .cmdsize = cmdsize, | |
| 2286 | .path = @sizeOf(macho.rpath_command), | |
| 2287 | }); | |
| 2288 | rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); | |
| 2289 | mem.set(u8, rpath_cmd.data, 0); | |
| 2290 | mem.copy(u8, rpath_cmd.data, rpath); | |
| 2291 | try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd }); | |
| 2292 | } | |
| 2293 | } | |
| 2294 | ||
| 2295 | fn flush(self: *Zld) !void { | |
| 2296 | try self.writeStubHelperCommon(); | |
| 2297 | try self.resolveRelocsAndWriteSections(); | |
| 2298 | ||
| 2299 | if (self.common_section_index) |index| { | |
| 2300 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2301 | const sect = &seg.sections.items[index]; | |
| 2302 | sect.offset = 0; | |
| 2303 | } | |
| 2304 | ||
| 2305 | if (self.bss_section_index) |index| { | |
| 2306 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2307 | const sect = &seg.sections.items[index]; | |
| 2308 | sect.offset = 0; | |
| 2309 | } | |
| 2310 | ||
| 2311 | if (self.tlv_bss_section_index) |index| { | |
| 2312 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2313 | const sect = &seg.sections.items[index]; | |
| 2314 | sect.offset = 0; | |
| 2315 | } | |
| 2316 | ||
| 2317 | if (self.tlv_section_index) |index| { | |
| 2318 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2319 | const sect = &seg.sections.items[index]; | |
| 2320 | ||
| 2321 | var buffer = try self.allocator.alloc(u8, @intCast(usize, sect.size)); | |
| 2322 | defer self.allocator.free(buffer); | |
| 2323 | _ = try self.file.?.preadAll(buffer, sect.offset); | |
| 2324 | ||
| 2325 | var stream = std.io.fixedBufferStream(buffer); | |
| 2326 | var writer = stream.writer(); | |
| 2327 | ||
| 2328 | std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp); | |
| 2329 | ||
| 2330 | const seek_amt = 2 * @sizeOf(u64); | |
| 2331 | for (self.threadlocal_offsets.items) |tlv| { | |
| 2332 | try writer.context.seekBy(seek_amt); | |
| 2333 | try writer.writeIntLittle(u64, tlv.offset); | |
| 2334 | } | |
| 2335 | ||
| 2336 | try self.file.?.pwriteAll(buffer, sect.offset); | |
| 2337 | } | |
| 2338 | ||
| 2339 | if (self.mod_init_func_section_index) |index| { | |
| 2340 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2341 | const sect = &seg.sections.items[index]; | |
| 2342 | ||
| 2343 | var initializers = std.ArrayList(u64).init(self.allocator); | |
| 2344 | defer initializers.deinit(); | |
| 2345 | ||
| 2346 | for (self.objects.items) |object| { | |
| 2347 | for (object.initializers.items) |initializer| { | |
| 2348 | const address = initializer.cast(Symbol.Regular).?.address; | |
| 2349 | try initializers.append(address); | |
| 2350 | } | |
| 2351 | } | |
| 2352 | ||
| 2353 | _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset); | |
| 2354 | sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64)); | |
| 2355 | } | |
| 2356 | ||
| 2357 | try self.writeGotEntries(); | |
| 2358 | try self.setEntryPoint(); | |
| 2359 | try self.writeRebaseInfoTable(); | |
| 2360 | try self.writeBindInfoTable(); | |
| 2361 | try self.writeLazyBindInfoTable(); | |
| 2362 | try self.writeExportInfo(); | |
| 2363 | try self.writeDataInCode(); | |
| 2364 | ||
| 2365 | { | |
| 2366 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2367 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2368 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2369 | } | |
| 2370 | ||
| 2371 | try self.writeDebugInfo(); | |
| 2372 | try self.writeSymbolTable(); | |
| 2373 | try self.writeStringTable(); | |
| 2374 | ||
| 2375 | { | |
| 2376 | // Seal __LINKEDIT size | |
| 2377 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2378 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); | |
| 2379 | } | |
| 2380 | ||
| 2381 | if (self.target.?.cpu.arch == .aarch64) { | |
| 2382 | try self.writeCodeSignaturePadding(); | |
| 2383 | } | |
| 2384 | ||
| 2385 | try self.writeLoadCommands(); | |
| 2386 | try self.writeHeader(); | |
| 2387 | ||
| 2388 | if (self.target.?.cpu.arch == .aarch64) { | |
| 2389 | try self.writeCodeSignature(); | |
| 2390 | } | |
| 2391 | ||
| 2392 | if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { | |
| 2393 | const out_path = self.output.?.path; | |
| 2394 | try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); | |
| 2395 | } | |
| 2396 | } | |
| 2397 | ||
| 2398 | fn writeGotEntries(self: *Zld) !void { | |
| 2399 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2400 | const sect = seg.sections.items[self.got_section_index.?]; | |
| 2401 | ||
| 2402 | var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); | |
| 2403 | defer self.allocator.free(buffer); | |
| 2404 | ||
| 2405 | var stream = std.io.fixedBufferStream(buffer); | |
| 2406 | var writer = stream.writer(); | |
| 2407 | ||
| 2408 | for (self.got_entries.items) |sym| { | |
| 2409 | const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0; | |
| 2410 | try writer.writeIntLittle(u64, address); | |
| 2411 | } | |
| 2412 | ||
| 2413 | log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); | |
| 2414 | ||
| 2415 | try self.file.?.pwriteAll(buffer, sect.offset); | |
| 2416 | } | |
| 2417 | ||
| 2418 | fn setEntryPoint(self: *Zld) !void { | |
| 2419 | if (self.output.?.tag != .exe) return; | |
| 2420 | ||
| 2421 | // TODO we should respect the -entry flag passed in by the user to set a custom | |
| 2422 | // entrypoint. For now, assume default of `_main`. | |
| 2423 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2424 | const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; | |
| 2425 | const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; | |
| 2426 | const ec = &self.load_commands.items[self.main_cmd_index.?].Main; | |
| 2427 | ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); | |
| 2428 | ec.stacksize = self.stack_size; | |
| 2429 | } | |
| 2430 | ||
| 2431 | fn writeRebaseInfoTable(self: *Zld) !void { | |
| 2432 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2433 | defer pointers.deinit(); | |
| 2434 | ||
| 2435 | try pointers.ensureCapacity(self.local_rebases.items.len); | |
| 2436 | pointers.appendSliceAssumeCapacity(self.local_rebases.items); | |
| 2437 | ||
| 2438 | if (self.got_section_index) |idx| { | |
| 2439 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2440 | const sect = seg.sections.items[idx]; | |
| 2441 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2442 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2443 | ||
| 2444 | for (self.got_entries.items) |sym| { | |
| 2445 | if (sym.@"type" == .proxy) continue; | |
| 2446 | try pointers.append(.{ | |
| 2447 | .offset = base_offset + sym.got_index.? * @sizeOf(u64), | |
| 2448 | .segment_id = segment_id, | |
| 2449 | }); | |
| 2450 | } | |
| 2451 | } | |
| 2452 | ||
| 2453 | if (self.mod_init_func_section_index) |idx| { | |
| 2454 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2455 | const sect = seg.sections.items[idx]; | |
| 2456 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2457 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2458 | ||
| 2459 | var index: u64 = 0; | |
| 2460 | for (self.objects.items) |object| { | |
| 2461 | for (object.initializers.items) |_| { | |
| 2462 | try pointers.append(.{ | |
| 2463 | .offset = base_offset + index * @sizeOf(u64), | |
| 2464 | .segment_id = segment_id, | |
| 2465 | }); | |
| 2466 | index += 1; | |
| 2467 | } | |
| 2468 | } | |
| 2469 | } | |
| 2470 | ||
| 2471 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2472 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2473 | const sect = seg.sections.items[idx]; | |
| 2474 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2475 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2476 | ||
| 2477 | try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len); | |
| 2478 | for (self.stubs.items) |sym| { | |
| 2479 | pointers.appendAssumeCapacity(.{ | |
| 2480 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), | |
| 2481 | .segment_id = segment_id, | |
| 2482 | }); | |
| 2483 | } | |
| 2484 | } | |
| 2485 | ||
| 2486 | std.sort.sort(Pointer, pointers.items, {}, pointerCmp); | |
| 2487 | ||
| 2488 | const size = try rebaseInfoSize(pointers.items); | |
| 2489 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2490 | defer self.allocator.free(buffer); | |
| 2491 | ||
| 2492 | var stream = std.io.fixedBufferStream(buffer); | |
| 2493 | try writeRebaseInfo(pointers.items, stream.writer()); | |
| 2494 | ||
| 2495 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2496 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2497 | dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); | |
| 2498 | dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64))); | |
| 2499 | seg.inner.filesize += dyld_info.rebase_size; | |
| 2500 | ||
| 2501 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); | |
| 2502 | ||
| 2503 | try self.file.?.pwriteAll(buffer, dyld_info.rebase_off); | |
| 2504 | } | |
| 2505 | ||
| 2506 | fn writeBindInfoTable(self: *Zld) !void { | |
| 2507 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2508 | defer pointers.deinit(); | |
| 2509 | ||
| 2510 | if (self.got_section_index) |idx| { | |
| 2511 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2512 | const sect = seg.sections.items[idx]; | |
| 2513 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2514 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2515 | ||
| 2516 | for (self.got_entries.items) |sym| { | |
| 2517 | if (sym.cast(Symbol.Proxy)) |proxy| { | |
| 2518 | try pointers.append(.{ | |
| 2519 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), | |
| 2520 | .segment_id = segment_id, | |
| 2521 | .dylib_ordinal = proxy.dylibOrdinal(), | |
| 2522 | .name = proxy.base.name, | |
| 2523 | }); | |
| 2524 | } | |
| 2525 | } | |
| 2526 | } | |
| 2527 | ||
| 2528 | for (self.imports.values()) |sym| { | |
| 2529 | if (sym.cast(Symbol.Proxy)) |proxy| { | |
| 2530 | for (proxy.bind_info.items) |info| { | |
| 2531 | const seg = self.load_commands.items[info.segment_id].Segment; | |
| 2532 | try pointers.append(.{ | |
| 2533 | .offset = info.address - seg.inner.vmaddr, | |
| 2534 | .segment_id = info.segment_id, | |
| 2535 | .dylib_ordinal = proxy.dylibOrdinal(), | |
| 2536 | .name = proxy.base.name, | |
| 2537 | }); | |
| 2538 | } | |
| 2539 | } | |
| 2540 | } | |
| 2541 | ||
| 2542 | if (self.tlv_section_index) |idx| { | |
| 2543 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2544 | const sect = seg.sections.items[idx]; | |
| 2545 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2546 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2547 | ||
| 2548 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; | |
| 2549 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | |
| 2550 | ||
| 2551 | try pointers.append(.{ | |
| 2552 | .offset = base_offset, | |
| 2553 | .segment_id = segment_id, | |
| 2554 | .dylib_ordinal = proxy.dylibOrdinal(), | |
| 2555 | .name = proxy.base.name, | |
| 2556 | }); | |
| 2557 | } | |
| 2558 | ||
| 2559 | const size = try bindInfoSize(pointers.items); | |
| 2560 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2561 | defer self.allocator.free(buffer); | |
| 2562 | ||
| 2563 | var stream = std.io.fixedBufferStream(buffer); | |
| 2564 | try writeBindInfo(pointers.items, stream.writer()); | |
| 2565 | ||
| 2566 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2567 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2568 | dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2569 | dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2570 | seg.inner.filesize += dyld_info.bind_size; | |
| 2571 | ||
| 2572 | log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); | |
| 2573 | ||
| 2574 | try self.file.?.pwriteAll(buffer, dyld_info.bind_off); | |
| 2575 | } | |
| 2576 | ||
| 2577 | fn writeLazyBindInfoTable(self: *Zld) !void { | |
| 2578 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2579 | defer pointers.deinit(); | |
| 2580 | ||
| 2581 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2582 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2583 | const sect = seg.sections.items[idx]; | |
| 2584 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2585 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2586 | ||
| 2587 | try pointers.ensureCapacity(self.stubs.items.len); | |
| 2588 | ||
| 2589 | for (self.stubs.items) |sym| { | |
| 2590 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | |
| 2591 | pointers.appendAssumeCapacity(.{ | |
| 2592 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), | |
| 2593 | .segment_id = segment_id, | |
| 2594 | .dylib_ordinal = proxy.dylibOrdinal(), | |
| 2595 | .name = sym.name, | |
| 2596 | }); | |
| 2597 | } | |
| 2598 | } | |
| 2599 | ||
| 2600 | const size = try lazyBindInfoSize(pointers.items); | |
| 2601 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2602 | defer self.allocator.free(buffer); | |
| 2603 | ||
| 2604 | var stream = std.io.fixedBufferStream(buffer); | |
| 2605 | try writeLazyBindInfo(pointers.items, stream.writer()); | |
| 2606 | ||
| 2607 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2608 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2609 | dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2610 | dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2611 | seg.inner.filesize += dyld_info.lazy_bind_size; | |
| 2612 | ||
| 2613 | log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); | |
| 2614 | ||
| 2615 | try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | |
| 2616 | try self.populateLazyBindOffsetsInStubHelper(buffer); | |
| 2617 | } | |
| 2618 | ||
| 2619 | fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { | |
| 2620 | var stream = std.io.fixedBufferStream(buffer); | |
| 2621 | var reader = stream.reader(); | |
| 2622 | var offsets = std.ArrayList(u32).init(self.allocator); | |
| 2623 | try offsets.append(0); | |
| 2624 | defer offsets.deinit(); | |
| 2625 | var valid_block = false; | |
| 2626 | ||
| 2627 | while (true) { | |
| 2628 | const inst = reader.readByte() catch |err| switch (err) { | |
| 2629 | error.EndOfStream => break, | |
| 2630 | else => return err, | |
| 2631 | }; | |
| 2632 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; | |
| 2633 | ||
| 2634 | switch (opcode) { | |
| 2635 | macho.BIND_OPCODE_DO_BIND => { | |
| 2636 | valid_block = true; | |
| 2637 | }, | |
| 2638 | macho.BIND_OPCODE_DONE => { | |
| 2639 | if (valid_block) { | |
| 2640 | const offset = try stream.getPos(); | |
| 2641 | try offsets.append(@intCast(u32, offset)); | |
| 2642 | } | |
| 2643 | valid_block = false; | |
| 2644 | }, | |
| 2645 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { | |
| 2646 | var next = try reader.readByte(); | |
| 2647 | while (next != @as(u8, 0)) { | |
| 2648 | next = try reader.readByte(); | |
| 2649 | } | |
| 2650 | }, | |
| 2651 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { | |
| 2652 | _ = try leb.readULEB128(u64, reader); | |
| 2653 | }, | |
| 2654 | macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { | |
| 2655 | _ = try leb.readULEB128(u64, reader); | |
| 2656 | }, | |
| 2657 | macho.BIND_OPCODE_SET_ADDEND_SLEB => { | |
| 2658 | _ = try leb.readILEB128(i64, reader); | |
| 2659 | }, | |
| 2660 | else => {}, | |
| 2661 | } | |
| 2662 | } | |
| 2663 | assert(self.stubs.items.len <= offsets.items.len); | |
| 2664 | ||
| 2665 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 2666 | .x86_64 => 10, | |
| 2667 | .aarch64 => 3 * @sizeOf(u32), | |
| 2668 | else => unreachable, | |
| 2669 | }; | |
| 2670 | const off: u4 = switch (self.target.?.cpu.arch) { | |
| 2671 | .x86_64 => 1, | |
| 2672 | .aarch64 => 2 * @sizeOf(u32), | |
| 2673 | else => unreachable, | |
| 2674 | }; | |
| 2675 | var buf: [@sizeOf(u32)]u8 = undefined; | |
| 2676 | for (self.stubs.items) |sym| { | |
| 2677 | const index = sym.stubs_index orelse unreachable; | |
| 2678 | const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; | |
| 2679 | mem.writeIntLittle(u32, &buf, offsets.items[index]); | |
| 2680 | try self.file.?.pwriteAll(&buf, placeholder_off); | |
| 2681 | } | |
| 2682 | } | |
| 2683 | ||
| 2684 | fn writeExportInfo(self: *Zld) !void { | |
| 2685 | var trie = Trie.init(self.allocator); | |
| 2686 | defer trie.deinit(); | |
| 2687 | ||
| 2688 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2689 | const base_address = text_segment.inner.vmaddr; | |
| 2690 | ||
| 2691 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. | |
| 2692 | log.debug("writing export trie", .{}); | |
| 2693 | ||
| 2694 | const Sorter = struct { | |
| 2695 | fn lessThan(_: void, a: []const u8, b: []const u8) bool { | |
| 2696 | return mem.lessThan(u8, a, b); | |
| 2697 | } | |
| 2698 | }; | |
| 2699 | ||
| 2700 | var sorted_globals = std.ArrayList([]const u8).init(self.allocator); | |
| 2701 | defer sorted_globals.deinit(); | |
| 2702 | ||
| 2703 | for (self.globals.values()) |sym| { | |
| 2704 | const reg = sym.cast(Symbol.Regular) orelse continue; | |
| 2705 | if (reg.linkage != .global) continue; | |
| 2706 | try sorted_globals.append(sym.name); | |
| 2707 | } | |
| 2708 | ||
| 2709 | std.sort.sort([]const u8, sorted_globals.items, {}, Sorter.lessThan); | |
| 2710 | ||
| 2711 | for (sorted_globals.items) |sym_name| { | |
| 2712 | const sym = self.globals.get(sym_name) orelse unreachable; | |
| 2713 | const reg = sym.cast(Symbol.Regular) orelse unreachable; | |
| 2714 | ||
| 2715 | log.debug(" | putting '{s}' defined at 0x{x}", .{ reg.base.name, reg.address }); | |
| 2716 | ||
| 2717 | try trie.put(.{ | |
| 2718 | .name = sym.name, | |
| 2719 | .vmaddr_offset = reg.address - base_address, | |
| 2720 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, | |
| 2721 | }); | |
| 2722 | } | |
| 2723 | ||
| 2724 | try trie.finalize(); | |
| 2725 | ||
| 2726 | var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size)); | |
| 2727 | defer self.allocator.free(buffer); | |
| 2728 | ||
| 2729 | var stream = std.io.fixedBufferStream(buffer); | |
| 2730 | const nwritten = try trie.write(stream.writer()); | |
| 2731 | assert(nwritten == trie.size); | |
| 2732 | ||
| 2733 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2734 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2735 | dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2736 | dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2737 | seg.inner.filesize += dyld_info.export_size; | |
| 2738 | ||
| 2739 | log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); | |
| 2740 | ||
| 2741 | try self.file.?.pwriteAll(buffer, dyld_info.export_off); | |
| 2742 | } | |
| 2743 | ||
| 2744 | fn writeDebugInfo(self: *Zld) !void { | |
| 2745 | var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); | |
| 2746 | defer stabs.deinit(); | |
| 2747 | ||
| 2748 | for (self.objects.items) |object| { | |
| 2749 | const tu_path = object.tu_path orelse continue; | |
| 2750 | const tu_mtime = object.tu_mtime orelse continue; | |
| 2751 | _ = tu_mtime; | |
| 2752 | const dirname = std.fs.path.dirname(tu_path) orelse "./"; | |
| 2753 | // Current dir | |
| 2754 | try stabs.append(.{ | |
| 2755 | .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]), | |
| 2756 | .n_type = macho.N_SO, | |
| 2757 | .n_sect = 0, | |
| 2758 | .n_desc = 0, | |
| 2759 | .n_value = 0, | |
| 2760 | }); | |
| 2761 | // Artifact name | |
| 2762 | try stabs.append(.{ | |
| 2763 | .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]), | |
| 2764 | .n_type = macho.N_SO, | |
| 2765 | .n_sect = 0, | |
| 2766 | .n_desc = 0, | |
| 2767 | .n_value = 0, | |
| 2768 | }); | |
| 2769 | // Path to object file with debug info | |
| 2770 | try stabs.append(.{ | |
| 2771 | .n_strx = try self.makeString(object.name.?), | |
| 2772 | .n_type = macho.N_OSO, | |
| 2773 | .n_sect = 0, | |
| 2774 | .n_desc = 1, | |
| 2775 | .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work | |
| 2776 | }); | |
| 2777 | ||
| 2778 | for (object.symbols.items) |sym| { | |
| 2779 | const reg = reg: { | |
| 2780 | switch (sym.@"type") { | |
| 2781 | .regular => break :reg sym.cast(Symbol.Regular) orelse unreachable, | |
| 2782 | .tentative => { | |
| 2783 | const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable; | |
| 2784 | if (object != final.file) continue; | |
| 2785 | break :reg final; | |
| 2786 | }, | |
| 2787 | else => continue, | |
| 2788 | } | |
| 2789 | }; | |
| 2790 | ||
| 2791 | if (reg.isTemp() or reg.stab == null) continue; | |
| 2792 | const stab = reg.stab orelse unreachable; | |
| 2793 | ||
| 2794 | switch (stab.kind) { | |
| 2795 | .function => { | |
| 2796 | try stabs.append(.{ | |
| 2797 | .n_strx = 0, | |
| 2798 | .n_type = macho.N_BNSYM, | |
| 2799 | .n_sect = reg.section, | |
| 2800 | .n_desc = 0, | |
| 2801 | .n_value = reg.address, | |
| 2802 | }); | |
| 2803 | try stabs.append(.{ | |
| 2804 | .n_strx = try self.makeString(sym.name), | |
| 2805 | .n_type = macho.N_FUN, | |
| 2806 | .n_sect = reg.section, | |
| 2807 | .n_desc = 0, | |
| 2808 | .n_value = reg.address, | |
| 2809 | }); | |
| 2810 | try stabs.append(.{ | |
| 2811 | .n_strx = 0, | |
| 2812 | .n_type = macho.N_FUN, | |
| 2813 | .n_sect = 0, | |
| 2814 | .n_desc = 0, | |
| 2815 | .n_value = stab.size, | |
| 2816 | }); | |
| 2817 | try stabs.append(.{ | |
| 2818 | .n_strx = 0, | |
| 2819 | .n_type = macho.N_ENSYM, | |
| 2820 | .n_sect = reg.section, | |
| 2821 | .n_desc = 0, | |
| 2822 | .n_value = stab.size, | |
| 2823 | }); | |
| 2824 | }, | |
| 2825 | .global => { | |
| 2826 | try stabs.append(.{ | |
| 2827 | .n_strx = try self.makeString(sym.name), | |
| 2828 | .n_type = macho.N_GSYM, | |
| 2829 | .n_sect = 0, | |
| 2830 | .n_desc = 0, | |
| 2831 | .n_value = 0, | |
| 2832 | }); | |
| 2833 | }, | |
| 2834 | .static => { | |
| 2835 | try stabs.append(.{ | |
| 2836 | .n_strx = try self.makeString(sym.name), | |
| 2837 | .n_type = macho.N_STSYM, | |
| 2838 | .n_sect = reg.section, | |
| 2839 | .n_desc = 0, | |
| 2840 | .n_value = reg.address, | |
| 2841 | }); | |
| 2842 | }, | |
| 2843 | } | |
| 2844 | } | |
| 2845 | ||
| 2846 | // Close the source file! | |
| 2847 | try stabs.append(.{ | |
| 2848 | .n_strx = 0, | |
| 2849 | .n_type = macho.N_SO, | |
| 2850 | .n_sect = 0, | |
| 2851 | .n_desc = 0, | |
| 2852 | .n_value = 0, | |
| 2853 | }); | |
| 2854 | } | |
| 2855 | ||
| 2856 | if (stabs.items.len == 0) return; | |
| 2857 | ||
| 2858 | // Write stabs into the symbol table | |
| 2859 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2860 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2861 | ||
| 2862 | symtab.nsyms = @intCast(u32, stabs.items.len); | |
| 2863 | ||
| 2864 | const stabs_off = symtab.symoff; | |
| 2865 | const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64); | |
| 2866 | log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off }); | |
| 2867 | try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off); | |
| 2868 | ||
| 2869 | linkedit.inner.filesize += stabs_size; | |
| 2870 | ||
| 2871 | // Update dynamic symbol table. | |
| 2872 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | |
| 2873 | dysymtab.nlocalsym = symtab.nsyms; | |
| 2874 | } | |
| 2875 | ||
| 2876 | fn writeSymbolTable(self: *Zld) !void { | |
| 2877 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2878 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2879 | ||
| 2880 | var locals = std.ArrayList(macho.nlist_64).init(self.allocator); | |
| 2881 | defer locals.deinit(); | |
| 2882 | ||
| 2883 | var exports = std.ArrayList(macho.nlist_64).init(self.allocator); | |
| 2884 | defer exports.deinit(); | |
| 2885 | ||
| 2886 | for (self.objects.items) |object| { | |
| 2887 | for (object.symbols.items) |sym| { | |
| 2888 | const final = sym.getTopmostAlias(); | |
| 2889 | if (final.@"type" != .regular) continue; | |
| 2890 | ||
| 2891 | const reg = final.cast(Symbol.Regular) orelse unreachable; | |
| 2892 | if (reg.isTemp()) continue; | |
| 2893 | if (reg.visited) continue; | |
| 2894 | ||
| 2895 | switch (reg.linkage) { | |
| 2896 | .translation_unit => { | |
| 2897 | try locals.append(.{ | |
| 2898 | .n_strx = try self.makeString(sym.name), | |
| 2899 | .n_type = macho.N_SECT, | |
| 2900 | .n_sect = reg.section, | |
| 2901 | .n_desc = 0, | |
| 2902 | .n_value = reg.address, | |
| 2903 | }); | |
| 2904 | }, | |
| 2905 | else => { | |
| 2906 | try exports.append(.{ | |
| 2907 | .n_strx = try self.makeString(sym.name), | |
| 2908 | .n_type = macho.N_SECT | macho.N_EXT, | |
| 2909 | .n_sect = reg.section, | |
| 2910 | .n_desc = 0, | |
| 2911 | .n_value = reg.address, | |
| 2912 | }); | |
| 2913 | }, | |
| 2914 | } | |
| 2915 | ||
| 2916 | reg.visited = true; | |
| 2917 | } | |
| 2918 | } | |
| 2919 | ||
| 2920 | var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); | |
| 2921 | defer undefs.deinit(); | |
| 2922 | ||
| 2923 | for (self.imports.values()) |sym| { | |
| 2924 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | |
| 2925 | try undefs.append(.{ | |
| 2926 | .n_strx = try self.makeString(sym.name), | |
| 2927 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 2928 | .n_sect = 0, | |
| 2929 | .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, | |
| 2930 | .n_value = 0, | |
| 2931 | }); | |
| 2932 | } | |
| 2933 | ||
| 2934 | const nlocals = locals.items.len; | |
| 2935 | const nexports = exports.items.len; | |
| 2936 | const nundefs = undefs.items.len; | |
| 2937 | ||
| 2938 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | |
| 2939 | const locals_size = nlocals * @sizeOf(macho.nlist_64); | |
| 2940 | log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); | |
| 2941 | try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); | |
| 2942 | ||
| 2943 | const exports_off = locals_off + locals_size; | |
| 2944 | const exports_size = nexports * @sizeOf(macho.nlist_64); | |
| 2945 | log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); | |
| 2946 | try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off); | |
| 2947 | ||
| 2948 | const undefs_off = exports_off + exports_size; | |
| 2949 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); | |
| 2950 | log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); | |
| 2951 | try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); | |
| 2952 | ||
| 2953 | symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs); | |
| 2954 | seg.inner.filesize += locals_size + exports_size + undefs_size; | |
| 2955 | ||
| 2956 | // Update dynamic symbol table. | |
| 2957 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | |
| 2958 | dysymtab.nlocalsym += @intCast(u32, nlocals); | |
| 2959 | dysymtab.iextdefsym = dysymtab.nlocalsym; | |
| 2960 | dysymtab.nextdefsym = @intCast(u32, nexports); | |
| 2961 | dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; | |
| 2962 | dysymtab.nundefsym = @intCast(u32, nundefs); | |
| 2963 | ||
| 2964 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2965 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; | |
| 2966 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2967 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 2968 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2969 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 2970 | ||
| 2971 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 2972 | const ngot_entries = @intCast(u32, self.got_entries.items.len); | |
| 2973 | ||
| 2974 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2975 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; | |
| 2976 | ||
| 2977 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | |
| 2978 | seg.inner.filesize += needed_size; | |
| 2979 | ||
| 2980 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ | |
| 2981 | dysymtab.indirectsymoff, | |
| 2982 | dysymtab.indirectsymoff + needed_size, | |
| 2983 | }); | |
| 2984 | ||
| 2985 | var buf = try self.allocator.alloc(u8, needed_size); | |
| 2986 | defer self.allocator.free(buf); | |
| 2987 | ||
| 2988 | var stream = std.io.fixedBufferStream(buf); | |
| 2989 | var writer = stream.writer(); | |
| 2990 | ||
| 2991 | stubs.reserved1 = 0; | |
| 2992 | for (self.stubs.items) |sym| { | |
| 2993 | const id = self.imports.getIndex(sym.name) orelse unreachable; | |
| 2994 | try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); | |
| 2995 | } | |
| 2996 | ||
| 2997 | got.reserved1 = nstubs; | |
| 2998 | for (self.got_entries.items) |sym| { | |
| 2999 | if (sym.@"type" == .proxy) { | |
| 3000 | const id = self.imports.getIndex(sym.name) orelse unreachable; | |
| 3001 | try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); | |
| 3002 | } else { | |
| 3003 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 3004 | } | |
| 3005 | } | |
| 3006 | ||
| 3007 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; | |
| 3008 | for (self.stubs.items) |sym| { | |
| 3009 | const id = self.imports.getIndex(sym.name) orelse unreachable; | |
| 3010 | try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); | |
| 3011 | } | |
| 3012 | ||
| 3013 | try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | |
| 3014 | } | |
| 3015 | ||
| 3016 | fn writeStringTable(self: *Zld) !void { | |
| 3017 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3018 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 3019 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 3020 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); | |
| 3021 | seg.inner.filesize += symtab.strsize; | |
| 3022 | ||
| 3023 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | |
| 3024 | ||
| 3025 | try self.file.?.pwriteAll(self.strtab.items, symtab.stroff); | |
| 3026 | ||
| 3027 | if (symtab.strsize > self.strtab.items.len and self.target.?.cpu.arch == .x86_64) { | |
| 3028 | // This is the last section, so we need to pad it out. | |
| 3029 | try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | |
| 3030 | } | |
| 3031 | } | |
| 3032 | ||
| 3033 | fn writeDataInCode(self: *Zld) !void { | |
| 3034 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3035 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; | |
| 3036 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 3037 | ||
| 3038 | var buf = std.ArrayList(u8).init(self.allocator); | |
| 3039 | defer buf.deinit(); | |
| 3040 | ||
| 3041 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 3042 | const text_sect = text_seg.sections.items[self.text_section_index.?]; | |
| 3043 | for (self.objects.items) |object| { | |
| 3044 | const source_sect = object.sections.items[object.text_section_index.?]; | |
| 3045 | const target_map = source_sect.target_map orelse continue; | |
| 3046 | ||
| 3047 | try buf.ensureCapacity( | |
| 3048 | buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry), | |
| 3049 | ); | |
| 3050 | for (object.data_in_code_entries.items) |dice| { | |
| 3051 | const new_dice: macho.data_in_code_entry = .{ | |
| 3052 | .offset = text_sect.offset + target_map.offset + dice.offset, | |
| 3053 | .length = dice.length, | |
| 3054 | .kind = dice.kind, | |
| 3055 | }; | |
| 3056 | buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice)); | |
| 3057 | } | |
| 3058 | } | |
| 3059 | const datasize = @intCast(u32, buf.items.len); | |
| 3060 | ||
| 3061 | dice_cmd.dataoff = @intCast(u32, fileoff); | |
| 3062 | dice_cmd.datasize = datasize; | |
| 3063 | seg.inner.filesize += datasize; | |
| 3064 | ||
| 3065 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize }); | |
| 3066 | ||
| 3067 | try self.file.?.pwriteAll(buf.items, fileoff); | |
| 3068 | } | |
| 3069 | ||
| 3070 | fn writeCodeSignaturePadding(self: *Zld) !void { | |
| 3071 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 3072 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | |
| 3073 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 3074 | const needed_size = CodeSignature.calcCodeSignaturePaddingSize( | |
| 3075 | self.output.?.path, | |
| 3076 | fileoff, | |
| 3077 | self.page_size.?, | |
| 3078 | ); | |
| 3079 | code_sig_cmd.dataoff = @intCast(u32, fileoff); | |
| 3080 | code_sig_cmd.datasize = needed_size; | |
| 3081 | ||
| 3082 | // Advance size of __LINKEDIT segment | |
| 3083 | seg.inner.filesize += needed_size; | |
| 3084 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); | |
| 3085 | ||
| 3086 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); | |
| 3087 | ||
| 3088 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | |
| 3089 | // except for code signature data. | |
| 3090 | try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); | |
| 3091 | } | |
| 3092 | ||
| 3093 | fn writeCodeSignature(self: *Zld) !void { | |
| 3094 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 3095 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | |
| 3096 | ||
| 3097 | var code_sig = CodeSignature.init(self.allocator, self.page_size.?); | |
| 3098 | defer code_sig.deinit(); | |
| 3099 | try code_sig.calcAdhocSignature( | |
| 3100 | self.file.?, | |
| 3101 | self.output.?.path, | |
| 3102 | text_seg.inner, | |
| 3103 | code_sig_cmd, | |
| 3104 | .Exe, | |
| 3105 | ); | |
| 3106 | ||
| 3107 | var buffer = try self.allocator.alloc(u8, code_sig.size()); | |
| 3108 | defer self.allocator.free(buffer); | |
| 3109 | var stream = std.io.fixedBufferStream(buffer); | |
| 3110 | try code_sig.write(stream.writer()); | |
| 3111 | ||
| 3112 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); | |
| 3113 | try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); | |
| 3114 | } | |
| 3115 | ||
| 3116 | fn writeLoadCommands(self: *Zld) !void { | |
| 3117 | var sizeofcmds: u32 = 0; | |
| 3118 | for (self.load_commands.items) |lc| { | |
| 3119 | sizeofcmds += lc.cmdsize(); | |
| 3120 | } | |
| 3121 | ||
| 3122 | var buffer = try self.allocator.alloc(u8, sizeofcmds); | |
| 3123 | defer self.allocator.free(buffer); | |
| 3124 | var writer = std.io.fixedBufferStream(buffer).writer(); | |
| 3125 | for (self.load_commands.items) |lc| { | |
| 3126 | try lc.write(writer); | |
| 3127 | } | |
| 3128 | ||
| 3129 | const off = @sizeOf(macho.mach_header_64); | |
| 3130 | log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); | |
| 3131 | try self.file.?.pwriteAll(buffer, off); | |
| 3132 | } | |
| 3133 | ||
| 3134 | fn writeHeader(self: *Zld) !void { | |
| 3135 | var header: macho.mach_header_64 = undefined; | |
| 3136 | header.magic = macho.MH_MAGIC_64; | |
| 3137 | ||
| 3138 | const CpuInfo = struct { | |
| 3139 | cpu_type: macho.cpu_type_t, | |
| 3140 | cpu_subtype: macho.cpu_subtype_t, | |
| 3141 | }; | |
| 3142 | ||
| 3143 | const cpu_info: CpuInfo = switch (self.target.?.cpu.arch) { | |
| 3144 | .aarch64 => .{ | |
| 3145 | .cpu_type = macho.CPU_TYPE_ARM64, | |
| 3146 | .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL, | |
| 3147 | }, | |
| 3148 | .x86_64 => .{ | |
| 3149 | .cpu_type = macho.CPU_TYPE_X86_64, | |
| 3150 | .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL, | |
| 3151 | }, | |
| 3152 | else => return error.UnsupportedCpuArchitecture, | |
| 3153 | }; | |
| 3154 | header.cputype = cpu_info.cpu_type; | |
| 3155 | header.cpusubtype = cpu_info.cpu_subtype; | |
| 3156 | ||
| 3157 | switch (self.output.?.tag) { | |
| 3158 | .exe => { | |
| 3159 | header.filetype = macho.MH_EXECUTE; | |
| 3160 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; | |
| 3161 | }, | |
| 3162 | .dylib => { | |
| 3163 | header.filetype = macho.MH_DYLIB; | |
| 3164 | header.flags = macho.MH_NOUNDEFS | | |
| 3165 | macho.MH_DYLDLINK | | |
| 3166 | macho.MH_PIE | | |
| 3167 | macho.MH_TWOLEVEL | | |
| 3168 | macho.MH_NO_REEXPORTED_DYLIBS; | |
| 3169 | }, | |
| 3170 | } | |
| 3171 | ||
| 3172 | header.reserved = 0; | |
| 3173 | ||
| 3174 | if (self.tlv_section_index) |_| | |
| 3175 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 3176 | ||
| 3177 | header.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 3178 | header.sizeofcmds = 0; | |
| 3179 | for (self.load_commands.items) |cmd| { | |
| 3180 | header.sizeofcmds += cmd.cmdsize(); | |
| 3181 | } | |
| 3182 | log.debug("writing Mach-O header {}", .{header}); | |
| 3183 | try self.file.?.pwriteAll(mem.asBytes(&header), 0); | |
| 3184 | } | |
| 3185 | ||
| 3186 | fn makeString(self: *Zld, bytes: []const u8) !u32 { | |
| 3187 | if (self.strtab_dir.get(bytes)) |offset| { | |
| 3188 | log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); | |
| 3189 | return offset; | |
| 3190 | } | |
| 3191 | ||
| 3192 | try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1); | |
| 3193 | const offset = @intCast(u32, self.strtab.items.len); | |
| 3194 | log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset }); | |
| 3195 | self.strtab.appendSliceAssumeCapacity(bytes); | |
| 3196 | self.strtab.appendAssumeCapacity(0); | |
| 3197 | try self.strtab_dir.putNoClobber(self.allocator, try self.allocator.dupe(u8, bytes), offset); | |
| 3198 | return offset; | |
| 3199 | } | |
| 3200 | ||
| 3201 | fn getString(self: *const Zld, str_off: u32) []const u8 { | |
| 3202 | assert(str_off < self.strtab.items.len); | |
| 3203 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off)); | |
| 3204 | } | |
| 3205 | ||
| 3206 | pub fn parseName(name: *const [16]u8) []const u8 { | |
| 3207 | const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; | |
| 3208 | return name[0..len]; | |
| 3209 | } |
src/link/MachO/commands.zig+60| ... | ... | @@ -11,6 +11,28 @@ const Allocator = std.mem.Allocator; |
| 11 | 11 | const MachO = @import("../MachO.zig"); |
| 12 | 12 | const padToIdeal = MachO.padToIdeal; |
| 13 | 13 | |
| 14 | pub const HeaderArgs = struct { | |
| 15 | magic: u32 = macho.MH_MAGIC_64, | |
| 16 | cputype: macho.cpu_type_t = 0, | |
| 17 | cpusubtype: macho.cpu_subtype_t = 0, | |
| 18 | filetype: u32 = 0, | |
| 19 | flags: u32 = 0, | |
| 20 | reserved: u32 = 0, | |
| 21 | }; | |
| 22 | ||
| 23 | pub fn emptyHeader(args: HeaderArgs) macho.mach_header_64 { | |
| 24 | return .{ | |
| 25 | .magic = args.magic, | |
| 26 | .cputype = args.cputype, | |
| 27 | .cpusubtype = args.cpusubtype, | |
| 28 | .filetype = args.filetype, | |
| 29 | .ncmds = 0, | |
| 30 | .sizeofcmds = 0, | |
| 31 | .flags = args.flags, | |
| 32 | .reserved = args.reserved, | |
| 33 | }; | |
| 34 | } | |
| 35 | ||
| 14 | 36 | pub const LoadCommand = union(enum) { |
| 15 | 37 | Segment: SegmentCommand, |
| 16 | 38 | DyldInfoOnly: macho.dyld_info_command, |
| ... | ... | @@ -403,6 +425,44 @@ fn makeStaticString(bytes: []const u8) [16]u8 { |
| 403 | 425 | return buf; |
| 404 | 426 | } |
| 405 | 427 | |
| 428 | fn parseName(name: *const [16]u8) []const u8 { | |
| 429 | const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; | |
| 430 | return name[0..len]; | |
| 431 | } | |
| 432 | ||
| 433 | pub fn segmentName(sect: macho.section_64) []const u8 { | |
| 434 | return parseName(&sect.segname); | |
| 435 | } | |
| 436 | ||
| 437 | pub fn sectionName(sect: macho.section_64) []const u8 { | |
| 438 | return parseName(&sect.sectname); | |
| 439 | } | |
| 440 | ||
| 441 | pub fn sectionType(sect: macho.section_64) u8 { | |
| 442 | return @truncate(u8, sect.flags & 0xff); | |
| 443 | } | |
| 444 | ||
| 445 | pub fn sectionAttrs(sect: macho.section_64) u32 { | |
| 446 | return sect.flags & 0xffffff00; | |
| 447 | } | |
| 448 | ||
| 449 | pub fn sectionIsCode(sect: macho.section_64) bool { | |
| 450 | const attr = sectionAttrs(sect); | |
| 451 | return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0; | |
| 452 | } | |
| 453 | ||
| 454 | pub fn sectionIsDebug(sect: macho.section_64) bool { | |
| 455 | return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0; | |
| 456 | } | |
| 457 | ||
| 458 | pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool { | |
| 459 | return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0; | |
| 460 | } | |
| 461 | ||
| 462 | pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool { | |
| 463 | return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0; | |
| 464 | } | |
| 465 | ||
| 406 | 466 | fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void { |
| 407 | 467 | var stream = io.fixedBufferStream(buffer); |
| 408 | 468 | var given = try LoadCommand.read(allocator, stream.reader()); |
src/link/MachO/reloc.zig deleted-206| ... | ... | @@ -1,206 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const log = std.log.scoped(.reloc); | |
| 4 | const macho = std.macho; | |
| 5 | const math = std.math; | |
| 6 | const mem = std.mem; | |
| 7 | const meta = std.meta; | |
| 8 | ||
| 9 | const aarch64 = @import("reloc/aarch64.zig"); | |
| 10 | const x86_64 = @import("reloc/x86_64.zig"); | |
| 11 | ||
| 12 | const Allocator = mem.Allocator; | |
| 13 | const Symbol = @import("Symbol.zig"); | |
| 14 | ||
| 15 | pub const Relocation = struct { | |
| 16 | @"type": Type, | |
| 17 | code: []u8, | |
| 18 | offset: u32, | |
| 19 | target: Target, | |
| 20 | ||
| 21 | pub fn cast(base: *Relocation, comptime T: type) ?*T { | |
| 22 | if (base.@"type" != T.base_type) | |
| 23 | return null; | |
| 24 | ||
| 25 | return @fieldParentPtr(T, "base", base); | |
| 26 | } | |
| 27 | ||
| 28 | pub const ResolveArgs = struct { | |
| 29 | source_addr: u64, | |
| 30 | target_addr: u64, | |
| 31 | subtractor: ?u64 = null, | |
| 32 | source_source_sect_addr: ?u64 = null, | |
| 33 | source_target_sect_addr: ?u64 = null, | |
| 34 | }; | |
| 35 | ||
| 36 | pub fn resolve(base: *Relocation, args: ResolveArgs) !void { | |
| 37 | log.debug("{s}", .{base.@"type"}); | |
| 38 | log.debug(" | offset 0x{x}", .{base.offset}); | |
| 39 | log.debug(" | source address 0x{x}", .{args.source_addr}); | |
| 40 | log.debug(" | target address 0x{x}", .{args.target_addr}); | |
| 41 | if (args.subtractor) |sub| | |
| 42 | log.debug(" | subtractor address 0x{x}", .{sub}); | |
| 43 | if (args.source_source_sect_addr) |addr| | |
| 44 | log.debug(" | source source section address 0x{x}", .{addr}); | |
| 45 | if (args.source_target_sect_addr) |addr| | |
| 46 | log.debug(" | source target section address 0x{x}", .{addr}); | |
| 47 | ||
| 48 | return switch (base.@"type") { | |
| 49 | .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(args), | |
| 50 | .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(args), | |
| 51 | .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(args), | |
| 52 | .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(args), | |
| 53 | .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(args), | |
| 54 | .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(args), | |
| 55 | .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(args), | |
| 56 | .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(args), | |
| 57 | .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(args), | |
| 58 | .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(args), | |
| 59 | .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(args), | |
| 60 | .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(args), | |
| 61 | .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(args), | |
| 62 | .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(args), | |
| 63 | }; | |
| 64 | } | |
| 65 | ||
| 66 | pub const Type = enum { | |
| 67 | branch_aarch64, | |
| 68 | unsigned, | |
| 69 | page, | |
| 70 | page_off, | |
| 71 | got_page, | |
| 72 | got_page_off, | |
| 73 | tlvp_page, | |
| 74 | pointer_to_got, | |
| 75 | tlvp_page_off, | |
| 76 | branch_x86_64, | |
| 77 | signed, | |
| 78 | got_load, | |
| 79 | got, | |
| 80 | tlv, | |
| 81 | }; | |
| 82 | ||
| 83 | pub const Target = union(enum) { | |
| 84 | symbol: *Symbol, | |
| 85 | section: u16, | |
| 86 | ||
| 87 | pub fn from_reloc(reloc: macho.relocation_info, symbols: []*Symbol) Target { | |
| 88 | return if (reloc.r_extern == 1) .{ | |
| 89 | .symbol = symbols[reloc.r_symbolnum], | |
| 90 | } else .{ | |
| 91 | .section = @intCast(u16, reloc.r_symbolnum - 1), | |
| 92 | }; | |
| 93 | } | |
| 94 | }; | |
| 95 | }; | |
| 96 | ||
| 97 | pub const Unsigned = struct { | |
| 98 | base: Relocation, | |
| 99 | subtractor: ?Relocation.Target = null, | |
| 100 | /// Addend embedded directly in the relocation slot | |
| 101 | addend: i64, | |
| 102 | /// Extracted from r_length: | |
| 103 | /// => 3 implies true | |
| 104 | /// => 2 implies false | |
| 105 | /// => * is unreachable | |
| 106 | is_64bit: bool, | |
| 107 | ||
| 108 | pub const base_type: Relocation.Type = .unsigned; | |
| 109 | ||
| 110 | pub fn resolve(unsigned: Unsigned, args: Relocation.ResolveArgs) !void { | |
| 111 | const addend = if (unsigned.base.target == .section) | |
| 112 | unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) | |
| 113 | else | |
| 114 | unsigned.addend; | |
| 115 | ||
| 116 | const result = if (args.subtractor) |subtractor| | |
| 117 | @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | |
| 118 | else | |
| 119 | @intCast(i64, args.target_addr) + addend; | |
| 120 | ||
| 121 | log.debug(" | calculated addend 0x{x}", .{addend}); | |
| 122 | log.debug(" | calculated unsigned value 0x{x}", .{result}); | |
| 123 | ||
| 124 | if (unsigned.is_64bit) { | |
| 125 | mem.writeIntLittle( | |
| 126 | u64, | |
| 127 | unsigned.base.code[0..8], | |
| 128 | @bitCast(u64, result), | |
| 129 | ); | |
| 130 | } else { | |
| 131 | mem.writeIntLittle( | |
| 132 | u32, | |
| 133 | unsigned.base.code[0..4], | |
| 134 | @truncate(u32, @bitCast(u64, result)), | |
| 135 | ); | |
| 136 | } | |
| 137 | } | |
| 138 | }; | |
| 139 | ||
| 140 | pub fn parse( | |
| 141 | allocator: *Allocator, | |
| 142 | arch: std.Target.Cpu.Arch, | |
| 143 | code: []u8, | |
| 144 | relocs: []const macho.relocation_info, | |
| 145 | symbols: []*Symbol, | |
| 146 | ) ![]*Relocation { | |
| 147 | var it = RelocIterator{ | |
| 148 | .buffer = relocs, | |
| 149 | }; | |
| 150 | ||
| 151 | switch (arch) { | |
| 152 | .aarch64 => { | |
| 153 | var parser = aarch64.Parser{ | |
| 154 | .allocator = allocator, | |
| 155 | .it = &it, | |
| 156 | .code = code, | |
| 157 | .parsed = std.ArrayList(*Relocation).init(allocator), | |
| 158 | .symbols = symbols, | |
| 159 | }; | |
| 160 | defer parser.deinit(); | |
| 161 | try parser.parse(); | |
| 162 | ||
| 163 | return parser.parsed.toOwnedSlice(); | |
| 164 | }, | |
| 165 | .x86_64 => { | |
| 166 | var parser = x86_64.Parser{ | |
| 167 | .allocator = allocator, | |
| 168 | .it = &it, | |
| 169 | .code = code, | |
| 170 | .parsed = std.ArrayList(*Relocation).init(allocator), | |
| 171 | .symbols = symbols, | |
| 172 | }; | |
| 173 | defer parser.deinit(); | |
| 174 | try parser.parse(); | |
| 175 | ||
| 176 | return parser.parsed.toOwnedSlice(); | |
| 177 | }, | |
| 178 | else => unreachable, | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | pub const RelocIterator = struct { | |
| 183 | buffer: []const macho.relocation_info, | |
| 184 | index: i32 = -1, | |
| 185 | ||
| 186 | pub fn next(self: *RelocIterator) ?macho.relocation_info { | |
| 187 | self.index += 1; | |
| 188 | if (self.index < self.buffer.len) { | |
| 189 | const reloc = self.buffer[@intCast(u32, self.index)]; | |
| 190 | log.debug("relocation", .{}); | |
| 191 | log.debug(" | type = {}", .{reloc.r_type}); | |
| 192 | log.debug(" | offset = {}", .{reloc.r_address}); | |
| 193 | log.debug(" | PC = {}", .{reloc.r_pcrel == 1}); | |
| 194 | log.debug(" | length = {}", .{reloc.r_length}); | |
| 195 | log.debug(" | symbolnum = {}", .{reloc.r_symbolnum}); | |
| 196 | log.debug(" | extern = {}", .{reloc.r_extern == 1}); | |
| 197 | return reloc; | |
| 198 | } | |
| 199 | return null; | |
| 200 | } | |
| 201 | ||
| 202 | pub fn peek(self: RelocIterator) macho.relocation_info { | |
| 203 | assert(self.index + 1 < self.buffer.len); | |
| 204 | return self.buffer[@intCast(u32, self.index + 1)]; | |
| 205 | } | |
| 206 | }; |
src/link/MachO/reloc/aarch64.zig deleted-628| ... | ... | @@ -1,628 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const aarch64 = @import("../../../codegen/aarch64.zig"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const log = std.log.scoped(.reloc); | |
| 5 | const macho = std.macho; | |
| 6 | const math = std.math; | |
| 7 | const mem = std.mem; | |
| 8 | const meta = std.meta; | |
| 9 | const reloc = @import("../reloc.zig"); | |
| 10 | ||
| 11 | const Allocator = mem.Allocator; | |
| 12 | const Relocation = reloc.Relocation; | |
| 13 | const Symbol = @import("../Symbol.zig"); | |
| 14 | ||
| 15 | pub const Branch = struct { | |
| 16 | base: Relocation, | |
| 17 | /// Always .UnconditionalBranchImmediate | |
| 18 | inst: aarch64.Instruction, | |
| 19 | ||
| 20 | pub const base_type: Relocation.Type = .branch_aarch64; | |
| 21 | ||
| 22 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 23 | const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 24 | ||
| 25 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 26 | ||
| 27 | var inst = branch.inst; | |
| 28 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 29 | mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | |
| 30 | } | |
| 31 | }; | |
| 32 | ||
| 33 | pub const Page = struct { | |
| 34 | base: Relocation, | |
| 35 | addend: ?u32 = null, | |
| 36 | /// Always .PCRelativeAddress | |
| 37 | inst: aarch64.Instruction, | |
| 38 | ||
| 39 | pub const base_type: Relocation.Type = .page; | |
| 40 | ||
| 41 | pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void { | |
| 42 | const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 43 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 44 | const target_page = @intCast(i32, target_addr >> 12); | |
| 45 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 46 | ||
| 47 | log.debug(" | calculated addend 0x{x}", .{page.addend}); | |
| 48 | log.debug(" | moving by {} pages", .{pages}); | |
| 49 | ||
| 50 | var inst = page.inst; | |
| 51 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 52 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 53 | ||
| 54 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 55 | } | |
| 56 | }; | |
| 57 | ||
| 58 | pub const PageOff = struct { | |
| 59 | base: Relocation, | |
| 60 | addend: ?u32 = null, | |
| 61 | op_kind: OpKind, | |
| 62 | inst: aarch64.Instruction, | |
| 63 | ||
| 64 | pub const base_type: Relocation.Type = .page_off; | |
| 65 | ||
| 66 | pub const OpKind = enum { | |
| 67 | arithmetic, | |
| 68 | load_store, | |
| 69 | }; | |
| 70 | ||
| 71 | pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void { | |
| 72 | const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 73 | const narrowed = @truncate(u12, target_addr); | |
| 74 | ||
| 75 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 76 | log.debug(" | {s} opcode", .{page_off.op_kind}); | |
| 77 | ||
| 78 | var inst = page_off.inst; | |
| 79 | if (page_off.op_kind == .arithmetic) { | |
| 80 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 81 | } else { | |
| 82 | const offset: u12 = blk: { | |
| 83 | if (inst.load_store_register.size == 0) { | |
| 84 | if (inst.load_store_register.v == 1) { | |
| 85 | // 128-bit SIMD is scaled by 16. | |
| 86 | break :blk try math.divExact(u12, narrowed, 16); | |
| 87 | } | |
| 88 | // Otherwise, 8-bit SIMD or ldrb. | |
| 89 | break :blk narrowed; | |
| 90 | } else { | |
| 91 | const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 92 | break :blk try math.divExact(u12, narrowed, denom); | |
| 93 | } | |
| 94 | }; | |
| 95 | inst.load_store_register.offset = offset; | |
| 96 | } | |
| 97 | ||
| 98 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 99 | } | |
| 100 | }; | |
| 101 | ||
| 102 | pub const GotPage = struct { | |
| 103 | base: Relocation, | |
| 104 | /// Always .PCRelativeAddress | |
| 105 | inst: aarch64.Instruction, | |
| 106 | ||
| 107 | pub const base_type: Relocation.Type = .got_page; | |
| 108 | ||
| 109 | pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void { | |
| 110 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 111 | const target_page = @intCast(i32, args.target_addr >> 12); | |
| 112 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 113 | ||
| 114 | log.debug(" | moving by {} pages", .{pages}); | |
| 115 | ||
| 116 | var inst = page.inst; | |
| 117 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 118 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 119 | ||
| 120 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 121 | } | |
| 122 | }; | |
| 123 | ||
| 124 | pub const GotPageOff = struct { | |
| 125 | base: Relocation, | |
| 126 | /// Always .LoadStoreRegister with size = 3 for GOT indirection | |
| 127 | inst: aarch64.Instruction, | |
| 128 | ||
| 129 | pub const base_type: Relocation.Type = .got_page_off; | |
| 130 | ||
| 131 | pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void { | |
| 132 | const narrowed = @truncate(u12, args.target_addr); | |
| 133 | ||
| 134 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 135 | ||
| 136 | var inst = page_off.inst; | |
| 137 | const offset = try math.divExact(u12, narrowed, 8); | |
| 138 | inst.load_store_register.offset = offset; | |
| 139 | ||
| 140 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 141 | } | |
| 142 | }; | |
| 143 | ||
| 144 | pub const PointerToGot = struct { | |
| 145 | base: Relocation, | |
| 146 | ||
| 147 | pub const base_type: Relocation.Type = .pointer_to_got; | |
| 148 | ||
| 149 | pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void { | |
| 150 | const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 151 | ||
| 152 | log.debug(" | calculated value 0x{x}", .{result}); | |
| 153 | ||
| 154 | mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result)); | |
| 155 | } | |
| 156 | }; | |
| 157 | ||
| 158 | pub const TlvpPage = struct { | |
| 159 | base: Relocation, | |
| 160 | /// Always .PCRelativeAddress | |
| 161 | inst: aarch64.Instruction, | |
| 162 | ||
| 163 | pub const base_type: Relocation.Type = .tlvp_page; | |
| 164 | ||
| 165 | pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void { | |
| 166 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 167 | const target_page = @intCast(i32, args.target_addr >> 12); | |
| 168 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 169 | ||
| 170 | log.debug(" | moving by {} pages", .{pages}); | |
| 171 | ||
| 172 | var inst = page.inst; | |
| 173 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 174 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 175 | ||
| 176 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 177 | } | |
| 178 | }; | |
| 179 | ||
| 180 | pub const TlvpPageOff = struct { | |
| 181 | base: Relocation, | |
| 182 | /// Always .AddSubtractImmediate regardless of the source instruction. | |
| 183 | /// This means, we always rewrite the instruction to add even if the | |
| 184 | /// source instruction was an ldr. | |
| 185 | inst: aarch64.Instruction, | |
| 186 | ||
| 187 | pub const base_type: Relocation.Type = .tlvp_page_off; | |
| 188 | ||
| 189 | pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void { | |
| 190 | const narrowed = @truncate(u12, args.target_addr); | |
| 191 | ||
| 192 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 193 | ||
| 194 | var inst = page_off.inst; | |
| 195 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 196 | ||
| 197 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 198 | } | |
| 199 | }; | |
| 200 | ||
| 201 | pub const Parser = struct { | |
| 202 | allocator: *Allocator, | |
| 203 | it: *reloc.RelocIterator, | |
| 204 | code: []u8, | |
| 205 | parsed: std.ArrayList(*Relocation), | |
| 206 | symbols: []*Symbol, | |
| 207 | addend: ?u32 = null, | |
| 208 | subtractor: ?Relocation.Target = null, | |
| 209 | ||
| 210 | pub fn deinit(parser: *Parser) void { | |
| 211 | parser.parsed.deinit(); | |
| 212 | } | |
| 213 | ||
| 214 | pub fn parse(parser: *Parser) !void { | |
| 215 | while (parser.it.next()) |rel| { | |
| 216 | switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 217 | .ARM64_RELOC_BRANCH26 => { | |
| 218 | try parser.parseBranch(rel); | |
| 219 | }, | |
| 220 | .ARM64_RELOC_SUBTRACTOR => { | |
| 221 | try parser.parseSubtractor(rel); | |
| 222 | }, | |
| 223 | .ARM64_RELOC_UNSIGNED => { | |
| 224 | try parser.parseUnsigned(rel); | |
| 225 | }, | |
| 226 | .ARM64_RELOC_ADDEND => { | |
| 227 | try parser.parseAddend(rel); | |
| 228 | }, | |
| 229 | .ARM64_RELOC_PAGE21, | |
| 230 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 231 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 232 | => { | |
| 233 | try parser.parsePage(rel); | |
| 234 | }, | |
| 235 | .ARM64_RELOC_PAGEOFF12 => { | |
| 236 | try parser.parsePageOff(rel); | |
| 237 | }, | |
| 238 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => { | |
| 239 | try parser.parseGotLoadPageOff(rel); | |
| 240 | }, | |
| 241 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | |
| 242 | try parser.parseTlvpLoadPageOff(rel); | |
| 243 | }, | |
| 244 | .ARM64_RELOC_POINTER_TO_GOT => { | |
| 245 | try parser.parsePointerToGot(rel); | |
| 246 | }, | |
| 247 | } | |
| 248 | } | |
| 249 | } | |
| 250 | ||
| 251 | fn parseAddend(parser: *Parser, rel: macho.relocation_info) !void { | |
| 252 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 253 | assert(rel_type == .ARM64_RELOC_ADDEND); | |
| 254 | assert(rel.r_pcrel == 0); | |
| 255 | assert(rel.r_extern == 0); | |
| 256 | assert(parser.addend == null); | |
| 257 | ||
| 258 | parser.addend = rel.r_symbolnum; | |
| 259 | ||
| 260 | // Verify ADDEND is followed by a load. | |
| 261 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | |
| 262 | switch (next) { | |
| 263 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 264 | else => { | |
| 265 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 266 | return error.UnexpectedRelocationType; | |
| 267 | }, | |
| 268 | } | |
| 269 | } | |
| 270 | ||
| 271 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | |
| 272 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 273 | assert(rel_type == .ARM64_RELOC_BRANCH26); | |
| 274 | assert(rel.r_pcrel == 1); | |
| 275 | assert(rel.r_length == 2); | |
| 276 | ||
| 277 | const offset = @intCast(u32, rel.r_address); | |
| 278 | const inst = parser.code[offset..][0..4]; | |
| 279 | const parsed_inst = aarch64.Instruction{ .unconditional_branch_immediate = mem.bytesToValue( | |
| 280 | meta.TagPayload( | |
| 281 | aarch64.Instruction, | |
| 282 | aarch64.Instruction.unconditional_branch_immediate, | |
| 283 | ), | |
| 284 | inst, | |
| 285 | ) }; | |
| 286 | ||
| 287 | var branch = try parser.allocator.create(Branch); | |
| 288 | errdefer parser.allocator.destroy(branch); | |
| 289 | ||
| 290 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 291 | ||
| 292 | branch.* = .{ | |
| 293 | .base = .{ | |
| 294 | .@"type" = .branch_aarch64, | |
| 295 | .code = inst, | |
| 296 | .offset = offset, | |
| 297 | .target = target, | |
| 298 | }, | |
| 299 | .inst = parsed_inst, | |
| 300 | }; | |
| 301 | ||
| 302 | log.debug(" | emitting {}", .{branch}); | |
| 303 | try parser.parsed.append(&branch.base); | |
| 304 | } | |
| 305 | ||
| 306 | fn parsePage(parser: *Parser, rel: macho.relocation_info) !void { | |
| 307 | assert(rel.r_pcrel == 1); | |
| 308 | assert(rel.r_length == 2); | |
| 309 | ||
| 310 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 311 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 312 | ||
| 313 | const offset = @intCast(u32, rel.r_address); | |
| 314 | const inst = parser.code[offset..][0..4]; | |
| 315 | const parsed_inst = aarch64.Instruction{ .pc_relative_address = mem.bytesToValue(meta.TagPayload( | |
| 316 | aarch64.Instruction, | |
| 317 | aarch64.Instruction.pc_relative_address, | |
| 318 | ), inst) }; | |
| 319 | ||
| 320 | const ptr: *Relocation = ptr: { | |
| 321 | switch (rel_type) { | |
| 322 | .ARM64_RELOC_PAGE21 => { | |
| 323 | defer { | |
| 324 | // Reset parser's addend state | |
| 325 | parser.addend = null; | |
| 326 | } | |
| 327 | var page = try parser.allocator.create(Page); | |
| 328 | errdefer parser.allocator.destroy(page); | |
| 329 | ||
| 330 | page.* = .{ | |
| 331 | .base = .{ | |
| 332 | .@"type" = .page, | |
| 333 | .code = inst, | |
| 334 | .offset = offset, | |
| 335 | .target = target, | |
| 336 | }, | |
| 337 | .addend = parser.addend, | |
| 338 | .inst = parsed_inst, | |
| 339 | }; | |
| 340 | ||
| 341 | log.debug(" | emitting {}", .{page}); | |
| 342 | ||
| 343 | break :ptr &page.base; | |
| 344 | }, | |
| 345 | .ARM64_RELOC_GOT_LOAD_PAGE21 => { | |
| 346 | var page = try parser.allocator.create(GotPage); | |
| 347 | errdefer parser.allocator.destroy(page); | |
| 348 | ||
| 349 | page.* = .{ | |
| 350 | .base = .{ | |
| 351 | .@"type" = .got_page, | |
| 352 | .code = inst, | |
| 353 | .offset = offset, | |
| 354 | .target = target, | |
| 355 | }, | |
| 356 | .inst = parsed_inst, | |
| 357 | }; | |
| 358 | ||
| 359 | log.debug(" | emitting {}", .{page}); | |
| 360 | ||
| 361 | break :ptr &page.base; | |
| 362 | }, | |
| 363 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => { | |
| 364 | var page = try parser.allocator.create(TlvpPage); | |
| 365 | errdefer parser.allocator.destroy(page); | |
| 366 | ||
| 367 | page.* = .{ | |
| 368 | .base = .{ | |
| 369 | .@"type" = .tlvp_page, | |
| 370 | .code = inst, | |
| 371 | .offset = offset, | |
| 372 | .target = target, | |
| 373 | }, | |
| 374 | .inst = parsed_inst, | |
| 375 | }; | |
| 376 | ||
| 377 | log.debug(" | emitting {}", .{page}); | |
| 378 | ||
| 379 | break :ptr &page.base; | |
| 380 | }, | |
| 381 | else => unreachable, | |
| 382 | } | |
| 383 | }; | |
| 384 | ||
| 385 | try parser.parsed.append(ptr); | |
| 386 | } | |
| 387 | ||
| 388 | fn parsePageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 389 | defer { | |
| 390 | // Reset parser's addend state | |
| 391 | parser.addend = null; | |
| 392 | } | |
| 393 | ||
| 394 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 395 | assert(rel_type == .ARM64_RELOC_PAGEOFF12); | |
| 396 | assert(rel.r_pcrel == 0); | |
| 397 | assert(rel.r_length == 2); | |
| 398 | ||
| 399 | const offset = @intCast(u32, rel.r_address); | |
| 400 | const inst = parser.code[offset..][0..4]; | |
| 401 | ||
| 402 | var op_kind: PageOff.OpKind = undefined; | |
| 403 | var parsed_inst: aarch64.Instruction = undefined; | |
| 404 | if (isArithmeticOp(inst)) { | |
| 405 | op_kind = .arithmetic; | |
| 406 | parsed_inst = .{ .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | |
| 407 | aarch64.Instruction, | |
| 408 | aarch64.Instruction.add_subtract_immediate, | |
| 409 | ), inst) }; | |
| 410 | } else { | |
| 411 | op_kind = .load_store; | |
| 412 | parsed_inst = .{ .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 413 | aarch64.Instruction, | |
| 414 | aarch64.Instruction.load_store_register, | |
| 415 | ), inst) }; | |
| 416 | } | |
| 417 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 418 | ||
| 419 | var page_off = try parser.allocator.create(PageOff); | |
| 420 | errdefer parser.allocator.destroy(page_off); | |
| 421 | ||
| 422 | page_off.* = .{ | |
| 423 | .base = .{ | |
| 424 | .@"type" = .page_off, | |
| 425 | .code = inst, | |
| 426 | .offset = offset, | |
| 427 | .target = target, | |
| 428 | }, | |
| 429 | .op_kind = op_kind, | |
| 430 | .inst = parsed_inst, | |
| 431 | .addend = parser.addend, | |
| 432 | }; | |
| 433 | ||
| 434 | log.debug(" | emitting {}", .{page_off}); | |
| 435 | try parser.parsed.append(&page_off.base); | |
| 436 | } | |
| 437 | ||
| 438 | fn parseGotLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 439 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 440 | assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12); | |
| 441 | assert(rel.r_pcrel == 0); | |
| 442 | assert(rel.r_length == 2); | |
| 443 | ||
| 444 | const offset = @intCast(u32, rel.r_address); | |
| 445 | const inst = parser.code[offset..][0..4]; | |
| 446 | assert(!isArithmeticOp(inst)); | |
| 447 | ||
| 448 | const parsed_inst = mem.bytesToValue(meta.TagPayload( | |
| 449 | aarch64.Instruction, | |
| 450 | aarch64.Instruction.load_store_register, | |
| 451 | ), inst); | |
| 452 | assert(parsed_inst.size == 3); | |
| 453 | ||
| 454 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 455 | ||
| 456 | var page_off = try parser.allocator.create(GotPageOff); | |
| 457 | errdefer parser.allocator.destroy(page_off); | |
| 458 | ||
| 459 | page_off.* = .{ | |
| 460 | .base = .{ | |
| 461 | .@"type" = .got_page_off, | |
| 462 | .code = inst, | |
| 463 | .offset = offset, | |
| 464 | .target = target, | |
| 465 | }, | |
| 466 | .inst = .{ | |
| 467 | .load_store_register = parsed_inst, | |
| 468 | }, | |
| 469 | }; | |
| 470 | ||
| 471 | log.debug(" | emitting {}", .{page_off}); | |
| 472 | try parser.parsed.append(&page_off.base); | |
| 473 | } | |
| 474 | ||
| 475 | fn parseTlvpLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 476 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 477 | assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12); | |
| 478 | assert(rel.r_pcrel == 0); | |
| 479 | assert(rel.r_length == 2); | |
| 480 | ||
| 481 | const RegInfo = struct { | |
| 482 | rd: u5, | |
| 483 | rn: u5, | |
| 484 | size: u1, | |
| 485 | }; | |
| 486 | ||
| 487 | const offset = @intCast(u32, rel.r_address); | |
| 488 | const inst = parser.code[offset..][0..4]; | |
| 489 | const parsed: RegInfo = parsed: { | |
| 490 | if (isArithmeticOp(inst)) { | |
| 491 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | |
| 492 | aarch64.Instruction, | |
| 493 | aarch64.Instruction.add_subtract_immediate, | |
| 494 | ), inst); | |
| 495 | break :parsed .{ | |
| 496 | .rd = parsed_inst.rd, | |
| 497 | .rn = parsed_inst.rn, | |
| 498 | .size = parsed_inst.sf, | |
| 499 | }; | |
| 500 | } else { | |
| 501 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | |
| 502 | aarch64.Instruction, | |
| 503 | aarch64.Instruction.load_store_register, | |
| 504 | ), inst); | |
| 505 | break :parsed .{ | |
| 506 | .rd = parsed_inst.rt, | |
| 507 | .rn = parsed_inst.rn, | |
| 508 | .size = @truncate(u1, parsed_inst.size), | |
| 509 | }; | |
| 510 | } | |
| 511 | }; | |
| 512 | ||
| 513 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 514 | ||
| 515 | var page_off = try parser.allocator.create(TlvpPageOff); | |
| 516 | errdefer parser.allocator.destroy(page_off); | |
| 517 | ||
| 518 | page_off.* = .{ | |
| 519 | .base = .{ | |
| 520 | .@"type" = .tlvp_page_off, | |
| 521 | .code = inst, | |
| 522 | .offset = offset, | |
| 523 | .target = target, | |
| 524 | }, | |
| 525 | .inst = .{ | |
| 526 | .add_subtract_immediate = .{ | |
| 527 | .rd = parsed.rd, | |
| 528 | .rn = parsed.rn, | |
| 529 | .imm12 = 0, // This will be filled when target addresses are known. | |
| 530 | .sh = 0, | |
| 531 | .s = 0, | |
| 532 | .op = 0, | |
| 533 | .sf = parsed.size, | |
| 534 | }, | |
| 535 | }, | |
| 536 | }; | |
| 537 | ||
| 538 | log.debug(" | emitting {}", .{page_off}); | |
| 539 | try parser.parsed.append(&page_off.base); | |
| 540 | } | |
| 541 | ||
| 542 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | |
| 543 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 544 | assert(rel_type == .ARM64_RELOC_SUBTRACTOR); | |
| 545 | assert(rel.r_pcrel == 0); | |
| 546 | assert(parser.subtractor == null); | |
| 547 | ||
| 548 | parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 549 | ||
| 550 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 551 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | |
| 552 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 553 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 554 | return error.UnexpectedRelocationType; | |
| 555 | } | |
| 556 | } | |
| 557 | ||
| 558 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 559 | defer { | |
| 560 | // Reset parser's subtractor state | |
| 561 | parser.subtractor = null; | |
| 562 | } | |
| 563 | ||
| 564 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 565 | assert(rel_type == .ARM64_RELOC_UNSIGNED); | |
| 566 | assert(rel.r_pcrel == 0); | |
| 567 | ||
| 568 | var unsigned = try parser.allocator.create(reloc.Unsigned); | |
| 569 | errdefer parser.allocator.destroy(unsigned); | |
| 570 | ||
| 571 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 572 | const is_64bit: bool = switch (rel.r_length) { | |
| 573 | 3 => true, | |
| 574 | 2 => false, | |
| 575 | else => unreachable, | |
| 576 | }; | |
| 577 | const offset = @intCast(u32, rel.r_address); | |
| 578 | const addend: i64 = if (is_64bit) | |
| 579 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | |
| 580 | else | |
| 581 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | |
| 582 | ||
| 583 | unsigned.* = .{ | |
| 584 | .base = .{ | |
| 585 | .@"type" = .unsigned, | |
| 586 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | |
| 587 | .offset = offset, | |
| 588 | .target = target, | |
| 589 | }, | |
| 590 | .subtractor = parser.subtractor, | |
| 591 | .is_64bit = is_64bit, | |
| 592 | .addend = addend, | |
| 593 | }; | |
| 594 | ||
| 595 | log.debug(" | emitting {}", .{unsigned}); | |
| 596 | try parser.parsed.append(&unsigned.base); | |
| 597 | } | |
| 598 | ||
| 599 | fn parsePointerToGot(parser: *Parser, rel: macho.relocation_info) !void { | |
| 600 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 601 | assert(rel_type == .ARM64_RELOC_POINTER_TO_GOT); | |
| 602 | assert(rel.r_pcrel == 1); | |
| 603 | assert(rel.r_length == 2); | |
| 604 | ||
| 605 | var ptr_to_got = try parser.allocator.create(PointerToGot); | |
| 606 | errdefer parser.allocator.destroy(ptr_to_got); | |
| 607 | ||
| 608 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 609 | const offset = @intCast(u32, rel.r_address); | |
| 610 | ||
| 611 | ptr_to_got.* = .{ | |
| 612 | .base = .{ | |
| 613 | .@"type" = .pointer_to_got, | |
| 614 | .code = parser.code[offset..][0..4], | |
| 615 | .offset = offset, | |
| 616 | .target = target, | |
| 617 | }, | |
| 618 | }; | |
| 619 | ||
| 620 | log.debug(" | emitting {}", .{ptr_to_got}); | |
| 621 | try parser.parsed.append(&ptr_to_got.base); | |
| 622 | } | |
| 623 | }; | |
| 624 | ||
| 625 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 626 | const group_decode = @truncate(u5, inst[3]); | |
| 627 | return ((group_decode >> 2) == 4); | |
| 628 | } |
src/link/MachO/reloc/x86_64.zig deleted-345| ... | ... | @@ -1,345 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const log = std.log.scoped(.reloc); | |
| 4 | const macho = std.macho; | |
| 5 | const math = std.math; | |
| 6 | const mem = std.mem; | |
| 7 | const meta = std.meta; | |
| 8 | const reloc = @import("../reloc.zig"); | |
| 9 | ||
| 10 | const Allocator = mem.Allocator; | |
| 11 | const Relocation = reloc.Relocation; | |
| 12 | const Symbol = @import("../Symbol.zig"); | |
| 13 | ||
| 14 | pub const Branch = struct { | |
| 15 | base: Relocation, | |
| 16 | ||
| 17 | pub const base_type: Relocation.Type = .branch_x86_64; | |
| 18 | ||
| 19 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 20 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 21 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 22 | mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement)); | |
| 23 | } | |
| 24 | }; | |
| 25 | ||
| 26 | pub const Signed = struct { | |
| 27 | base: Relocation, | |
| 28 | addend: i32, | |
| 29 | correction: i4, | |
| 30 | ||
| 31 | pub const base_type: Relocation.Type = .signed; | |
| 32 | ||
| 33 | pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { | |
| 34 | const target_addr = target_addr: { | |
| 35 | if (signed.base.target == .section) { | |
| 36 | const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; | |
| 37 | const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); | |
| 38 | break :target_addr @intCast(i64, args.target_addr) + source_disp; | |
| 39 | } | |
| 40 | break :target_addr @intCast(i64, args.target_addr) + signed.addend; | |
| 41 | }; | |
| 42 | const displacement = try math.cast( | |
| 43 | i32, | |
| 44 | target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, | |
| 45 | ); | |
| 46 | ||
| 47 | log.debug(" | addend 0x{x}", .{signed.addend}); | |
| 48 | log.debug(" | correction 0x{x}", .{signed.correction}); | |
| 49 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 50 | ||
| 51 | mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | |
| 52 | } | |
| 53 | }; | |
| 54 | ||
| 55 | pub const GotLoad = struct { | |
| 56 | base: Relocation, | |
| 57 | ||
| 58 | pub const base_type: Relocation.Type = .got_load; | |
| 59 | ||
| 60 | pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void { | |
| 61 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 62 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 63 | mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement)); | |
| 64 | } | |
| 65 | }; | |
| 66 | ||
| 67 | pub const Got = struct { | |
| 68 | base: Relocation, | |
| 69 | addend: i32, | |
| 70 | ||
| 71 | pub const base_type: Relocation.Type = .got; | |
| 72 | ||
| 73 | pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void { | |
| 74 | const displacement = try math.cast( | |
| 75 | i32, | |
| 76 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend, | |
| 77 | ); | |
| 78 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 79 | mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement)); | |
| 80 | } | |
| 81 | }; | |
| 82 | ||
| 83 | pub const Tlv = struct { | |
| 84 | base: Relocation, | |
| 85 | op: *u8, | |
| 86 | ||
| 87 | pub const base_type: Relocation.Type = .tlv; | |
| 88 | ||
| 89 | pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void { | |
| 90 | // We need to rewrite the opcode from movq to leaq. | |
| 91 | tlv.op.* = 0x8d; | |
| 92 | log.debug(" | rewriting op to leaq", .{}); | |
| 93 | ||
| 94 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 95 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 96 | ||
| 97 | mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement)); | |
| 98 | } | |
| 99 | }; | |
| 100 | ||
| 101 | pub const Parser = struct { | |
| 102 | allocator: *Allocator, | |
| 103 | it: *reloc.RelocIterator, | |
| 104 | code: []u8, | |
| 105 | parsed: std.ArrayList(*Relocation), | |
| 106 | symbols: []*Symbol, | |
| 107 | subtractor: ?Relocation.Target = null, | |
| 108 | ||
| 109 | pub fn deinit(parser: *Parser) void { | |
| 110 | parser.parsed.deinit(); | |
| 111 | } | |
| 112 | ||
| 113 | pub fn parse(parser: *Parser) !void { | |
| 114 | while (parser.it.next()) |rel| { | |
| 115 | switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 116 | .X86_64_RELOC_BRANCH => { | |
| 117 | try parser.parseBranch(rel); | |
| 118 | }, | |
| 119 | .X86_64_RELOC_SUBTRACTOR => { | |
| 120 | try parser.parseSubtractor(rel); | |
| 121 | }, | |
| 122 | .X86_64_RELOC_UNSIGNED => { | |
| 123 | try parser.parseUnsigned(rel); | |
| 124 | }, | |
| 125 | .X86_64_RELOC_SIGNED, | |
| 126 | .X86_64_RELOC_SIGNED_1, | |
| 127 | .X86_64_RELOC_SIGNED_2, | |
| 128 | .X86_64_RELOC_SIGNED_4, | |
| 129 | => { | |
| 130 | try parser.parseSigned(rel); | |
| 131 | }, | |
| 132 | .X86_64_RELOC_GOT_LOAD => { | |
| 133 | try parser.parseGotLoad(rel); | |
| 134 | }, | |
| 135 | .X86_64_RELOC_GOT => { | |
| 136 | try parser.parseGot(rel); | |
| 137 | }, | |
| 138 | .X86_64_RELOC_TLV => { | |
| 139 | try parser.parseTlv(rel); | |
| 140 | }, | |
| 141 | } | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | |
| 146 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 147 | assert(rel_type == .X86_64_RELOC_BRANCH); | |
| 148 | assert(rel.r_pcrel == 1); | |
| 149 | assert(rel.r_length == 2); | |
| 150 | ||
| 151 | const offset = @intCast(u32, rel.r_address); | |
| 152 | const inst = parser.code[offset..][0..4]; | |
| 153 | ||
| 154 | var branch = try parser.allocator.create(Branch); | |
| 155 | errdefer parser.allocator.destroy(branch); | |
| 156 | ||
| 157 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 158 | ||
| 159 | branch.* = .{ | |
| 160 | .base = .{ | |
| 161 | .@"type" = .branch_x86_64, | |
| 162 | .code = inst, | |
| 163 | .offset = offset, | |
| 164 | .target = target, | |
| 165 | }, | |
| 166 | }; | |
| 167 | ||
| 168 | log.debug(" | emitting {}", .{branch}); | |
| 169 | try parser.parsed.append(&branch.base); | |
| 170 | } | |
| 171 | ||
| 172 | fn parseSigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 173 | assert(rel.r_pcrel == 1); | |
| 174 | assert(rel.r_length == 2); | |
| 175 | ||
| 176 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 177 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 178 | ||
| 179 | const offset = @intCast(u32, rel.r_address); | |
| 180 | const inst = parser.code[offset..][0..4]; | |
| 181 | const correction: i4 = switch (rel_type) { | |
| 182 | .X86_64_RELOC_SIGNED => 0, | |
| 183 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 184 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 185 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 186 | else => unreachable, | |
| 187 | }; | |
| 188 | const addend = mem.readIntLittle(i32, inst) + correction; | |
| 189 | ||
| 190 | var signed = try parser.allocator.create(Signed); | |
| 191 | errdefer parser.allocator.destroy(signed); | |
| 192 | ||
| 193 | signed.* = .{ | |
| 194 | .base = .{ | |
| 195 | .@"type" = .signed, | |
| 196 | .code = inst, | |
| 197 | .offset = offset, | |
| 198 | .target = target, | |
| 199 | }, | |
| 200 | .addend = addend, | |
| 201 | .correction = correction, | |
| 202 | }; | |
| 203 | ||
| 204 | log.debug(" | emitting {}", .{signed}); | |
| 205 | try parser.parsed.append(&signed.base); | |
| 206 | } | |
| 207 | ||
| 208 | fn parseGotLoad(parser: *Parser, rel: macho.relocation_info) !void { | |
| 209 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 210 | assert(rel_type == .X86_64_RELOC_GOT_LOAD); | |
| 211 | assert(rel.r_pcrel == 1); | |
| 212 | assert(rel.r_length == 2); | |
| 213 | ||
| 214 | const offset = @intCast(u32, rel.r_address); | |
| 215 | const inst = parser.code[offset..][0..4]; | |
| 216 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 217 | ||
| 218 | var got_load = try parser.allocator.create(GotLoad); | |
| 219 | errdefer parser.allocator.destroy(got_load); | |
| 220 | ||
| 221 | got_load.* = .{ | |
| 222 | .base = .{ | |
| 223 | .@"type" = .got_load, | |
| 224 | .code = inst, | |
| 225 | .offset = offset, | |
| 226 | .target = target, | |
| 227 | }, | |
| 228 | }; | |
| 229 | ||
| 230 | log.debug(" | emitting {}", .{got_load}); | |
| 231 | try parser.parsed.append(&got_load.base); | |
| 232 | } | |
| 233 | ||
| 234 | fn parseGot(parser: *Parser, rel: macho.relocation_info) !void { | |
| 235 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 236 | assert(rel_type == .X86_64_RELOC_GOT); | |
| 237 | assert(rel.r_pcrel == 1); | |
| 238 | assert(rel.r_length == 2); | |
| 239 | ||
| 240 | const offset = @intCast(u32, rel.r_address); | |
| 241 | const inst = parser.code[offset..][0..4]; | |
| 242 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 243 | const addend = mem.readIntLittle(i32, inst); | |
| 244 | ||
| 245 | var got = try parser.allocator.create(Got); | |
| 246 | errdefer parser.allocator.destroy(got); | |
| 247 | ||
| 248 | got.* = .{ | |
| 249 | .base = .{ | |
| 250 | .@"type" = .got, | |
| 251 | .code = inst, | |
| 252 | .offset = offset, | |
| 253 | .target = target, | |
| 254 | }, | |
| 255 | .addend = addend, | |
| 256 | }; | |
| 257 | ||
| 258 | log.debug(" | emitting {}", .{got}); | |
| 259 | try parser.parsed.append(&got.base); | |
| 260 | } | |
| 261 | ||
| 262 | fn parseTlv(parser: *Parser, rel: macho.relocation_info) !void { | |
| 263 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 264 | assert(rel_type == .X86_64_RELOC_TLV); | |
| 265 | assert(rel.r_pcrel == 1); | |
| 266 | assert(rel.r_length == 2); | |
| 267 | ||
| 268 | const offset = @intCast(u32, rel.r_address); | |
| 269 | const inst = parser.code[offset..][0..4]; | |
| 270 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 271 | ||
| 272 | var tlv = try parser.allocator.create(Tlv); | |
| 273 | errdefer parser.allocator.destroy(tlv); | |
| 274 | ||
| 275 | tlv.* = .{ | |
| 276 | .base = .{ | |
| 277 | .@"type" = .tlv, | |
| 278 | .code = inst, | |
| 279 | .offset = offset, | |
| 280 | .target = target, | |
| 281 | }, | |
| 282 | .op = &parser.code[offset - 2], | |
| 283 | }; | |
| 284 | ||
| 285 | log.debug(" | emitting {}", .{tlv}); | |
| 286 | try parser.parsed.append(&tlv.base); | |
| 287 | } | |
| 288 | ||
| 289 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | |
| 290 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 291 | assert(rel_type == .X86_64_RELOC_SUBTRACTOR); | |
| 292 | assert(rel.r_pcrel == 0); | |
| 293 | assert(parser.subtractor == null); | |
| 294 | ||
| 295 | parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 296 | ||
| 297 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 298 | const next = @intToEnum(macho.reloc_type_x86_64, parser.it.peek().r_type); | |
| 299 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 300 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 301 | return error.UnexpectedRelocationType; | |
| 302 | } | |
| 303 | } | |
| 304 | ||
| 305 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 306 | defer { | |
| 307 | // Reset parser's subtractor state | |
| 308 | parser.subtractor = null; | |
| 309 | } | |
| 310 | ||
| 311 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 312 | assert(rel_type == .X86_64_RELOC_UNSIGNED); | |
| 313 | assert(rel.r_pcrel == 0); | |
| 314 | ||
| 315 | var unsigned = try parser.allocator.create(reloc.Unsigned); | |
| 316 | errdefer parser.allocator.destroy(unsigned); | |
| 317 | ||
| 318 | const target = Relocation.Target.from_reloc(rel, parser.symbols); | |
| 319 | const is_64bit: bool = switch (rel.r_length) { | |
| 320 | 3 => true, | |
| 321 | 2 => false, | |
| 322 | else => unreachable, | |
| 323 | }; | |
| 324 | const offset = @intCast(u32, rel.r_address); | |
| 325 | const addend: i64 = if (is_64bit) | |
| 326 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | |
| 327 | else | |
| 328 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | |
| 329 | ||
| 330 | unsigned.* = .{ | |
| 331 | .base = .{ | |
| 332 | .@"type" = .unsigned, | |
| 333 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | |
| 334 | .offset = offset, | |
| 335 | .target = target, | |
| 336 | }, | |
| 337 | .subtractor = parser.subtractor, | |
| 338 | .is_64bit = is_64bit, | |
| 339 | .addend = addend, | |
| 340 | }; | |
| 341 | ||
| 342 | log.debug(" | emitting {}", .{unsigned}); | |
| 343 | try parser.parsed.append(&unsigned.base); | |
| 344 | } | |
| 345 | }; |
src/main.zig+12| ... | ... | @@ -1655,6 +1655,18 @@ fn buildOutputType( |
| 1655 | 1655 | } |
| 1656 | 1656 | } |
| 1657 | 1657 | |
| 1658 | if (use_lld) |opt| { | |
| 1659 | if (opt and cross_target.isDarwin()) { | |
| 1660 | fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{}); | |
| 1661 | } | |
| 1662 | } | |
| 1663 | ||
| 1664 | if (want_lto) |opt| { | |
| 1665 | if (opt and cross_target.isDarwin()) { | |
| 1666 | fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{}); | |
| 1667 | } | |
| 1668 | } | |
| 1669 | ||
| 1658 | 1670 | if (comptime std.Target.current.isDarwin()) { |
| 1659 | 1671 | // If we want to link against frameworks, we need system headers. |
| 1660 | 1672 | if (framework_dirs.items.len > 0 or frameworks.items.len > 0) |