authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-23 20:03:15+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-23 20:03:15+02:00
logf9798108f8434f277de6089502446f2544ee98b3
treeeba6945383fefa59a8858f7eec8beee3cfca7731
parente5b476209a1215a03164890d331e2013f20882a6
parent5533f77054acad376f2fce0d529569a7449e9949
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9387 from ziglang/zld-incremental-2

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
581581 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
582582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
583583 "${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"
585585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
587586 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
588587 "${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"
592588 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
593589 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
594590 "${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 {
894894
895895 // Make a decision on whether to use LLD or our own linker.
896896 const use_lld = options.use_lld orelse blk: {
897 if (options.target.isDarwin()) {
898 break :blk false;
899 }
900
897901 if (!build_options.have_llvm)
898902 break :blk false;
899903
......@@ -931,11 +935,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
931935 break :blk false;
932936 };
933937
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 };
939942
940943 const sysroot = blk: {
941944 if (options.sysroot) |sysroot| {
......@@ -952,10 +955,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
952955
953956 const lto = blk: {
954957 if (options.want_lto) |explicit| {
955 if (!use_lld)
958 if (!use_lld and !options.target.isDarwin())
956959 return error.LtoUnavailableWithoutLld;
957960 break :blk explicit;
958961 } else if (!use_lld) {
962 // TODO zig ld LTO support
963 // See https://github.com/ziglang/zig/issues/8680
959964 break :blk false;
960965 } else if (options.c_source_files.len == 0) {
961966 break :blk false;
src/codegen.zig+75-41
......@@ -2590,9 +2590,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25902590 const got_addr = blk: {
25912591 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
25922592 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);
25942598 };
2595 log.debug("got_addr = 0x{x}", .{got_addr});
25962599 switch (arch) {
25972600 .x86_64 => {
25982601 try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr });
......@@ -2609,37 +2612,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26092612 }
26102613 } else if (func_value.castTag(.extern_fn)) |func_payload| {
26112614 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: {
26212617 switch (arch) {
26222618 .x86_64 => {
26232619 // callq
26242620 try self.code.ensureCapacity(self.code.items.len + 5);
26252621 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
2626 break :blk 5;
2622 break :blk @intCast(u32, self.code.items.len) - 4;
26272623 },
26282624 .aarch64 => {
2625 const offset = @intCast(u32, self.code.items.len);
26292626 // 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;
26322629 },
26332630 else => unreachable, // unsupported architecture on MachO
26342631 }
26352632 };
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 } },
26412641 });
2642 // We mark the space and fix it up later.
26432642 } else {
26442643 return self.fail("TODO implement calling bitcasted functions", .{});
26452644 }
......@@ -4144,19 +4143,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
41444143 .memory => |addr| {
41454144 if (self.bin_file.options.pie) {
41464145 // 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);
41604148 mem.writeIntLittle(
41614149 u32,
41624150 try self.code.addManyAsArray(4),
......@@ -4169,6 +4157,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
41694157 .offset = Instruction.LoadStoreOffset.imm(0),
41704158 },
41714159 }).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 }
41724190 } else {
41734191 // The value is in memory at a hard-coded address.
41744192 // 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 {
44214439 encoder.modRm_RIPDisp32(reg.low_id());
44224440 encoder.disp32(0);
44234441
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
44274444 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 } },
44324462 });
44334463 } else {
44344464 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 {
46474677 const got_addr = blk: {
46484678 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
46494679 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;
46514685 };
46524686 return MCValue{ .memory = got_addr };
46534687 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
src/link.zig+1-1
......@@ -543,7 +543,7 @@ pub const File = struct {
543543 }
544544 }
545545
546 fn linkAsArchive(base: *File, comp: *Compilation) !void {
546 pub fn linkAsArchive(base: *File, comp: *Compilation) !void {
547547 const tracy = trace(@src());
548548 defer tracy.end();
549549
src/link/MachO.zig+3304-727
......@@ -1,38 +1,43 @@
11const MachO = @This();
22
33const std = @import("std");
4const build_options = @import("build_options");
45const builtin = @import("builtin");
5const Allocator = std.mem.Allocator;
66const assert = std.debug.assert;
77const fmt = std.fmt;
88const fs = std.fs;
99const log = std.log.scoped(.link);
1010const macho = std.macho;
11const codegen = @import("../codegen.zig");
12const aarch64 = @import("../codegen/aarch64.zig");
1311const math = std.math;
1412const mem = std.mem;
1513const meta = std.meta;
1614
15const aarch64 = @import("../codegen/aarch64.zig");
1716const bind = @import("MachO/bind.zig");
18const trace = @import("../tracy.zig").trace;
19const build_options = @import("build_options");
20const Module = @import("../Module.zig");
21const Compilation = @import("../Compilation.zig");
17const codegen = @import("../codegen.zig");
18const commands = @import("MachO/commands.zig");
2219const link = @import("../link.zig");
23const File = link.File;
24const Cache = @import("../Cache.zig");
20const llvm_backend = @import("../codegen/llvm.zig");
2521const target_util = @import("../target.zig");
26const Air = @import("../Air.zig");
27const Liveness = @import("../Liveness.zig");
22const trace = @import("../tracy.zig").trace;
2823
29const DebugSymbols = @import("MachO/DebugSymbols.zig");
30const Trie = @import("MachO/Trie.zig");
24const Air = @import("../Air.zig");
25const Allocator = mem.Allocator;
26const Archive = @import("MachO/Archive.zig");
27const Cache = @import("../Cache.zig");
3128const CodeSignature = @import("MachO/CodeSignature.zig");
32const Zld = @import("MachO/Zld.zig");
29const Compilation = @import("../Compilation.zig");
30const DebugSymbols = @import("MachO/DebugSymbols.zig");
31const Dylib = @import("MachO/Dylib.zig");
32const File = link.File;
33const Object = @import("MachO/Object.zig");
34const Liveness = @import("../Liveness.zig");
3335const LlvmObject = @import("../codegen/llvm.zig").Object;
34
35usingnamespace @import("MachO/commands.zig");
36const LoadCommand = commands.LoadCommand;
37const Module = @import("../Module.zig");
38const SegmentCommand = commands.SegmentCommand;
39pub const TextBlock = @import("MachO/TextBlock.zig");
40const Trie = @import("MachO/Trie.zig");
3641
3742pub const base_tag: File.Tag = File.Tag.macho;
3843
......@@ -48,102 +53,131 @@ d_sym: ?DebugSymbols = null,
4853/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
4954page_size: u16,
5055
51/// Mach-O header
52header: ?macho.mach_header_64 = null,
5356/// We commit 0x1000 = 4096 bytes of space to the header and
5457/// the table of load commands. This should be plenty for any
5558/// potential future extensions.
5659header_pad: u16 = 0x1000,
5760
58/// Table of all load commands
61/// The absolute address of the entry point.
62entry_addr: ?u64 = null,
63
64objects: std.ArrayListUnmanaged(*Object) = .{},
65archives: std.ArrayListUnmanaged(*Archive) = .{},
66dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
67
68next_dylib_ordinal: u16 = 1,
69
5970load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
60/// __PAGEZERO segment
71
6172pagezero_segment_cmd_index: ?u16 = null,
62/// __TEXT segment
6373text_segment_cmd_index: ?u16 = null,
64/// __DATA_CONST segment
6574data_const_segment_cmd_index: ?u16 = null,
66/// __DATA segment
6775data_segment_cmd_index: ?u16 = null,
68/// __LINKEDIT segment
6976linkedit_segment_cmd_index: ?u16 = null,
70/// Dyld info
7177dyld_info_cmd_index: ?u16 = null,
72/// Symbol table
7378symtab_cmd_index: ?u16 = null,
74/// Dynamic symbol table
7579dysymtab_cmd_index: ?u16 = null,
76/// Path to dyld linker
7780dylinker_cmd_index: ?u16 = null,
78/// Path to libSystem
79libsystem_cmd_index: ?u16 = null,
80/// Data-in-code section of __LINKEDIT segment
8181data_in_code_cmd_index: ?u16 = null,
82/// Address to entry point function
8382function_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.
8783main_cmd_index: ?u16 = null,
88/// Minimum OS version
84dylib_id_cmd_index: ?u16 = null,
8985version_min_cmd_index: ?u16 = null,
90/// Source version
9186source_version_cmd_index: ?u16 = null,
92/// UUID load command
9387uuid_cmd_index: ?u16 = null,
94/// Code signature
9588code_signature_cmd_index: ?u16 = null,
89/// Path to libSystem
90/// TODO this is obsolete, remove it.
91libsystem_cmd_index: ?u16 = null,
9692
97/// Index into __TEXT,__text section.
93// __TEXT segment sections
9894text_section_index: ?u16 = null,
99/// Index into __TEXT,__stubs section.
10095stubs_section_index: ?u16 = null,
101/// Index into __TEXT,__stub_helper section.
10296stub_helper_section_index: ?u16 = null,
103/// Index into __DATA_CONST,__got section.
97text_const_section_index: ?u16 = null,
98cstring_section_index: ?u16 = null,
99ustring_section_index: ?u16 = null,
100gcc_except_tab_section_index: ?u16 = null,
101unwind_info_section_index: ?u16 = null,
102eh_frame_section_index: ?u16 = null,
103
104objc_methlist_section_index: ?u16 = null,
105objc_methname_section_index: ?u16 = null,
106objc_methtype_section_index: ?u16 = null,
107objc_classname_section_index: ?u16 = null,
108
109// __DATA_CONST segment sections
104110got_section_index: ?u16 = null,
105/// Index into __DATA,__la_symbol_ptr section.
111mod_init_func_section_index: ?u16 = null,
112mod_term_func_section_index: ?u16 = null,
113data_const_section_index: ?u16 = null,
114
115objc_cfstring_section_index: ?u16 = null,
116objc_classlist_section_index: ?u16 = null,
117objc_imageinfo_section_index: ?u16 = null,
118
119// __DATA segment sections
120tlv_section_index: ?u16 = null,
121tlv_data_section_index: ?u16 = null,
122tlv_bss_section_index: ?u16 = null,
106123la_symbol_ptr_section_index: ?u16 = null,
107/// Index into __DATA,__data section.
108124data_section_index: ?u16 = null,
109/// The absolute address of the entry point.
110entry_addr: ?u64 = null,
125bss_section_index: ?u16 = null,
126common_section_index: ?u16 = null,
127
128objc_const_section_index: ?u16 = null,
129objc_selrefs_section_index: ?u16 = null,
130objc_classrefs_section_index: ?u16 = null,
131objc_data_section_index: ?u16 = null,
111132
112/// Table of all local symbols
113/// Internally references string table for names (which are optional).
114133locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
115/// Table of all global symbols
116134globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
117/// Table of all extern nonlazy symbols, indexed by name.
118nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
119/// Table of all extern lazy symbols, indexed by name.
120lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
135imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
136undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
137tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
138symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
121139
122140locals_free_list: std.ArrayListUnmanaged(u32) = .{},
123141globals_free_list: std.ArrayListUnmanaged(u32) = .{},
124offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
125142
126143stub_helper_stubs_start_off: ?u64 = null,
127144
128/// Table of symbol names aka the string table.
129string_table: std.ArrayListUnmanaged(u8) = .{},
130string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
145strtab: std.ArrayListUnmanaged(u8) = .{},
146strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
147
148got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{},
149got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{},
150
151got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
131152
132/// Table of GOT entries.
133offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
153stubs: std.ArrayListUnmanaged(u32) = .{},
154stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
134155
135156error_flags: File.ErrorFlags = File.ErrorFlags{},
136157
137offset_table_count_dirty: bool = false,
138header_dirty: bool = false,
158got_entries_count_dirty: bool = false,
139159load_commands_dirty: bool = false,
140160rebase_info_dirty: bool = false,
141161binding_info_dirty: bool = false,
142162lazy_binding_info_dirty: bool = false,
143163export_info_dirty: bool = false,
144string_table_dirty: bool = false,
145164
146string_table_needs_relocation: bool = false,
165strtab_dirty: bool = false,
166strtab_needs_relocation: bool = false,
167
168has_dices: bool = false,
169has_stabs: bool = false,
170
171section_ordinals: std.ArrayListUnmanaged(MatchingSection) = .{},
172section_to_ordinal: std.AutoHashMapUnmanaged(MatchingSection, u8) = .{},
173
174pending_updates: std.ArrayListUnmanaged(struct {
175 kind: enum {
176 got,
177 stub,
178 },
179 index: u32,
180}) = .{},
147181
148182/// A list of text blocks that have surplus capacity. This list can have false
149183/// positives, as functions grow and shrink over time, only sometimes being added
......@@ -165,47 +199,73 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
165199/// Pointer to the last allocated text block
166200last_text_block: ?*TextBlock = null,
167201
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.
175pie_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.
182stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
183
184pub 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.
207managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
190208
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,
209blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
195210
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.
215decls: 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.
221active_decl: ?*Module.Decl = null,
222
223const 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 }
198234};
199235
200pub const Import = struct {
201 /// MachO symbol table entry.
202 symbol: macho.nlist_64,
236pub 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 }
203243
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};
206249
207 /// Index of this import within the import list.
208 index: u32,
250const 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
263pub const GotIndirectionKey = struct {
264 where: enum {
265 local,
266 import,
267 },
268 where_index: u32,
209269};
210270
211271pub const PIEFixup = struct {
......@@ -219,19 +279,6 @@ pub const PIEFixup = struct {
219279 size: usize,
220280};
221281
222pub 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
235282/// When allocating, the ideal_capacity is calculated by
236283/// actual_capacity + (actual_capacity / ideal_factor)
237284const ideal_factor = 2;
......@@ -254,75 +301,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.
254301/// it as a possible place to put new symbols, it must have enough room for this many bytes
255302/// (plus extra for reserved capacity).
256303const minimum_text_block_size = 64;
257const min_text_capacity = padToIdeal(minimum_text_block_size);
258
259pub 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};
304pub const min_text_capacity = padToIdeal(minimum_text_block_size);
326305
327306pub const Export = struct {
328307 sym_index: ?u32 = null,
......@@ -374,6 +353,10 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
374353
375354 self.base.file = file;
376355
356 if (options.output_mode == .Lib and options.link_mode == .Static) {
357 return self;
358 }
359
377360 if (!options.strip and options.module != null) {
378361 // Create dSYM bundle.
379362 const dir = options.module.?.zig_cache_artifact_directory;
......@@ -409,12 +392,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
409392 .n_value = 0,
410393 });
411394
412 switch (options.output_mode) {
413 .Exe => {},
414 .Obj => {},
415 .Lib => return error.TODOImplementWritingLibFiles,
416 }
417
418395 try self.populateMissingMetadata();
419396 try self.writeLocalSymbol(0);
420397
......@@ -428,6 +405,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
428405
429406pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
430407 const self = try gpa.create(MachO);
408
431409 self.* = .{
432410 .base = .{
433411 .tag = .macho,
......@@ -437,11 +415,22 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
437415 },
438416 .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000,
439417 };
418
440419 return self;
441420}
442421
443422pub 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) {
445434 return self.linkWithZld(comp);
446435 } else {
447436 switch (self.base.options.effectiveOutputMode()) {
......@@ -471,9 +460,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
471460 self.load_commands_dirty = true;
472461 }
473462 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();
477466 try self.writeAllGlobalAndUndefSymbols();
478467 try self.writeIndirectSymbolTable();
479468 try self.writeStringTable();
......@@ -508,15 +497,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
508497 self.error_flags.no_entry_point_found = false;
509498 }
510499
511 assert(!self.offset_table_count_dirty);
512 assert(!self.header_dirty);
500 assert(!self.got_entries_count_dirty);
513501 assert(!self.load_commands_dirty);
514502 assert(!self.rebase_info_dirty);
515503 assert(!self.binding_info_dirty);
516504 assert(!self.lazy_binding_info_dirty);
517505 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);
520508
521509 if (target.cpu.arch == .aarch64) {
522510 switch (output_mode) {
......@@ -639,7 +627,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
639627 const is_lib = self.base.options.output_mode == .Lib;
640628 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
641629 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
642 const target = self.base.options.target;
643630 const stack_size = self.base.options.stack_size_override orelse 0;
644631 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
645632
......@@ -738,14 +725,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
738725 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
739726 }
740727 } 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
749728 // Positional arguments to the linker such as object files and static archives.
750729 var positionals = std.ArrayList([]const u8).init(arena);
751730
......@@ -888,23 +867,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
888867 rpaths.appendAssumeCapacity(key.*);
889868 }
890869
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
908870 if (self.base.options.verbose_link) {
909871 var argv = std.ArrayList([]const u8).init(arena);
910872
......@@ -918,8 +880,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
918880 if (is_dyn_lib) {
919881 try argv.append("-dylib");
920882
883 const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{
884 self.base.options.emit.?.sub_path,
885 });
921886 try argv.append("-install_name");
922 try argv.append(output.install_name.?);
887 try argv.append(install_name);
923888 }
924889
925890 if (self.base.options.sysroot) |syslibroot| {
......@@ -935,7 +900,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
935900 try argv.appendSlice(positionals.items);
936901
937902 try argv.append("-o");
938 try argv.append(output.path);
903 try argv.append(full_out_path);
939904
940905 if (native_libsystem_available) {
941906 try argv.append("-lSystem");
......@@ -953,11 +918,56 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
953918 Compilation.dump_argv(argv.items);
954919 }
955920
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,
960937 });
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();
961971 }
962972
963973 if (!self.base.options.disable_lld_caching) {
......@@ -976,162 +986,2513 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
976986 }
977987}
978988
979fn 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}
989fn 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 };
990997
991pub 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;
10151001 }
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}
10281002
1029fn 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;
10431006 }
1044 }
1045 // TODO process free list for dbg info just like we do above for vaddrs
10461007
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;
10581014 }
1015
1016 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
10591017 }
1018}
10601019
1061 if (text_block.prev) |prev| {
1062 prev.next = text_block.next;
1020fn 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 }
10631030
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;
10681034 }
1069 } else {
1070 text_block.prev = null;
1071 }
10721035
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});
10771037 }
1038}
10781039
1079 if (text_block.dbg_info_prev) |prev| {
1080 prev.dbg_info_next = text_block.dbg_info_next;
1040pub const MatchingSection = struct {
1041 seg: u16,
1042 sect: u16,
1043};
10811044
1082 // TODO the free list logic like we do for text blocks above
1083 } else {
1084 text_block.dbg_info_prev = null;
1085 }
1045pub 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 }
10861059
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 }
10931103
1094fn 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 }
11011110
1102fn 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 }
11091130
1110pub 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 }
11121154
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 }
11151194
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 }
11241207
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
1450fn 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
1605fn 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
1656fn 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
1671fn 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
1688fn 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
1695fn 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
1714fn 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
1787fn 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
1846fn 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
1995fn 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
2196fn 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
2458fn parseTextBlocks(self: *MachO) !void {
2459 for (self.objects.items) |object| {
2460 try object.parseTextBlocks(self);
2461 }
2462}
2463
2464fn 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
2773fn 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
2787fn 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
2801fn 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
2820fn 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
2883fn 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
2906fn 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
2926fn 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
3009fn 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
3081fn 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
3123fn 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
3164fn 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
3308pub 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
3373pub 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
3382fn 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
3450fn 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
3458fn 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
3466pub 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 };
11353496
11363497 self.locals.items[decl.link.macho.local_sym_index] = .{
11373498 .n_strx = 0,
......@@ -1140,11 +3501,12 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11403501 .n_desc = 0,
11413502 .n_value = 0,
11423503 };
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,
11473507 };
3508 self.got_entries.items[got_index] = got_entry;
3509 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index);
11483510}
11493511
11503512pub 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
11793541 }
11803542 }
11813543
3544 self.active_decl = decl;
3545
11823546 const res = if (debug_buffers) |dbg|
11833547 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
11843548 .dwarf = .{
......@@ -1190,92 +3554,25 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
11903554 else
11913555 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
11923556 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 },
11943566 .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);
11993567 decl.analysis = .codegen_failure;
12003568 try module.failed_decls.put(module.gpa, decl, em);
12013569 return;
12023570 },
12033571 }
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);
12713572
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);
12773574
1278 try self.writeCode(symbol, code_buffer.items);
3575 try self.writeCode(symbol, decl.link.macho.code.items);
12793576
12803577 if (debug_buffers) |db| {
12813578 try self.d_sym.?.commitDeclDebugInfo(
......@@ -1327,6 +3624,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
13273624 }
13283625 }
13293626
3627 self.active_decl = decl;
3628
13303629 const res = if (debug_buffers) |dbg|
13313630 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
13323631 .ty = decl.ty,
......@@ -1344,18 +3643,27 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
13443643 .val = decl.val,
13453644 }, &code_buffer, .none);
13463645
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 }
13553665 };
13563666 const symbol = try self.placeDecl(decl, code.len);
1357 assert(self.pie_fixups.items.len == 0);
1358 assert(self.stub_fixups.items.len == 0);
13593667
13603668 try self.writeCode(symbol, code);
13613669
......@@ -1379,13 +3687,12 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
13793687 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
13803688
13813689 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);
13893696 }
13903697
13913698 symbol.n_value = vaddr;
......@@ -1397,7 +3704,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
13973704 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
13983705 defer self.base.allocator.free(new_name);
13993706
1400 symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
3707 symbol.n_strx = try self.makeString(new_name);
14013708 symbol.n_type = macho.N_SECT;
14023709 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
14033710 symbol.n_desc = 0;
......@@ -1423,16 +3730,35 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
14233730 .n_desc = 0,
14243731 .n_value = addr,
14253732 };
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);
14313738
14323739 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
14333740 if (self.d_sym) |*ds|
14343741 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 }
14363762 }
14373763
14383764 return symbol;
......@@ -1443,6 +3769,7 @@ fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void {
14433769 const text_section = text_segment.sections.items[self.text_section_index.?];
14443770 const section_offset = symbol.n_value - text_section.addr;
14453771 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 });
14463773 try self.base.file.?.pwriteAll(code, file_offset);
14473774}
14483775
......@@ -1518,7 +3845,7 @@ pub fn updateDeclExports(
15183845 if (exp.link.macho.sym_index) |i| {
15193846 const sym = &self.globals.items[i];
15203847 sym.* = .{
1521 .n_strx = try self.updateString(sym.n_strx, exp_name),
3848 .n_strx = sym.n_strx,
15223849 .n_type = n_type,
15233850 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
15243851 .n_desc = n_desc,
......@@ -1529,7 +3856,7 @@ pub fn updateDeclExports(
15293856 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
15303857 _ = self.globals.addOneAssumeCapacity();
15313858 self.export_info_dirty = true;
1532 break :blk self.globals.items.len - 1;
3859 break :blk @intCast(u32, self.globals.items.len - 1);
15333860 };
15343861 self.globals.items[i] = .{
15353862 .n_strx = name_str_index,
......@@ -1538,6 +3865,12 @@ pub fn updateDeclExports(
15383865 .n_desc = n_desc,
15393866 .n_value = decl_sym.n_value,
15403867 };
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 };
15413874
15423875 exp.link.macho.sym_index = @intCast(u32, i);
15433876 }
......@@ -1551,14 +3884,22 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
15513884}
15523885
15533886pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3887 log.debug("freeDecl {*}", .{decl});
3888 _ = self.decls.swapRemove(decl);
15543889 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
15553890 self.freeTextBlock(&decl.link.macho);
15563891 if (decl.link.macho.local_sym_index != 0) {
15573892 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 {};
15593893
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 {};
15613901
3902 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
15623903 decl.link.macho.local_sym_index = 0;
15633904 }
15643905 if (self.d_sym) |*ds| {
......@@ -1598,54 +3939,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15983939 .Lib => return error.TODOImplementWritingLibFiles,
15993940 }
16003941
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 }
16493942 if (self.pagezero_segment_cmd_index == null) {
16503943 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
16513944 try self.load_commands.append(self.base.allocator, .{
......@@ -1653,7 +3946,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16533946 .vmsize = 0x100000000, // size always set to 4GB
16543947 }),
16553948 });
1656 self.header_dirty = true;
16573949 self.load_commands_dirty = true;
16583950 }
16593951 if (self.text_segment_cmd_index == null) {
......@@ -1662,8 +3954,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16623954 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
16633955
16643956 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;
16673959 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
16683960
16693961 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 {
16773969 .initprot = initprot,
16783970 }),
16793971 });
1680 self.header_dirty = true;
16813972 self.load_commands_dirty = true;
16823973 }
16833974 if (self.text_section_index == null) {
......@@ -1702,7 +3993,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
17023993 .@"align" = alignment,
17033994 .flags = flags,
17043995 });
1705 self.header_dirty = true;
17063996 self.load_commands_dirty = true;
17073997 }
17083998 if (self.stubs_section_index == null) {
......@@ -1734,7 +4024,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
17344024 .flags = flags,
17354025 .reserved2 = stub_size,
17364026 });
1737 self.header_dirty = true;
17384027 self.load_commands_dirty = true;
17394028 }
17404029 if (self.stub_helper_section_index == null) {
......@@ -1760,7 +4049,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
17604049 .@"align" = alignment,
17614050 .flags = flags,
17624051 });
1763 self.header_dirty = true;
17644052 self.load_commands_dirty = true;
17654053 }
17664054 if (self.data_const_segment_cmd_index == null) {
......@@ -1784,7 +4072,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
17844072 .initprot = initprot,
17854073 }),
17864074 });
1787 self.header_dirty = true;
17884075 self.load_commands_dirty = true;
17894076 }
17904077 if (self.got_section_index == null) {
......@@ -1805,7 +4092,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18054092 .@"align" = 3, // 2^3 = @sizeOf(u64)
18064093 .flags = flags,
18074094 });
1808 self.header_dirty = true;
18094095 self.load_commands_dirty = true;
18104096 }
18114097 if (self.data_segment_cmd_index == null) {
......@@ -1829,7 +4115,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18294115 .initprot = initprot,
18304116 }),
18314117 });
1832 self.header_dirty = true;
18334118 self.load_commands_dirty = true;
18344119 }
18354120 if (self.la_symbol_ptr_section_index == null) {
......@@ -1850,7 +4135,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18504135 .@"align" = 3, // 2^3 = @sizeOf(u64)
18514136 .flags = flags,
18524137 });
1853 self.header_dirty = true;
18544138 self.load_commands_dirty = true;
18554139 }
18564140 if (self.data_section_index == null) {
......@@ -1869,7 +4153,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18694153 .offset = @intCast(u32, off),
18704154 .@"align" = 3, // 2^3 = @sizeOf(u64)
18714155 });
1872 self.header_dirty = true;
18734156 self.load_commands_dirty = true;
18744157 }
18754158 if (self.linkedit_segment_cmd_index == null) {
......@@ -1889,7 +4172,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18894172 .initprot = initprot,
18904173 }),
18914174 });
1892 self.header_dirty = true;
18934175 self.load_commands_dirty = true;
18944176 }
18954177 if (self.dyld_info_cmd_index == null) {
......@@ -1936,7 +4218,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
19364218 dyld.export_off = @intCast(u32, export_off);
19374219 dyld.export_size = expected_size;
19384220
1939 self.header_dirty = true;
19404221 self.load_commands_dirty = true;
19414222 }
19424223 if (self.symtab_cmd_index == null) {
......@@ -1961,16 +4242,15 @@ pub fn populateMissingMetadata(self: *MachO) !void {
19614242 symtab.symoff = @intCast(u32, symtab_off);
19624243 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
19634244
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;
19664247 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off);
19674248 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
19684249 symtab.stroff = @intCast(u32, strtab_off);
19694250 symtab.strsize = @intCast(u32, strtab_size);
19704251
1971 self.header_dirty = true;
19724252 self.load_commands_dirty = true;
1973 self.string_table_dirty = true;
4253 self.strtab_dirty = true;
19744254 }
19754255 if (self.dysymtab_cmd_index == null) {
19764256 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -2005,7 +4285,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20054285 .nlocrel = 0,
20064286 },
20074287 });
2008 self.header_dirty = true;
20094288 self.load_commands_dirty = true;
20104289 }
20114290 if (self.dylinker_cmd_index == null) {
......@@ -2015,7 +4294,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20154294 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
20164295 @sizeOf(u64),
20174296 ));
2018 var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{
4297 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
20194298 .cmd = macho.LC_LOAD_DYLINKER,
20204299 .cmdsize = cmdsize,
20214300 .name = @sizeOf(macho.dylinker_command),
......@@ -2024,18 +4303,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20244303 mem.set(u8, dylinker_cmd.data, 0);
20254304 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
20264305 try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
2027 self.header_dirty = true;
20284306 self.load_commands_dirty = true;
20294307 }
20304308 if (self.libsystem_cmd_index == null) {
20314309 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
20324310
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);
20344312 errdefer dylib_cmd.deinit(self.base.allocator);
20354313
20364314 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
20374315
2038 self.header_dirty = true;
20394316 self.load_commands_dirty = true;
20404317 }
20414318 if (self.main_cmd_index == null) {
......@@ -2048,7 +4325,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20484325 .stacksize = 0,
20494326 },
20504327 });
2051 self.header_dirty = true;
20524328 self.load_commands_dirty = true;
20534329 }
20544330 if (self.version_min_cmd_index == null) {
......@@ -2070,7 +4346,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20704346 .sdk = version,
20714347 },
20724348 });
2073 self.header_dirty = true;
20744349 self.load_commands_dirty = true;
20754350 }
20764351 if (self.source_version_cmd_index == null) {
......@@ -2082,7 +4357,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20824357 .version = 0x0,
20834358 },
20844359 });
2085 self.header_dirty = true;
20864360 self.load_commands_dirty = true;
20874361 }
20884362 if (self.uuid_cmd_index == null) {
......@@ -2094,7 +4368,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20944368 };
20954369 std.crypto.random.bytes(&uuid_cmd.uuid);
20964370 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
2097 self.header_dirty = true;
20984371 self.load_commands_dirty = true;
20994372 }
21004373 if (self.code_signature_cmd_index == null) {
......@@ -2107,31 +4380,32 @@ pub fn populateMissingMetadata(self: *MachO) !void {
21074380 .datasize = 0,
21084381 },
21094382 });
2110 self.header_dirty = true;
21114383 self.load_commands_dirty = true;
21124384 }
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,
21274396 });
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,
21334400 });
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);
21354409 self.binding_info_dirty = true;
21364410 }
21374411 if (self.stub_helper_stubs_start_off == null) {
......@@ -2172,7 +4446,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21724446 // should be deleted because the block that it points to has grown to take up
21734447 // more of the extra capacity.
21744448 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);
21764451 } else {
21774452 i += 1;
21784453 }
......@@ -2244,64 +4519,45 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
22444519 return vaddr;
22454520}
22464521
2247fn 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);
4522pub 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);
22554525
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 }
22734532
2274fn 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 });
22784547
2279fn 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);
22864551
2287pub 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,
23024558 });
2303 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
2304 return index;
4559
4560 return import_sym_index;
23054561}
23064562
23074563const NextSegmentAddressAndOffset = struct {
......@@ -2455,29 +4711,26 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
24554711 return st;
24564712}
24574713
2458fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
4714fn writeGotEntry(self: *MachO, index: usize) !void {
24594715 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
24604716 const sect = &seg.sections.items[self.got_section_index.?];
24614717 const off = sect.offset + @sizeOf(u64) * index;
24624718
2463 if (self.offset_table_count_dirty) {
4719 if (self.got_entries_count_dirty) {
24644720 // TODO relocate.
2465 self.offset_table_count_dirty = false;
4721 self.got_entries_count_dirty = false;
24664722 }
24674723
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],
24784728 };
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 });
24814734 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
24824735}
24834736
......@@ -2755,7 +5008,7 @@ fn relocateSymbolTable(self: *MachO) !void {
27555008 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
27565009 const nlocals = self.locals.items.len;
27575010 const nglobals = self.globals.items.len;
2758 const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count();
5011 const nundefs = self.imports.items.len;
27595012 const nsyms = nlocals + nglobals + nundefs;
27605013
27615014 if (symtab.nsyms < nsyms) {
......@@ -2775,7 +5028,7 @@ fn relocateSymbolTable(self: *MachO) !void {
27755028 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);
27765029 if (amt != existing_size) return error.InputOutput;
27775030 symtab.symoff = @intCast(u32, new_symoff);
2778 self.string_table_needs_relocation = true;
5031 self.strtab_needs_relocation = true;
27795032 }
27805033 symtab.nsyms = @intCast(u32, nsyms);
27815034 self.load_commands_dirty = true;
......@@ -2800,17 +5053,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
28005053 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
28015054 const nlocals = self.locals.items.len;
28025055 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;
28145057
28155058 const locals_off = symtab.symoff;
28165059 const locals_size = nlocals * @sizeOf(macho.nlist_64);
......@@ -2823,7 +5066,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
28235066 const undefs_off = globals_off + globals_size;
28245067 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
28255068 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);
28275070
28285071 // Update dynamic symbol table.
28295072 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
......@@ -2849,10 +5092,10 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
28495092 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
28505093 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
28515094
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);
28545097 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;
28565099 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
28575100
28585101 if (needed_size > allocated_size) {
......@@ -2871,41 +5114,85 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
28715114 var writer = stream.writer();
28725115
28735116 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);
28805119 }
28815120
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);
28885126 },
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);
28925129 },
28935130 }
28945131 }
28955132
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);
29035136 }
29045137
29055138 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
29065139 self.load_commands_dirty = true;
29075140}
29085141
5142fn 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
29095196fn writeCodeSignaturePadding(self: *MachO) !void {
29105197 // TODO figure out how not to rewrite padding every single time.
29115198 const tracy = trace(@src());
......@@ -2961,7 +5248,7 @@ fn writeCodeSignature(self: *MachO) !void {
29615248 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
29625249}
29635250
2964fn writeExportTrie(self: *MachO) !void {
5251fn writeExportInfo(self: *MachO) !void {
29655252 if (!self.export_info_dirty) return;
29665253 if (self.globals.items.len == 0) return;
29675254
......@@ -2972,13 +5259,18 @@ fn writeExportTrie(self: *MachO) !void {
29725259 defer trie.deinit();
29735260
29745261 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
29795271 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,
29825274 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
29835275 });
29845276 }
......@@ -3016,31 +5308,60 @@ fn writeRebaseInfoTable(self: *MachO) !void {
30165308 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
30175309 defer pointers.deinit();
30185310
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
30195339 if (self.got_section_index) |idx| {
30205340 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
30215341 const sect = seg.sections.items[idx];
30225342 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;
30245347
3025 for (self.offset_table.items) |entry| {
3026 if (entry.kind == .Extern) continue;
30275348 try pointers.append(.{
3028 .offset = base_offset + entry.index * @sizeOf(u64),
5349 .offset = base_offset + i * @sizeOf(u64),
30295350 .segment_id = segment_id,
30305351 });
30315352 }
30325353 }
30335354
30345355 if (self.la_symbol_ptr_section_index) |idx| {
3035 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.count());
30365356 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
30375357 const sect = seg.sections.items[idx];
30385358 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.?);
30405360
3041 for (self.lazy_imports.values()) |*value| {
5361 try pointers.ensureUnusedCapacity(self.stubs.items.len);
5362 for (self.stubs.items) |_, i| {
30425363 pointers.appendAssumeCapacity(.{
3043 .offset = base_offset + value.index * @sizeOf(u64),
5364 .offset = base_offset + i * @sizeOf(u64),
30445365 .segment_id = segment_id,
30455366 });
30465367 }
......@@ -3073,7 +5394,7 @@ fn writeRebaseInfoTable(self: *MachO) !void {
30735394 self.rebase_info_dirty = false;
30745395}
30755396
3076fn writeBindingInfoTable(self: *MachO) !void {
5397fn writeBindInfoTable(self: *MachO) !void {
30775398 if (!self.binding_info_dirty) return;
30785399
30795400 const tracy = trace(@src());
......@@ -3088,19 +5409,50 @@ fn writeBindingInfoTable(self: *MachO) !void {
30885409 const base_offset = sect.addr - seg.inner.vmaddr;
30895410 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
30905411
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];
30955416 try pointers.append(.{
3096 .offset = base_offset + entry.index * @sizeOf(u64),
5417 .offset = base_offset + i * @sizeOf(u64),
30975418 .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),
31005421 });
31015422 }
31025423 }
31035424
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
31045456 const size = try bind.bindInfoSize(pointers.items);
31055457 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
31065458 defer self.base.allocator.free(buffer);
......@@ -3126,7 +5478,7 @@ fn writeBindingInfoTable(self: *MachO) !void {
31265478 self.binding_info_dirty = false;
31275479}
31285480
3129fn writeLazyBindingInfoTable(self: *MachO) !void {
5481fn writeLazyBindInfoTable(self: *MachO) !void {
31305482 if (!self.lazy_binding_info_dirty) return;
31315483
31325484 const tracy = trace(@src());
......@@ -3136,21 +5488,20 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
31365488 defer pointers.deinit();
31375489
31385490 if (self.la_symbol_ptr_section_index) |idx| {
3139 try pointers.ensureCapacity(self.lazy_imports.count());
31405491 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
31415492 const sect = seg.sections.items[idx];
31425493 const base_offset = sect.addr - seg.inner.vmaddr;
31435494 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
31445495
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];
31495500 pointers.appendAssumeCapacity(.{
3150 .offset = base_offset + values[i].index * @sizeOf(u64),
5501 .offset = base_offset + i * @sizeOf(u64),
31515502 .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),
31545505 });
31555506 }
31565507 }
......@@ -3182,7 +5533,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
31825533}
31835534
31845535fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
3185 if (self.lazy_imports.count() == 0) return;
5536 if (self.stubs.items.len == 0) return;
31865537
31875538 var stream = std.io.fixedBufferStream(buffer);
31885539 var reader = stream.reader();
......@@ -3227,7 +5578,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
32275578 else => {},
32285579 }
32295580 }
3230 assert(self.lazy_imports.count() <= offsets.items.len);
5581 assert(self.stubs.items.len <= offsets.items.len);
32315582
32325583 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
32335584 .x86_64 => 10,
......@@ -3240,34 +5591,51 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
32405591 else => unreachable,
32415592 };
32425593 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]);
32465597 try self.base.file.?.pwriteAll(&buf, placeholder_off);
32475598 }
32485599}
32495600
32505601fn writeStringTable(self: *MachO) !void {
3251 if (!self.string_table_dirty) return;
5602 if (!self.strtab_dirty) return;
32525603
32535604 const tracy = trace(@src());
32545605 defer tracy.end();
32555606
32565607 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
32575608 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));
32595610
3260 if (needed_size > allocated_size or self.string_table_needs_relocation) {
5611 if (needed_size > allocated_size or self.strtab_needs_relocation) {
32615612 symtab.strsize = 0;
32625613 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));
3263 self.string_table_needs_relocation = false;
5614 self.strtab_needs_relocation = false;
32645615 }
32655616 symtab.strsize = @intCast(u32, needed_size);
32665617 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
32675618
3268 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
5619 try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
32695620 self.load_commands_dirty = true;
3270 self.string_table_dirty = false;
5621 self.strtab_dirty = false;
5622}
5623
5624fn 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 }
32715639}
32725640
32735641fn updateLinkeditSegmentSizes(self: *MachO) !void {
......@@ -3334,24 +5702,57 @@ fn writeLoadCommands(self: *MachO) !void {
33345702 }
33355703
33365704 const off = @sizeOf(macho.mach_header_64);
5705
33375706 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
5707
33385708 try self.base.file.?.pwriteAll(buffer, off);
33395709 self.load_commands_dirty = false;
33405710}
33415711
33425712/// Writes Mach-O file header.
33435713fn 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;
33455748
3346 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
3347 var sizeofcmds: u32 = 0;
33485749 for (self.load_commands.items) |cmd| {
3349 sizeofcmds += cmd.cmdsize();
5750 header.sizeofcmds += cmd.cmdsize();
33505751 }
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);
33555756}
33565757
33575758pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
......@@ -3359,3 +5760,179 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
33595760 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
33605761 std.math.maxInt(@TypeOf(actual_size));
33615762}
5763
5764pub 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
5785pub 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
5790pub fn symbolIsStab(sym: macho.nlist_64) bool {
5791 return (macho.N_STAB & sym.n_type) != 0;
5792}
5793
5794pub fn symbolIsPext(sym: macho.nlist_64) bool {
5795 return (macho.N_PEXT & sym.n_type) != 0;
5796}
5797
5798pub fn symbolIsExt(sym: macho.nlist_64) bool {
5799 return (macho.N_EXT & sym.n_type) != 0;
5800}
5801
5802pub fn symbolIsSect(sym: macho.nlist_64) bool {
5803 const type_ = macho.N_TYPE & sym.n_type;
5804 return type_ == macho.N_SECT;
5805}
5806
5807pub fn symbolIsUndf(sym: macho.nlist_64) bool {
5808 const type_ = macho.N_TYPE & sym.n_type;
5809 return type_ == macho.N_UNDF;
5810}
5811
5812pub fn symbolIsIndr(sym: macho.nlist_64) bool {
5813 const type_ = macho.N_TYPE & sym.n_type;
5814 return type_ == macho.N_INDR;
5815}
5816
5817pub fn symbolIsAbs(sym: macho.nlist_64) bool {
5818 const type_ = macho.N_TYPE & sym.n_type;
5819 return type_ == macho.N_ABS;
5820}
5821
5822pub fn symbolIsWeakDef(sym: macho.nlist_64) bool {
5823 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
5824}
5825
5826pub fn symbolIsWeakRef(sym: macho.nlist_64) bool {
5827 return (sym.n_desc & macho.N_WEAK_REF) != 0;
5828}
5829
5830pub fn symbolIsTentative(sym: macho.nlist_64) bool {
5831 if (!symbolIsUndf(sym)) return false;
5832 return sym.n_value != 0;
5833}
5834
5835pub 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
5839pub 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
5845fn packDylibOrdinal(ordinal: u16) u16 {
5846 return ordinal * macho.N_SYMBOL_RESOLVER;
5847}
5848
5849fn unpackDylibOrdinal(pack: u16) u16 {
5850 return @divExact(pack, macho.N_SYMBOL_RESOLVER);
5851}
5852
5853pub 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
5866fn 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
5873fn 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 {
8181 }
8282 }
8383
84 fn date(self: ar_hdr) !u64 {
85 const value = getValue(&self.ar_date);
86 return std.fmt.parseInt(u64, value, 10);
87 }
88
8489 fn size(self: ar_hdr) !u32 {
8590 const value = getValue(&self.ar_size);
8691 return std.fmt.parseInt(u32, value, 10);
......@@ -264,6 +269,7 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
264269 .file = try fs.cwd().openFile(self.name.?, .{}),
265270 .name = name,
266271 .file_offset = @intCast(u32, try reader.context.getPos()),
272 .mtime = try self.header.?.date(),
267273 };
268274 try object.parse();
269275 try reader.context.seekTo(0);
src/link/MachO/DebugSymbols.zig+42-60
......@@ -3,7 +3,7 @@ const DebugSymbols = @This();
33const std = @import("std");
44const assert = std.debug.assert;
55const fs = std.fs;
6const log = std.log.scoped(.link);
6const log = std.log.scoped(.dsym);
77const macho = std.macho;
88const mem = std.mem;
99const DW = std.dwarf;
......@@ -27,9 +27,6 @@ const page_size: u16 = 0x1000;
2727base: *MachO,
2828file: fs.File,
2929
30/// Mach header
31header: ?macho.mach_header_64 = null,
32
3330/// Table of all load commands
3431load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3532/// __PAGEZERO segment
......@@ -78,9 +75,8 @@ dbg_info_decl_last: ?*TextBlock = null,
7875/// Table of debug symbol names aka the debug string table.
7976debug_string_table: std.ArrayListUnmanaged(u8) = .{},
8077
81header_dirty: bool = false,
8278load_commands_dirty: bool = false,
83string_table_dirty: bool = false,
79strtab_dirty: bool = false,
8480debug_string_table_dirty: bool = false,
8581debug_abbrev_section_dirty: bool = false,
8682debug_aranges_section_dirty: bool = false,
......@@ -106,26 +102,10 @@ const min_nop_size = 2;
106102/// You must call this function *after* `MachO.populateMissingMetadata()`
107103/// has been called to get a viable debug symbols output.
108104pub 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 }
124105 if (self.uuid_cmd_index == null) {
125106 const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
126107 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
127108 try self.load_commands.append(allocator, base_cmd);
128 self.header_dirty = true;
129109 self.load_commands_dirty = true;
130110 }
131111 if (self.symtab_cmd_index == null) {
......@@ -134,11 +114,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
134114 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
135115 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
136116
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 });
138118
139119 const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1);
140120
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 });
142122
143123 try self.load_commands.append(allocator, .{
144124 .Symtab = .{
......@@ -150,16 +130,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
150130 .strsize = base_cmd.strsize,
151131 },
152132 });
153 self.header_dirty = true;
154133 self.load_commands_dirty = true;
155 self.string_table_dirty = true;
134 self.strtab_dirty = true;
156135 }
157136 if (self.pagezero_segment_cmd_index == null) {
158137 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
159138 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
160139 const cmd = try self.copySegmentCommand(allocator, base_cmd);
161140 try self.load_commands.append(allocator, .{ .Segment = cmd });
162 self.header_dirty = true;
163141 self.load_commands_dirty = true;
164142 }
165143 if (self.text_segment_cmd_index == null) {
......@@ -167,7 +145,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
167145 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
168146 const cmd = try self.copySegmentCommand(allocator, base_cmd);
169147 try self.load_commands.append(allocator, .{ .Segment = cmd });
170 self.header_dirty = true;
171148 self.load_commands_dirty = true;
172149 }
173150 if (self.data_const_segment_cmd_index == null) outer: {
......@@ -176,7 +153,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
176153 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].Segment;
177154 const cmd = try self.copySegmentCommand(allocator, base_cmd);
178155 try self.load_commands.append(allocator, .{ .Segment = cmd });
179 self.header_dirty = true;
180156 self.load_commands_dirty = true;
181157 }
182158 if (self.data_segment_cmd_index == null) outer: {
......@@ -185,7 +161,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
185161 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
186162 const cmd = try self.copySegmentCommand(allocator, base_cmd);
187163 try self.load_commands.append(allocator, .{ .Segment = cmd });
188 self.header_dirty = true;
189164 self.load_commands_dirty = true;
190165 }
191166 if (self.linkedit_segment_cmd_index == null) {
......@@ -196,7 +171,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
196171 cmd.inner.fileoff = self.linkedit_off;
197172 cmd.inner.filesize = self.linkedit_size;
198173 try self.load_commands.append(allocator, .{ .Segment = cmd });
199 self.header_dirty = true;
200174 self.load_commands_dirty = true;
201175 }
202176 if (self.dwarf_segment_cmd_index == null) {
......@@ -208,7 +182,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
208182 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
209183 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
210184
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 });
212186
213187 try self.load_commands.append(allocator, .{
214188 .Segment = SegmentCommand.empty("__DWARF", .{
......@@ -218,7 +192,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
218192 .filesize = needed_size,
219193 }),
220194 });
221 self.header_dirty = true;
222195 self.load_commands_dirty = true;
223196 }
224197 if (self.debug_str_section_index == null) {
......@@ -232,7 +205,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
232205 .offset = @intCast(u32, dwarf_segment.inner.fileoff),
233206 .@"align" = 1,
234207 });
235 self.header_dirty = true;
236208 self.load_commands_dirty = true;
237209 self.debug_string_table_dirty = true;
238210 }
......@@ -244,7 +216,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
244216 const p_align = 1;
245217 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
246218
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 });
248220
249221 try dwarf_segment.addSection(allocator, "__debug_info", .{
250222 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
......@@ -252,7 +224,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
252224 .offset = @intCast(u32, off),
253225 .@"align" = p_align,
254226 });
255 self.header_dirty = true;
256227 self.load_commands_dirty = true;
257228 self.debug_info_header_dirty = true;
258229 }
......@@ -264,7 +235,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
264235 const p_align = 1;
265236 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
266237
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 });
268239
269240 try dwarf_segment.addSection(allocator, "__debug_abbrev", .{
270241 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
......@@ -272,7 +243,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
272243 .offset = @intCast(u32, off),
273244 .@"align" = p_align,
274245 });
275 self.header_dirty = true;
276246 self.load_commands_dirty = true;
277247 self.debug_abbrev_section_dirty = true;
278248 }
......@@ -284,7 +254,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
284254 const p_align = 16;
285255 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
286256
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 });
288258
289259 try dwarf_segment.addSection(allocator, "__debug_aranges", .{
290260 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
......@@ -292,7 +262,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
292262 .offset = @intCast(u32, off),
293263 .@"align" = p_align,
294264 });
295 self.header_dirty = true;
296265 self.load_commands_dirty = true;
297266 self.debug_aranges_section_dirty = true;
298267 }
......@@ -304,7 +273,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
304273 const p_align = 1;
305274 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
306275
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 });
308277
309278 try dwarf_segment.addSection(allocator, "__debug_line", .{
310279 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
......@@ -312,7 +281,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
312281 .offset = @intCast(u32, off),
313282 .@"align" = p_align,
314283 });
315 self.header_dirty = true;
316284 self.load_commands_dirty = true;
317285 self.debug_line_header_dirty = true;
318286 }
......@@ -624,9 +592,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
624592 try self.writeLoadCommands(allocator);
625593 try self.writeHeader();
626594
627 assert(!self.header_dirty);
628595 assert(!self.load_commands_dirty);
629 assert(!self.string_table_dirty);
596 assert(!self.strtab_dirty);
630597 assert(!self.debug_abbrev_section_dirty);
631598 assert(!self.debug_aranges_section_dirty);
632599 assert(!self.debug_string_table_dirty);
......@@ -716,23 +683,38 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
716683 }
717684
718685 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 });
720687 try self.file.pwriteAll(buffer, off);
721688 self.load_commands_dirty = false;
722689}
723690
724691fn 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;
726710
727 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
728 var sizeofcmds: u32 = 0;
729711 for (self.load_commands.items) |cmd| {
730 sizeofcmds += cmd.cmdsize();
712 header.sizeofcmds += cmd.cmdsize();
731713 }
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);
736718}
737719
738720fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
......@@ -798,7 +780,7 @@ fn relocateSymbolTable(self: *DebugSymbols) !void {
798780 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
799781
800782 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}", .{
802784 symtab.symoff,
803785 symtab.symoff + existing_size,
804786 new_symoff,
......@@ -820,30 +802,30 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
820802 try self.relocateSymbolTable();
821803 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
822804 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 });
824806 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
825807}
826808
827809fn writeStringTable(self: *DebugSymbols) !void {
828 if (!self.string_table_dirty) return;
810 if (!self.strtab_dirty) return;
829811
830812 const tracy = trace(@src());
831813 defer tracy.end();
832814
833815 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
834816 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));
836818
837819 if (needed_size > allocated_size) {
838820 symtab.strsize = 0;
839821 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
840822 }
841823 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 });
843825
844 try self.file.pwriteAll(self.base.string_table.items, symtab.stroff);
826 try self.file.pwriteAll(self.base.strtab.items, symtab.stroff);
845827 self.load_commands_dirty = true;
846 self.string_table_dirty = false;
828 self.strtab_dirty = false;
847829}
848830
849831pub 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");
1212
1313const Allocator = mem.Allocator;
1414const Arch = std.Target.Cpu.Arch;
15const Symbol = @import("Symbol.zig");
1615const LibStub = @import("../tapi.zig").LibStub;
16const MachO = @import("../MachO.zig");
1717
1818usingnamespace @import("commands.zig");
1919
2020allocator: *Allocator,
21
2221arch: ?Arch = null,
2322header: ?macho.mach_header_64 = null,
2423file: ?fs.File = null,
......@@ -146,7 +145,12 @@ pub const CreateOpts = struct {
146145 id: ?Id = null,
147146};
148147
149pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib {
148pub fn createAndParseFromPath(
149 allocator: *Allocator,
150 arch: Arch,
151 path: []const u8,
152 opts: CreateOpts,
153) Error!?[]*Dylib {
150154 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
151155 error.FileNotFound => return null,
152156 else => |e| return e,
......@@ -320,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void {
320324 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
321325
322326 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));
324328
325329 if (!add_to_symtab) continue;
326330
......@@ -502,21 +506,3 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
502506 }
503507 }
504508}
505
506pub 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;
77const io = std.io;
88const log = std.log.scoped(.object);
99const macho = std.macho;
10const math = std.math;
1011const mem = std.mem;
11const reloc = @import("reloc.zig");
12const sort = std.sort;
1213
1314const Allocator = mem.Allocator;
1415const Arch = std.Target.Cpu.Arch;
15const Relocation = reloc.Relocation;
16const Symbol = @import("Symbol.zig");
17const parseName = @import("Zld.zig").parseName;
16const MachO = @import("../MachO.zig");
17const TextBlock = @import("TextBlock.zig");
1818
1919usingnamespace @import("commands.zig");
2020
......@@ -26,7 +26,6 @@ file_offset: ?u32 = null,
2626name: ?[]const u8 = null,
2727
2828load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
29sections: std.ArrayListUnmanaged(Section) = .{},
3029
3130segment_cmd_index: ?u16 = null,
3231symtab_cmd_index: ?u16 = null,
......@@ -44,71 +43,23 @@ dwarf_debug_str_index: ?u16 = null,
4443dwarf_debug_line_index: ?u16 = null,
4544dwarf_debug_ranges_index: ?u16 = null,
4645
47symbols: std.ArrayListUnmanaged(*Symbol) = .{},
48initializers: std.ArrayListUnmanaged(*Symbol) = .{},
46symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
47strtab: std.ArrayListUnmanaged(u8) = .{},
4948data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
5049
51tu_path: ?[]const u8 = null,
52tu_mtime: ?u64 = null,
50// Debug info
51debug_info: ?DebugInfo = null,
52tu_name: ?[]const u8 = null,
53tu_comp_dir: ?[]const u8 = null,
54mtime: ?u64 = null,
5355
54pub 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 }
56text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
57sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
9458
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.
61symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
62reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
11263
11364const DebugInfo = struct {
11465 inner: dwarf.DwarfInfo,
......@@ -211,27 +162,28 @@ pub fn deinit(self: *Object) void {
211162 lc.deinit(self.allocator);
212163 }
213164 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);
217175 }
218 self.sections.deinit(self.allocator);
219176
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);
223179 }
224 self.symbols.deinit(self.allocator);
225180
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| {
230182 self.allocator.free(n);
231183 }
232184
233 if (self.tu_path) |tu_path| {
234 self.allocator.free(tu_path);
185 if (self.name) |n| {
186 self.allocator.free(n);
235187 }
236188}
237189
......@@ -270,10 +222,8 @@ pub fn parse(self: *Object) !void {
270222 self.header = header;
271223
272224 try self.readLoadCommands(reader);
273 try self.parseSymbols();
274 try self.parseSections();
225 try self.parseSymtab();
275226 try self.parseDataInCode();
276 try self.parseInitializers();
277227 try self.parseDebugInfo();
278228}
279229
......@@ -290,8 +240,8 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
290240 var seg = cmd.Segment;
291241 for (seg.sections.items) |*sect, j| {
292242 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.*);
295245 if (mem.eql(u8, segname, "__DWARF")) {
296246 if (mem.eql(u8, sectname, "__debug_info")) {
297247 self.dwarf_debug_info_index = index;
......@@ -345,153 +295,561 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
345295 }
346296}
347297
348pub 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);
298const 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 }
354318
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,
360322
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 }
365326 };
366327
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 });
371330
372 _ = try self.file.?.preadAll(raw_relocs, sect.reloff);
331 return symbols[start..end];
332 }
333};
373334
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 );
335fn 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;
381341 }
342 };
382343
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];
385348}
386349
387pub fn parseInitializers(self: *Object) !void {
388 const index = self.mod_init_func_section_index orelse return;
389 const section = self.sections.items[index];
350const 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 }
390364
391 log.debug("parsing initializers in {s}", .{self.name.?});
365 const SeniorityContext = struct {
366 object: *Object,
367 };
392368
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 }
398377 }
399378
400 mem.reverse(*Symbol, self.initializers.items);
401}
379 pub fn next(self: *TextBlockParser) !?*TextBlock {
380 if (self.index == self.nlists.len) return null;
402381
403pub 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();
406384
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);
411388
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;
415398
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 }
418402
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 );
422411 }
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;
426460 }
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 }
430482 }
431483
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
490pub 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;
454568 }
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 }
455631
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,
467663 };
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;
469718 }
470719
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;
479741 };
480 break :symbol &undef.base;
481 };
482742
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 }
484827 }
485828}
486829
830fn 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
487846pub fn parseDebugInfo(self: *Object) !void {
847 log.debug("parsing debug info in '{s}'", .{self.name.?});
848
488849 var debug_info = blk: {
489850 var di = try DebugInfo.parseFromObject(self.allocator, self);
490851 break :blk di orelse return;
491852 };
492 defer debug_info.deinit(self.allocator);
493
494 log.debug("parsing debug info in '{s}'", .{self.name.?});
495853
496854 // We assume there is only one CU.
497855 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
......@@ -505,44 +863,19 @@ pub fn parseDebugInfo(self: *Object) !void {
505863 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
506864 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
507865
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);
513869
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 };
535876 }
536877}
537878
538fn 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
546879pub fn parseDataInCode(self: *Object) !void {
547880 const index = self.data_in_code_cmd_index orelse return;
548881 const data_in_code = self.load_commands.items[index].LinkeditData;
......@@ -562,3 +895,16 @@ pub fn parseDataInCode(self: *Object) !void {
562895 try self.data_in_code_entries.append(self.allocator, dice);
563896 }
564897}
898
899fn 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
907pub 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 @@
1const Symbol = @This();
2
3const std = @import("std");
4const macho = std.macho;
5const mem = std.mem;
6
7const Allocator = mem.Allocator;
8const Dylib = @import("Dylib.zig");
9const Object = @import("Object.zig");
10
11pub 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.
22name: []u8,
23
24/// Alias of.
25alias: ?*Symbol = null,
26
27/// Index in GOT table for indirection.
28got_index: ?u32 = null,
29
30/// Index in stubs table for late binding.
31stubs_index: ?u32 = null,
32
33pub 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
84pub 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
110pub 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
120pub 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
135pub 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
143pub 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
150pub fn getTopmostAlias(base: *Symbol) *Symbol {
151 if (base.alias) |alias| {
152 return alias.getTopmostAlias();
153 }
154 return base;
155}
156
157pub fn isStab(sym: macho.nlist_64) bool {
158 return (macho.N_STAB & sym.n_type) != 0;
159}
160
161pub fn isPext(sym: macho.nlist_64) bool {
162 return (macho.N_PEXT & sym.n_type) != 0;
163}
164
165pub fn isExt(sym: macho.nlist_64) bool {
166 return (macho.N_EXT & sym.n_type) != 0;
167}
168
169pub fn isSect(sym: macho.nlist_64) bool {
170 const type_ = macho.N_TYPE & sym.n_type;
171 return type_ == macho.N_SECT;
172}
173
174pub fn isUndf(sym: macho.nlist_64) bool {
175 const type_ = macho.N_TYPE & sym.n_type;
176 return type_ == macho.N_UNDF;
177}
178
179pub fn isIndr(sym: macho.nlist_64) bool {
180 const type_ = macho.N_TYPE & sym.n_type;
181 return type_ == macho.N_INDR;
182}
183
184pub fn isAbs(sym: macho.nlist_64) bool {
185 const type_ = macho.N_TYPE & sym.n_type;
186 return type_ == macho.N_ABS;
187}
188
189pub fn isWeakDef(sym: macho.nlist_64) bool {
190 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
191}
192
193pub 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 @@
1const TextBlock = @This();
2
3const std = @import("std");
4const aarch64 = @import("../../codegen/aarch64.zig");
5const assert = std.debug.assert;
6const commands = @import("commands.zig");
7const log = std.log.scoped(.text_block);
8const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
11const meta = std.meta;
12
13const Allocator = mem.Allocator;
14const Arch = std.Target.Cpu.Arch;
15const MachO = @import("../MachO.zig");
16const 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.
24local_sym_index: u32,
25
26/// List of symbol aliases pointing to the same block via different nlists
27aliases: std.ArrayListUnmanaged(u32) = .{},
28
29/// List of symbols contained within this block
30contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
31
32/// Code (may be non-relocated) this block represents
33code: 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.
38size: u64,
39alignment: u32,
40
41relocs: std.ArrayListUnmanaged(Relocation) = .{},
42
43/// List of offsets contained within this block that need rebasing by the dynamic
44/// loader in presence of ASLR
45rebases: 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)
50bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
51
52/// List of data-in-code entries. This is currently specific to x86_64 only.
53dices: 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.
59stab: ?Stab = null,
60
61/// Points to the previous and next neighbours
62next: ?*TextBlock,
63prev: ?*TextBlock,
64
65/// Previous/next linked list pointers.
66/// This is the linked list node for this Decl's corresponding .debug_info tag.
67dbg_info_prev: ?*TextBlock,
68dbg_info_next: ?*TextBlock,
69/// Offset into .debug_info pointing to the tag for this Decl.
70dbg_info_off: u32,
71/// Size of the .debug_info tag for this Decl, not including padding.
72dbg_info_len: u32,
73
74pub 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
95pub 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
161pub 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
558pub 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
570pub 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.
583pub 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
595pub 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
607const RelocContext = struct {
608 base_addr: u64 = 0,
609 macho_file: *MachO,
610};
611
612fn 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
678pub 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
902fn 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
907fn 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
915fn 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
950fn 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
962fn 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
980fn 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
1008fn 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
1018fn 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
1049fn 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
1071pub 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
1168pub 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
1188const 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
1206fn 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
1221inline 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 @@
1const Zld = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const mem = std.mem;
7const meta = std.meta;
8const fs = std.fs;
9const macho = std.macho;
10const math = std.math;
11const log = std.log.scoped(.zld);
12const aarch64 = @import("../../codegen/aarch64.zig");
13const reloc = @import("reloc.zig");
14
15const Allocator = mem.Allocator;
16const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");
18const Dylib = @import("Dylib.zig");
19const Object = @import("Object.zig");
20const Symbol = @import("Symbol.zig");
21const Trie = @import("Trie.zig");
22
23usingnamespace @import("commands.zig");
24usingnamespace @import("bind.zig");
25
26allocator: *Allocator,
27
28target: ?std.Target = null,
29page_size: ?u16 = null,
30file: ?fs.File = null,
31output: ?Output = null,
32
33// TODO these args will become obselete once Zld is coalesced with incremental
34// linker.
35stack_size: u64 = 0,
36
37objects: std.ArrayListUnmanaged(*Object) = .{},
38archives: std.ArrayListUnmanaged(*Archive) = .{},
39dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
40
41next_dylib_ordinal: u16 = 1,
42
43load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
44
45pagezero_segment_cmd_index: ?u16 = null,
46text_segment_cmd_index: ?u16 = null,
47data_const_segment_cmd_index: ?u16 = null,
48data_segment_cmd_index: ?u16 = null,
49linkedit_segment_cmd_index: ?u16 = null,
50dyld_info_cmd_index: ?u16 = null,
51symtab_cmd_index: ?u16 = null,
52dysymtab_cmd_index: ?u16 = null,
53dylinker_cmd_index: ?u16 = null,
54data_in_code_cmd_index: ?u16 = null,
55function_starts_cmd_index: ?u16 = null,
56main_cmd_index: ?u16 = null,
57dylib_id_cmd_index: ?u16 = null,
58version_min_cmd_index: ?u16 = null,
59source_version_cmd_index: ?u16 = null,
60uuid_cmd_index: ?u16 = null,
61code_signature_cmd_index: ?u16 = null,
62
63// __TEXT segment sections
64text_section_index: ?u16 = null,
65stubs_section_index: ?u16 = null,
66stub_helper_section_index: ?u16 = null,
67text_const_section_index: ?u16 = null,
68cstring_section_index: ?u16 = null,
69ustring_section_index: ?u16 = null,
70gcc_except_tab_section_index: ?u16 = null,
71unwind_info_section_index: ?u16 = null,
72eh_frame_section_index: ?u16 = null,
73
74objc_methlist_section_index: ?u16 = null,
75objc_methname_section_index: ?u16 = null,
76objc_methtype_section_index: ?u16 = null,
77objc_classname_section_index: ?u16 = null,
78
79// __DATA_CONST segment sections
80got_section_index: ?u16 = null,
81mod_init_func_section_index: ?u16 = null,
82mod_term_func_section_index: ?u16 = null,
83data_const_section_index: ?u16 = null,
84
85objc_cfstring_section_index: ?u16 = null,
86objc_classlist_section_index: ?u16 = null,
87objc_imageinfo_section_index: ?u16 = null,
88
89// __DATA segment sections
90tlv_section_index: ?u16 = null,
91tlv_data_section_index: ?u16 = null,
92tlv_bss_section_index: ?u16 = null,
93la_symbol_ptr_section_index: ?u16 = null,
94data_section_index: ?u16 = null,
95bss_section_index: ?u16 = null,
96common_section_index: ?u16 = null,
97
98objc_const_section_index: ?u16 = null,
99objc_selrefs_section_index: ?u16 = null,
100objc_classrefs_section_index: ?u16 = null,
101objc_data_section_index: ?u16 = null,
102
103globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
104imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
105unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
106tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
107
108/// Offset into __DATA,__common section.
109/// Set if the linker found tentative definitions in any of the objects.
110tentative_defs_offset: u64 = 0,
111
112strtab: std.ArrayListUnmanaged(u8) = .{},
113strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
114
115threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
116local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
117stubs: std.ArrayListUnmanaged(*Symbol) = .{},
118got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
119
120stub_helper_stubs_start_off: ?u64 = null,
121
122pub const Output = struct {
123 tag: enum { exe, dylib },
124 path: []const u8,
125 install_name: ?[]const u8 = null,
126};
127
128const 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
139const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
140
141pub fn init(allocator: *Allocator) Zld {
142 return .{ .allocator = allocator };
143}
144
145pub 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
194pub 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
204const LinkArgs = struct {
205 syslibroot: ?[]const u8,
206 libs: []const []const u8,
207 rpaths: []const []const u8,
208};
209
210pub 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
246fn 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
279fn 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
301fn 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
335fn 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
432const MatchingSection = struct {
433 seg: u16,
434 sect: u16,
435};
436
437fn 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
838fn 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
967fn 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
1018fn 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
1033fn 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
1050fn 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
1057fn 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
1076fn 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
1115fn 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
1183fn 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
1208fn 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
1343fn 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
1363fn 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
1434fn 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
1475fn 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
1567fn 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
1696fn 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
1744fn 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
1912fn 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
1966fn 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
2248fn 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
2262fn 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
2276fn 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
2295fn 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
2398fn 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
2418fn 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
2431fn 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
2506fn 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
2577fn 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
2619fn 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
2684fn 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
2744fn 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
2876fn 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
3016fn 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
3033fn 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
3070fn 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
3093fn 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
3116fn 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
3134fn 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
3186fn 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
3201fn 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
3206pub 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;
1111const MachO = @import("../MachO.zig");
1212const padToIdeal = MachO.padToIdeal;
1313
14pub 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
23pub 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
1436pub const LoadCommand = union(enum) {
1537 Segment: SegmentCommand,
1638 DyldInfoOnly: macho.dyld_info_command,
......@@ -403,6 +425,44 @@ fn makeStaticString(bytes: []const u8) [16]u8 {
403425 return buf;
404426}
405427
428fn 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
433pub fn segmentName(sect: macho.section_64) []const u8 {
434 return parseName(&sect.segname);
435}
436
437pub fn sectionName(sect: macho.section_64) []const u8 {
438 return parseName(&sect.sectname);
439}
440
441pub fn sectionType(sect: macho.section_64) u8 {
442 return @truncate(u8, sect.flags & 0xff);
443}
444
445pub fn sectionAttrs(sect: macho.section_64) u32 {
446 return sect.flags & 0xffffff00;
447}
448
449pub 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
454pub fn sectionIsDebug(sect: macho.section_64) bool {
455 return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0;
456}
457
458pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool {
459 return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0;
460}
461
462pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool {
463 return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0;
464}
465
406466fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void {
407467 var stream = io.fixedBufferStream(buffer);
408468 var given = try LoadCommand.read(allocator, stream.reader());
src/link/MachO/reloc.zig deleted-206
......@@ -1,206 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.reloc);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const meta = std.meta;
8
9const aarch64 = @import("reloc/aarch64.zig");
10const x86_64 = @import("reloc/x86_64.zig");
11
12const Allocator = mem.Allocator;
13const Symbol = @import("Symbol.zig");
14
15pub 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
97pub 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
140pub 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
182pub 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 @@
1const std = @import("std");
2const aarch64 = @import("../../../codegen/aarch64.zig");
3const assert = std.debug.assert;
4const log = std.log.scoped(.reloc);
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const meta = std.meta;
9const reloc = @import("../reloc.zig");
10
11const Allocator = mem.Allocator;
12const Relocation = reloc.Relocation;
13const Symbol = @import("../Symbol.zig");
14
15pub 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
33pub 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
58pub 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
102pub 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
124pub 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
144pub 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
158pub 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
180pub 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
201pub 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
625inline 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 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.reloc);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const meta = std.meta;
8const reloc = @import("../reloc.zig");
9
10const Allocator = mem.Allocator;
11const Relocation = reloc.Relocation;
12const Symbol = @import("../Symbol.zig");
13
14pub 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
26pub 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
55pub 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
67pub 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
83pub 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
101pub 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(
16551655 }
16561656 }
16571657
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
16581670 if (comptime std.Target.current.isDarwin()) {
16591671 // If we want to link against frameworks, we need system headers.
16601672 if (framework_dirs.items.len > 0 or frameworks.items.len > 0)