authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-09-14 14:20:11+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-09-14 14:20:11+02:00
log85f065a5115b201b7b6b0b7325a4626211bbb642
treee255717a101910d0b024b17cff189952e61368a2
parentd1908c9f661abebb2879b02c8ea3ac823fec27e7
parent05763f43b3d8318c95891650c11ab243ce9a1fd5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9676 from ziglang/zld-incr

MachO: merges stage1 with self-hosted codepath

11 files changed, 4819 insertions(+), 5787 deletions(-)

CMakeLists.txt+1-1
...@@ -574,11 +574,11 @@ set(ZIG_STAGE2_SOURCES...@@ -574,11 +574,11 @@ set(ZIG_STAGE2_SOURCES
574 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"574 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
575 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"575 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
576 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"576 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"578 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
578 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"579 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
579 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"581 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
581 "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"583 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"584 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
lib/std/macho.zig+20-20
...@@ -601,35 +601,35 @@ pub const segment_command = extern struct {...@@ -601,35 +601,35 @@ pub const segment_command = extern struct {
601/// command and their size is reflected in cmdsize.601/// command and their size is reflected in cmdsize.
602pub const segment_command_64 = extern struct {602pub const segment_command_64 = extern struct {
603 /// LC_SEGMENT_64603 /// LC_SEGMENT_64
604 cmd: u32,604 cmd: u32 = LC_SEGMENT_64,
605605
606 /// includes sizeof section_64 structs606 /// includes sizeof section_64 structs
607 cmdsize: u32,607 cmdsize: u32 = @sizeOf(segment_command_64),
608608
609 /// segment name609 /// segment name
610 segname: [16]u8,610 segname: [16]u8,
611611
612 /// memory address of this segment612 /// memory address of this segment
613 vmaddr: u64,613 vmaddr: u64 = 0,
614614
615 /// memory size of this segment615 /// memory size of this segment
616 vmsize: u64,616 vmsize: u64 = 0,
617617
618 /// file offset of this segment618 /// file offset of this segment
619 fileoff: u64,619 fileoff: u64 = 0,
620620
621 /// amount to map from the file621 /// amount to map from the file
622 filesize: u64,622 filesize: u64 = 0,
623623
624 /// maximum VM protection624 /// maximum VM protection
625 maxprot: vm_prot_t,625 maxprot: vm_prot_t = VM_PROT_NONE,
626626
627 /// initial VM protection627 /// initial VM protection
628 initprot: vm_prot_t,628 initprot: vm_prot_t = VM_PROT_NONE,
629629
630 /// number of sections in segment630 /// number of sections in segment
631 nsects: u32,631 nsects: u32 = 0,
632 flags: u32,632 flags: u32 = 0,
633};633};
634634
635/// A segment is made up of zero or more sections. Non-MH_OBJECT files have635/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
...@@ -700,34 +700,34 @@ pub const section_64 = extern struct {...@@ -700,34 +700,34 @@ pub const section_64 = extern struct {
700 segname: [16]u8,700 segname: [16]u8,
701701
702 /// memory address of this section702 /// memory address of this section
703 addr: u64,703 addr: u64 = 0,
704704
705 /// size in bytes of this section705 /// size in bytes of this section
706 size: u64,706 size: u64 = 0,
707707
708 /// file offset of this section708 /// file offset of this section
709 offset: u32,709 offset: u32 = 0,
710710
711 /// section alignment (power of 2)711 /// section alignment (power of 2)
712 @"align": u32,712 @"align": u32 = 0,
713713
714 /// file offset of relocation entries714 /// file offset of relocation entries
715 reloff: u32,715 reloff: u32 = 0,
716716
717 /// number of relocation entries717 /// number of relocation entries
718 nreloc: u32,718 nreloc: u32 = 0,
719719
720 /// flags (section type and attributes720 /// flags (section type and attributes
721 flags: u32,721 flags: u32 = S_REGULAR,
722722
723 /// reserved (for offset or index)723 /// reserved (for offset or index)
724 reserved1: u32,724 reserved1: u32 = 0,
725725
726 /// reserved (for count or sizeof)726 /// reserved (for count or sizeof)
727 reserved2: u32,727 reserved2: u32 = 0,
728728
729 /// reserved729 /// reserved
730 reserved3: u32,730 reserved3: u32 = 0,
731};731};
732732
733pub const nlist = extern struct {733pub const nlist = extern struct {
src/codegen.zig+17-45
...@@ -2816,24 +2816,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2816,24 +2816,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2816 if (self.air.value(callee)) |func_value| {2816 if (self.air.value(callee)) |func_value| {
2817 if (func_value.castTag(.function)) |func_payload| {2817 if (func_value.castTag(.function)) |func_payload| {
2818 const func = func_payload.data;2818 const func = func_payload.data;
2819 const got_addr = blk: {2819 // TODO I'm hacking my way through here by repurposing .memory for storing
2820 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;2820 // index to the GOT target symbol index.
2821 const got = seg.sections.items[macho_file.got_section_index.?];
2822 const got_index = macho_file.got_entries_map.get(.{
2823 .where = .local,
2824 .where_index = func.owner_decl.link.macho.local_sym_index,
2825 }) orelse unreachable;
2826 break :blk got.addr + got_index * @sizeOf(u64);
2827 };
2828 switch (arch) {2821 switch (arch) {
2829 .x86_64 => {2822 .x86_64 => {
2830 try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr });2823 try self.genSetReg(Type.initTag(.u64), .rax, .{
2824 .memory = func.owner_decl.link.macho.local_sym_index,
2825 });
2831 // callq *%rax2826 // callq *%rax
2832 try self.code.ensureCapacity(self.code.items.len + 2);2827 try self.code.ensureCapacity(self.code.items.len + 2);
2833 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });2828 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
2834 },2829 },
2835 .aarch64 => {2830 .aarch64 => {
2836 try self.genSetReg(Type.initTag(.u64), .x30, .{ .memory = got_addr });2831 try self.genSetReg(Type.initTag(.u64), .x30, .{
2832 .memory = func.owner_decl.link.macho.local_sym_index,
2833 });
2837 // blr x302834 // blr x30
2838 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2835 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2839 },2836 },
...@@ -4345,29 +4342,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4345,29 +4342,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4345 }).toU32());4342 }).toU32());
43464343
4347 if (self.bin_file.cast(link.File.MachO)) |macho_file| {4344 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4348 // TODO this is super awkward. We are reversing the address of the GOT entry here.4345 // TODO I think the reloc might be in the wrong place.
4349 // We should probably have it cached or move the reloc adding somewhere else.
4350 const got_addr = blk: {
4351 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4352 const got = seg.sections.items[macho_file.got_section_index.?];
4353 break :blk got.addr;
4354 };
4355 const where_index = blk: for (macho_file.got_entries.items) |key, id| {
4356 if (got_addr + id * @sizeOf(u64) == addr) break :blk key.where_index;
4357 } else unreachable;
4358 const decl = macho_file.active_decl.?;4346 const decl = macho_file.active_decl.?;
4359 // Page reloc for adrp instruction.4347 // Page reloc for adrp instruction.
4360 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4348 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4361 .offset = offset,4349 .offset = offset,
4362 .where = .local,4350 .where = .local,
4363 .where_index = where_index,4351 .where_index = @intCast(u32, addr),
4364 .payload = .{ .page = .{ .kind = .got } },4352 .payload = .{ .page = .{ .kind = .got } },
4365 });4353 });
4366 // Pageoff reloc for adrp instruction.4354 // Pageoff reloc for adrp instruction.
4367 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4355 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4368 .offset = offset + 4,4356 .offset = offset + 4,
4369 .where = .local,4357 .where = .local,
4370 .where_index = where_index,4358 .where_index = @intCast(u32, addr),
4371 .payload = .{ .page_off = .{ .kind = .got } },4359 .payload = .{ .page_off = .{ .kind = .got } },
4372 });4360 });
4373 } else {4361 } else {
...@@ -4628,22 +4616,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4628,22 +4616,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4628 const offset = @intCast(u32, self.code.items.len);4616 const offset = @intCast(u32, self.code.items.len);
46294617
4630 if (self.bin_file.cast(link.File.MachO)) |macho_file| {4618 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4631 // TODO this is super awkward. We are reversing the address of the GOT entry here.4619 // TODO I think the reloc might be in the wrong place.
4632 // We should probably have it cached or move the reloc adding somewhere else.
4633 const got_addr = blk: {
4634 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4635 const got = seg.sections.items[macho_file.got_section_index.?];
4636 break :blk got.addr;
4637 };
4638 const where_index = blk: for (macho_file.got_entries.items) |key, id| {
4639 if (got_addr + id * @sizeOf(u64) == x) break :blk key.where_index;
4640 } else unreachable;
4641 const decl = macho_file.active_decl.?;4620 const decl = macho_file.active_decl.?;
4642 // Load reloc for LEA instruction.4621 // Load reloc for LEA instruction.
4643 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4622 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4644 .offset = offset - 4,4623 .offset = offset - 4,
4645 .where = .local,4624 .where = .local,
4646 .where_index = where_index,4625 .where_index = @intCast(u32, x),
4647 .payload = .{ .load = .{ .kind = .got } },4626 .payload = .{ .load = .{ .kind = .got } },
4648 });4627 });
4649 } else {4628 } else {
...@@ -4869,17 +4848,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4869,17 +4848,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4869 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];4848 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4870 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;4849 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4871 return MCValue{ .memory = got_addr };4850 return MCValue{ .memory = got_addr };
4872 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4851 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4873 const got_addr = blk: {4852 // TODO I'm hacking my way through here by repurposing .memory for storing
4874 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;4853 // index to the GOT target symbol index.
4875 const got = seg.sections.items[macho_file.got_section_index.?];4854 return MCValue{ .memory = decl.link.macho.local_sym_index };
4876 const got_index = macho_file.got_entries_map.get(.{
4877 .where = .local,
4878 .where_index = decl.link.macho.local_sym_index,
4879 }) orelse unreachable;
4880 break :blk got.addr + got_index * ptr_bytes;
4881 };
4882 return MCValue{ .memory = got_addr };
4883 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4855 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4884 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;4856 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4885 return MCValue{ .memory = got_addr };4857 return MCValue{ .memory = got_addr };
src/link/MachO.zig+3231-4152
...@@ -24,6 +24,7 @@ const trace = @import("../tracy.zig").trace;...@@ -24,6 +24,7 @@ const trace = @import("../tracy.zig").trace;
24const Air = @import("../Air.zig");24const Air = @import("../Air.zig");
25const Allocator = mem.Allocator;25const Allocator = mem.Allocator;
26const Archive = @import("MachO/Archive.zig");26const Archive = @import("MachO/Archive.zig");
27const Atom = @import("MachO/Atom.zig");
27const Cache = @import("../Cache.zig");28const Cache = @import("../Cache.zig");
28const CodeSignature = @import("MachO/CodeSignature.zig");29const CodeSignature = @import("MachO/CodeSignature.zig");
29const Compilation = @import("../Compilation.zig");30const Compilation = @import("../Compilation.zig");
...@@ -39,9 +40,10 @@ const Module = @import("../Module.zig");...@@ -39,9 +40,10 @@ const Module = @import("../Module.zig");
39const SegmentCommand = commands.SegmentCommand;40const SegmentCommand = commands.SegmentCommand;
40const StringIndexAdapter = std.hash_map.StringIndexAdapter;41const StringIndexAdapter = std.hash_map.StringIndexAdapter;
41const StringIndexContext = std.hash_map.StringIndexContext;42const StringIndexContext = std.hash_map.StringIndexContext;
42pub const TextBlock = @import("MachO/TextBlock.zig");
43const Trie = @import("MachO/Trie.zig");43const Trie = @import("MachO/Trie.zig");
4444
45pub const TextBlock = Atom;
46
45pub const base_tag: File.Tag = File.Tag.macho;47pub const base_tag: File.Tag = File.Tag.macho;
4648
47base: File,49base: File,
...@@ -95,9 +97,6 @@ source_version_cmd_index: ?u16 = null,...@@ -95,9 +97,6 @@ source_version_cmd_index: ?u16 = null,
95build_version_cmd_index: ?u16 = null,97build_version_cmd_index: ?u16 = null,
96uuid_cmd_index: ?u16 = null,98uuid_cmd_index: ?u16 = null,
97code_signature_cmd_index: ?u16 = null,99code_signature_cmd_index: ?u16 = null,
98/// Path to libSystem
99/// TODO this is obsolete, remove it.
100libsystem_cmd_index: ?u16 = null,
101100
102// __TEXT segment sections101// __TEXT segment sections
103text_section_index: ?u16 = null,102text_section_index: ?u16 = null,
...@@ -132,65 +131,59 @@ tlv_bss_section_index: ?u16 = null,...@@ -132,65 +131,59 @@ tlv_bss_section_index: ?u16 = null,
132la_symbol_ptr_section_index: ?u16 = null,131la_symbol_ptr_section_index: ?u16 = null,
133data_section_index: ?u16 = null,132data_section_index: ?u16 = null,
134bss_section_index: ?u16 = null,133bss_section_index: ?u16 = null,
135common_section_index: ?u16 = null,
136134
137objc_const_section_index: ?u16 = null,135objc_const_section_index: ?u16 = null,
138objc_selrefs_section_index: ?u16 = null,136objc_selrefs_section_index: ?u16 = null,
139objc_classrefs_section_index: ?u16 = null,137objc_classrefs_section_index: ?u16 = null,
140objc_data_section_index: ?u16 = null,138objc_data_section_index: ?u16 = null,
141139
140bss_file_offset: u32 = 0,
141tlv_bss_file_offset: u32 = 0,
142
142locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},143locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
143globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},144globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
144undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},145undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
145symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},146symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
147unresolved: std.AutoArrayHashMapUnmanaged(u32, enum {
148 none,
149 stub,
150 got,
151}) = .{},
152tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
146153
147locals_free_list: std.ArrayListUnmanaged(u32) = .{},154locals_free_list: std.ArrayListUnmanaged(u32) = .{},
148globals_free_list: std.ArrayListUnmanaged(u32) = .{},155globals_free_list: std.ArrayListUnmanaged(u32) = .{},
149156
150stub_helper_stubs_start_off: ?u64 = null,157dyld_stub_binder_index: ?u32 = null,
158dyld_private_atom: ?*Atom = null,
159stub_helper_preamble_atom: ?*Atom = null,
151160
152strtab: std.ArrayListUnmanaged(u8) = .{},161strtab: std.ArrayListUnmanaged(u8) = .{},
153strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},162strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
154163
155got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{},164got_entries_map: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, *Atom) = .{},
156got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{},165stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{},
157
158got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
159
160stubs: std.ArrayListUnmanaged(u32) = .{},
161stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
162166
163error_flags: File.ErrorFlags = File.ErrorFlags{},167error_flags: File.ErrorFlags = File.ErrorFlags{},
164168
165got_entries_count_dirty: bool = false,
166load_commands_dirty: bool = false,169load_commands_dirty: bool = false,
167rebase_info_dirty: bool = false,170sections_order_dirty: bool = false,
168binding_info_dirty: bool = false,
169lazy_binding_info_dirty: bool = false,
170export_info_dirty: bool = false,
171
172strtab_dirty: bool = false,
173strtab_needs_relocation: bool = false,
174
175has_dices: bool = false,171has_dices: bool = false,
176has_stabs: bool = false,172has_stabs: bool = false,
173/// A helper var to indicate if we are at the start of the incremental updates, or
174/// already somewhere further along the update-and-run chain.
175/// TODO once we add opening a prelinked output binary from file, this will become
176/// obsolete as we will carry on where we left off.
177cold_start: bool = false,
177178
178section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},179section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
179180
180pending_updates: std.ArrayListUnmanaged(struct {181/// A list of atoms that have surplus capacity. This list can have false
181 kind: enum {
182 got,
183 stub,
184 },
185 index: u32,
186}) = .{},
187
188/// A list of text blocks that have surplus capacity. This list can have false
189/// positives, as functions grow and shrink over time, only sometimes being added182/// positives, as functions grow and shrink over time, only sometimes being added
190/// or removed from the freelist.183/// or removed from the freelist.
191///184///
192/// A text block has surplus capacity when its overcapacity value is greater than185/// An atom has surplus capacity when its overcapacity value is greater than
193/// padToIdeal(minimum_text_block_size). That is, when it has so186/// padToIdeal(minimum_atom_size). That is, when it has so
194/// much extra capacity, that we could fit a small new symbol in it, itself with187/// much extra capacity, that we could fit a small new symbol in it, itself with
195/// ideal_capacity or more.188/// ideal_capacity or more.
196///189///
...@@ -198,25 +191,23 @@ pending_updates: std.ArrayListUnmanaged(struct {...@@ -198,25 +191,23 @@ pending_updates: std.ArrayListUnmanaged(struct {
198///191///
199/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that192/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
200/// overcapacity can be negative. A simple way to have negative overcapacity is to193/// overcapacity can be negative. A simple way to have negative overcapacity is to
201/// allocate a fresh text block, which will have ideal capacity, and then grow it194/// allocate a fresh atom, which will have ideal capacity, and then grow it
202/// by 1 byte. It will then have -1 overcapacity.195/// by 1 byte. It will then have -1 overcapacity.
203text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},196atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanaged(*Atom)) = .{},
204197
205/// Pointer to the last allocated text block198/// Pointer to the last allocated atom
206last_text_block: ?*TextBlock = null,199atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
207200
208/// List of TextBlocks that are owned directly by the linker.201/// List of atoms that are owned directly by the linker.
209/// Currently these are only TextBlocks that are the result of linking202/// Currently these are only atoms that are the result of linking
210/// object files. TextBlock which take part in incremental linking are 203/// object files. Atoms which take part in incremental linking are
211/// at present owned by Module.Decl.204/// at present owned by Module.Decl.
212/// TODO consolidate this.205/// TODO consolidate this.
213managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
214
215blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
216207
217/// Table of Decls that are currently alive.208/// Table of Decls that are currently alive.
218/// We store them here so that we can properly dispose of any allocated209/// We store them here so that we can properly dispose of any allocated
219/// memory within the TextBlock in the incremental linker.210/// memory within the atom in the incremental linker.
220/// TODO consolidate this.211/// TODO consolidate this.
221decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},212decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
222213
...@@ -226,6 +217,12 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},...@@ -226,6 +217,12 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
226/// somewhere else in the codegen.217/// somewhere else in the codegen.
227active_decl: ?*Module.Decl = null,218active_decl: ?*Module.Decl = null,
228219
220const PendingUpdate = union(enum) {
221 resolve_undef: u32,
222 add_stub_entry: u32,
223 add_got_entry: u32,
224};
225
229const SymbolWithLoc = struct {226const SymbolWithLoc = struct {
230 // Table where the symbol can be found.227 // Table where the symbol can be found.
231 where: enum {228 where: enum {
...@@ -247,21 +244,10 @@ pub const GotIndirectionKey = struct {...@@ -247,21 +244,10 @@ pub const GotIndirectionKey = struct {
247244
248/// When allocating, the ideal_capacity is calculated by245/// When allocating, the ideal_capacity is calculated by
249/// actual_capacity + (actual_capacity / ideal_factor)246/// actual_capacity + (actual_capacity / ideal_factor)
250const ideal_factor = 2;247const ideal_factor = 4;
251248
252/// Default path to dyld249/// Default path to dyld
253/// TODO instead of hardcoding it, we should probably look through some env vars and search paths250const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
254/// instead but this will do for now.
255const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
256
257/// Default lib search path
258/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
259/// instead but this will do for now.
260const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
261
262const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
263/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
264const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
265251
266/// In order for a slice of bytes to be considered eligible to keep metadata pointing at252/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
267/// it as a possible place to put new symbols, it must have enough room for this many bytes253/// it as a possible place to put new symbols, it must have enough room for this many bytes
...@@ -269,6 +255,10 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B....@@ -269,6 +255,10 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.
269const minimum_text_block_size = 64;255const minimum_text_block_size = 64;
270pub const min_text_capacity = padToIdeal(minimum_text_block_size);256pub const min_text_capacity = padToIdeal(minimum_text_block_size);
271257
258/// Virtual memory offset corresponds to the size of __PAGEZERO segment and start of
259/// __TEXT segment.
260const pagezero_vmsize: u64 = 0x100000000;
261
272pub const Export = struct {262pub const Export = struct {
273 sym_index: ?u32 = null,263 sym_index: ?u32 = null,
274};264};
...@@ -323,31 +313,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -323,31 +313,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
323 return self;313 return self;
324 }314 }
325315
326 if (!options.strip and options.module != null) {316 // TODO Migrate DebugSymbols to the merged linker codepaths
327 // Create dSYM bundle.317 // if (!options.strip and options.module != null) {
328 const dir = options.module.?.zig_cache_artifact_directory;318 // // Create dSYM bundle.
329 log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path });319 // const dir = options.module.?.zig_cache_artifact_directory;
320 // log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path });
330321
331 const d_sym_path = try fmt.allocPrint(322 // const d_sym_path = try fmt.allocPrint(
332 allocator,323 // allocator,
333 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",324 // "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
334 .{sub_path},325 // .{sub_path},
335 );326 // );
336 defer allocator.free(d_sym_path);327 // defer allocator.free(d_sym_path);
337328
338 var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});329 // var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});
339 defer d_sym_bundle.close();330 // defer d_sym_bundle.close();
340331
341 const d_sym_file = try d_sym_bundle.createFile(sub_path, .{332 // const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
342 .truncate = false,333 // .truncate = false,
343 .read = true,334 // .read = true,
344 });335 // });
345336
346 self.d_sym = .{337 // self.d_sym = .{
347 .base = self,338 // .base = self,
348 .file = d_sym_file,339 // .file = d_sym_file,
349 };340 // };
350 }341 // }
351342
352 // Index 0 is always a null symbol.343 // Index 0 is always a null symbol.
353 try self.locals.append(allocator, .{344 try self.locals.append(allocator, .{
...@@ -357,13 +348,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -357,13 +348,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
357 .n_desc = 0,348 .n_desc = 0,
358 .n_value = 0,349 .n_value = 0,
359 });350 });
351 try self.strtab.append(allocator, 0);
360352
361 try self.populateMissingMetadata();353 try self.populateMissingMetadata();
362 try self.writeLocalSymbol(0);
363354
364 if (self.d_sym) |*ds| {355 if (self.d_sym) |*ds| {
365 try ds.populateMissingMetadata(allocator);356 try ds.populateMissingMetadata(allocator);
366 try ds.writeLocalSymbol(0);
367 }357 }
368358
369 return self;359 return self;
...@@ -403,179 +393,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -403,179 +393,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
403 }393 }
404 }394 }
405395
406 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
407 if (use_stage1) {
408 return self.linkWithZld(comp);
409 } else {
410 switch (self.base.options.effectiveOutputMode()) {
411 .Exe, .Obj => {},
412 .Lib => return error.TODOImplementWritingLibFiles,
413 }
414 return self.flushModule(comp);
415 }
416}
417
418pub fn flushModule(self: *MachO, comp: *Compilation) !void {
419 _ = comp;
420 const tracy = trace(@src());
421 defer tracy.end();
422
423 const output_mode = self.base.options.output_mode;
424
425 switch (output_mode) {
426 .Exe => {
427 if (self.entry_addr) |addr| {
428 // Update LC_MAIN with entry offset.
429 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
430 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main;
431 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
432 main_cmd.stacksize = self.base.options.stack_size_override orelse 0;
433 self.load_commands_dirty = true;
434 }
435 try self.writeRebaseInfoTable();
436 try self.writeBindInfoTable();
437 try self.writeLazyBindInfoTable();
438 try self.writeExportInfo();
439 try self.writeAllGlobalAndUndefSymbols();
440 try self.writeIndirectSymbolTable();
441 try self.writeStringTable();
442 try self.updateLinkeditSegmentSizes();
443
444 if (self.d_sym) |*ds| {
445 // Flush debug symbols bundle.
446 try ds.flushModule(self.base.allocator, self.base.options);
447 }
448
449 if (self.requires_adhoc_codesig) {
450 // Preallocate space for the code signature.
451 // We need to do this at this stage so that we have the load commands with proper values
452 // written out to the file.
453 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
454 // where the code signature goes into.
455 try self.writeCodeSignaturePadding();
456 }
457 },
458 .Obj => {},
459 .Lib => return error.TODOImplementWritingLibFiles,
460 }
461
462 try self.writeLoadCommands();
463 try self.writeHeader();
464
465 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
466 log.debug("flushing. no_entry_point_found = true", .{});
467 self.error_flags.no_entry_point_found = true;
468 } else {
469 log.debug("flushing. no_entry_point_found = false", .{});
470 self.error_flags.no_entry_point_found = false;
471 }
472
473 assert(!self.got_entries_count_dirty);
474 assert(!self.load_commands_dirty);
475 assert(!self.rebase_info_dirty);
476 assert(!self.binding_info_dirty);
477 assert(!self.lazy_binding_info_dirty);
478 assert(!self.export_info_dirty);
479 assert(!self.strtab_dirty);
480 assert(!self.strtab_needs_relocation);
481
482 if (self.requires_adhoc_codesig) {
483 try self.writeCodeSignature(); // code signing always comes last
484 }
485}
486
487fn resolveSearchDir(
488 arena: *Allocator,
489 dir: []const u8,
490 syslibroot: ?[]const u8,
491) !?[]const u8 {
492 var candidates = std.ArrayList([]const u8).init(arena);
493
494 if (fs.path.isAbsolute(dir)) {
495 if (syslibroot) |root| {
496 const common_dir = if (std.Target.current.os.tag == .windows) blk: {
497 // We need to check for disk designator and strip it out from dir path so
498 // that we can concat dir with syslibroot.
499 // TODO we should backport this mechanism to 'MachO.Dylib.parseDependentLibs()'
500 const disk_designator = fs.path.diskDesignatorWindows(dir);
501
502 if (mem.indexOf(u8, dir, disk_designator)) |where| {
503 break :blk dir[where + disk_designator.len ..];
504 }
505
506 break :blk dir;
507 } else dir;
508 const full_path = try fs.path.join(arena, &[_][]const u8{ root, common_dir });
509 try candidates.append(full_path);
510 }
511 }
512
513 try candidates.append(dir);
514
515 for (candidates.items) |candidate| {
516 // Verify that search path actually exists
517 var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
518 error.FileNotFound => continue,
519 else => |e| return e,
520 };
521 defer tmp.close();
522
523 return candidate;
524 }
525
526 return null;
527}
528
529fn resolveLib(
530 arena: *Allocator,
531 search_dirs: []const []const u8,
532 name: []const u8,
533 ext: []const u8,
534) !?[]const u8 {
535 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
536
537 for (search_dirs) |dir| {
538 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
539
540 // Check if the file exists.
541 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
542 error.FileNotFound => continue,
543 else => |e| return e,
544 };
545 defer tmp.close();
546
547 return full_path;
548 }
549
550 return null;
551}
552
553fn resolveFramework(
554 arena: *Allocator,
555 search_dirs: []const []const u8,
556 name: []const u8,
557 ext: []const u8,
558) !?[]const u8 {
559 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
560 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
561
562 for (search_dirs) |dir| {
563 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
564
565 // Check if the file exists.
566 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
567 error.FileNotFound => continue,
568 else => |e| return e,
569 };
570 defer tmp.close();
571
572 return full_path;
573 }
574
575 return null;
576}
577
578fn linkWithZld(self: *MachO, comp: *Compilation) !void {
579 const tracy = trace(@src());396 const tracy = trace(@src());
580 defer tracy.end();397 defer tracy.end();
581398
...@@ -584,11 +401,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -584,11 +401,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
584 const arena = &arena_allocator.allocator;401 const arena = &arena_allocator.allocator;
585402
586 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.403 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
404 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
587405
588 // If there is no Zig code to compile, then we should skip flushing the output file because it406 // If there is no Zig code to compile, then we should skip flushing the output file because it
589 // will not be part of the linker line anyway.407 // will not be part of the linker line anyway.
590 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {408 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
591 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
592 if (use_stage1) {409 if (use_stage1) {
593 const obj_basename = try std.zig.binNameAlloc(arena, .{410 const obj_basename = try std.zig.binNameAlloc(arena, .{
594 .root_name = self.base.options.root_name,411 .root_name = self.base.options.root_name,
...@@ -600,8 +417,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -600,8 +417,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
600 break :blk full_obj_path;417 break :blk full_obj_path;
601 }418 }
602419
420 const obj_basename = self.base.intermediary_basename orelse break :blk null;
603 try self.flushModule(comp);421 try self.flushModule(comp);
604 const obj_basename = self.base.intermediary_basename.?;
605 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});422 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
606 break :blk full_obj_path;423 break :blk full_obj_path;
607 } else null;424 } else null;
...@@ -617,8 +434,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -617,8 +434,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
617 defer if (!self.base.options.disable_lld_caching) man.deinit();434 defer if (!self.base.options.disable_lld_caching) man.deinit();
618435
619 var digest: [Cache.hex_digest_len]u8 = undefined;436 var digest: [Cache.hex_digest_len]u8 = undefined;
437 var needs_full_relink = true;
438
439 cache: {
440 if (use_stage1 and self.base.options.disable_lld_caching) break :cache;
620441
621 if (!self.base.options.disable_lld_caching) {
622 man = comp.cache_parent.obtain();442 man = comp.cache_parent.obtain();
623443
624 // We are about to obtain this lock, so here we give other processes a chance first.444 // We are about to obtain this lock, so here we give other processes a chance first.
...@@ -652,17 +472,36 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -652,17 +472,36 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
652 id_symlink_basename,472 id_symlink_basename,
653 &prev_digest_buf,473 &prev_digest_buf,
654 ) catch |err| blk: {474 ) catch |err| blk: {
655 log.debug("MachO Zld new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });475 log.debug("MachO Zld new_digest={s} error: {s}", .{
476 std.fmt.fmtSliceHexLower(&digest),
477 @errorName(err),
478 });
656 // Handle this as a cache miss.479 // Handle this as a cache miss.
657 break :blk prev_digest_buf[0..0];480 break :blk prev_digest_buf[0..0];
658 };481 };
659 if (mem.eql(u8, prev_digest, &digest)) {482 if (mem.eql(u8, prev_digest, &digest)) {
660 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
661 // Hot diggity dog! The output binary is already there.483 // Hot diggity dog! The output binary is already there.
662 self.base.lock = man.toOwnedLock();484
663 return;485 if (use_stage1) {
486 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
487 self.base.lock = man.toOwnedLock();
488 return;
489 } else {
490 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});
491 if (!self.cold_start) {
492 log.debug(" no need to relink objects", .{});
493 needs_full_relink = false;
494 } else {
495 log.debug(" TODO parse prelinked binary and continue linking where we left off", .{});
496 // TODO until such time however, perform a full relink of objects.
497 needs_full_relink = true;
498 }
499 }
664 }500 }
665 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });501 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
502 std.fmt.fmtSliceHexLower(prev_digest),
503 std.fmt.fmtSliceHexLower(&digest),
504 });
666505
667 // We are about to change the output file to be different, so we invalidate the build hash now.506 // We are about to change the output file to be different, so we invalidate the build hash now.
668 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {507 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
...@@ -670,7 +509,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -670,7 +509,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
670 else => |e| return e,509 else => |e| return e,
671 };510 };
672 }511 }
673
674 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});512 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
675513
676 if (self.base.options.output_mode == .Obj) {514 if (self.base.options.output_mode == .Obj) {
...@@ -697,270 +535,456 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -697,270 +535,456 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
697 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});535 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
698 }536 }
699 } else {537 } else {
700 // Positional arguments to the linker such as object files and static archives.538 if (use_stage1) {
701 var positionals = std.ArrayList([]const u8).init(arena);539 const sub_path = self.base.options.emit.?.sub_path;
702540 self.base.file = try directory.handle.createFile(sub_path, .{
703 try positionals.appendSlice(self.base.options.objects);541 .truncate = true,
542 .read = true,
543 .mode = link.determineMode(self.base.options),
544 });
545 try self.populateMissingMetadata();
704546
705 for (comp.c_object_table.keys()) |key| {547 // TODO mimicking insertion of null symbol from incremental linker.
706 try positionals.append(key.status.success.object_path);548 // This will need to moved.
549 try self.locals.append(self.base.allocator, .{
550 .n_strx = 0,
551 .n_type = macho.N_UNDF,
552 .n_sect = 0,
553 .n_desc = 0,
554 .n_value = 0,
555 });
556 try self.strtab.append(self.base.allocator, 0);
707 }557 }
708558
709 if (module_obj_path) |p| {559 if (needs_full_relink) {
710 try positionals.append(p);560 self.objects.clearRetainingCapacity();
711 }561 self.archives.clearRetainingCapacity();
562 self.dylibs.clearRetainingCapacity();
563 self.dylibs_map.clearRetainingCapacity();
564 self.referenced_dylibs.clearRetainingCapacity();
712565
713 try positionals.append(comp.compiler_rt_static_lib.?.full_object_path);566 // TODO figure out how to clear atoms from objects, etc.
714567
715 // libc++ dep568 // Positional arguments to the linker such as object files and static archives.
716 if (self.base.options.link_libcpp) {569 var positionals = std.ArrayList([]const u8).init(arena);
717 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
718 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
719 }
720570
721 // Shared and static libraries passed via `-l` flag.571 try positionals.appendSlice(self.base.options.objects);
722 var search_lib_names = std.ArrayList([]const u8).init(arena);
723572
724 const system_libs = self.base.options.system_libs.keys();573 for (comp.c_object_table.keys()) |key| {
725 for (system_libs) |link_lib| {574 try positionals.append(key.status.success.object_path);
726 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
727 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
728 // case we want to avoid prepending "-l".
729 if (Compilation.classifyFileExt(link_lib) == .shared_library) {
730 try positionals.append(link_lib);
731 continue;
732 }575 }
733576
734 try search_lib_names.append(link_lib);577 if (module_obj_path) |p| {
735 }578 try positionals.append(p);
579 }
736580
737 var lib_dirs = std.ArrayList([]const u8).init(arena);581 if (comp.compiler_rt_static_lib) |lib| {
738 for (self.base.options.lib_dirs) |dir| {582 try positionals.append(lib.full_object_path);
739 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
740 try lib_dirs.append(search_dir);
741 } else {
742 log.warn("directory not found for '-L{s}'", .{dir});
743 }583 }
744 }
745584
746 var libs = std.ArrayList([]const u8).init(arena);585 // libc++ dep
747 var lib_not_found = false;586 if (self.base.options.link_libcpp) {
748 for (search_lib_names.items) |lib_name| {587 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
749 // Assume ld64 default: -search_paths_first588 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
750 // Look in each directory for a dylib (stub first), and then for archive589 }
751 // TODO implement alternative: -search_dylibs_first590
752 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {591 // Shared and static libraries passed via `-l` flag.
753 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {592 var search_lib_names = std.ArrayList([]const u8).init(arena);
754 try libs.append(full_path);593
755 break;594 const system_libs = self.base.options.system_libs.keys();
595 for (system_libs) |link_lib| {
596 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
597 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
598 // case we want to avoid prepending "-l".
599 if (Compilation.classifyFileExt(link_lib) == .shared_library) {
600 try positionals.append(link_lib);
601 continue;
756 }602 }
757 } else {603
758 log.warn("library not found for '-l{s}'", .{lib_name});604 try search_lib_names.append(link_lib);
759 lib_not_found = true;
760 }605 }
761 }
762606
763 if (lib_not_found) {607 var lib_dirs = std.ArrayList([]const u8).init(arena);
764 log.warn("Library search paths:", .{});608 for (self.base.options.lib_dirs) |dir| {
765 for (lib_dirs.items) |dir| {609 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
766 log.warn(" {s}", .{dir});610 try lib_dirs.append(search_dir);
611 } else {
612 log.warn("directory not found for '-L{s}'", .{dir});
613 }
767 }614 }
768 }
769615
770 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.616 var libs = std.ArrayList([]const u8).init(arena);
771 var libsystem_available = false;617 var lib_not_found = false;
772 if (self.base.options.sysroot != null) blk: {618 for (search_lib_names.items) |lib_name| {
773 // Try stub file first. If we hit it, then we're done as the stub file619 // Assume ld64 default: -search_paths_first
774 // re-exports every single symbol definition.620 // Look in each directory for a dylib (stub first), and then for archive
775 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {621 // TODO implement alternative: -search_dylibs_first
776 try libs.append(full_path);622 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
777 libsystem_available = true;623 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
778 break :blk;624 try libs.append(full_path);
625 break;
626 }
627 } else {
628 log.warn("library not found for '-l{s}'", .{lib_name});
629 lib_not_found = true;
630 }
631 }
632
633 if (lib_not_found) {
634 log.warn("Library search paths:", .{});
635 for (lib_dirs.items) |dir| {
636 log.warn(" {s}", .{dir});
637 }
779 }638 }
780 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib639
781 // doesn't export libc.dylib which we'll need to resolve subsequently also.640 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
782 if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {641 var libsystem_available = false;
783 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {642 if (self.base.options.sysroot != null) blk: {
784 try libs.append(libsystem_path);643 // Try stub file first. If we hit it, then we're done as the stub file
785 try libs.append(libc_path);644 // re-exports every single symbol definition.
645 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
646 try libs.append(full_path);
786 libsystem_available = true;647 libsystem_available = true;
787 break :blk;648 break :blk;
788 }649 }
650 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
651 // doesn't export libc.dylib which we'll need to resolve subsequently also.
652 if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
653 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
654 try libs.append(libsystem_path);
655 try libs.append(libc_path);
656 libsystem_available = true;
657 break :blk;
658 }
659 }
660 }
661 if (!libsystem_available) {
662 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
663 "libc", "darwin", "libSystem.B.tbd",
664 });
665 try libs.append(full_path);
789 }666 }
790 }
791 if (!libsystem_available) {
792 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
793 "libc", "darwin", "libSystem.B.tbd",
794 });
795 try libs.append(full_path);
796 }
797667
798 // frameworks668 // frameworks
799 var framework_dirs = std.ArrayList([]const u8).init(arena);669 var framework_dirs = std.ArrayList([]const u8).init(arena);
800 for (self.base.options.framework_dirs) |dir| {670 for (self.base.options.framework_dirs) |dir| {
801 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {671 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
802 try framework_dirs.append(search_dir);672 try framework_dirs.append(search_dir);
803 } else {673 } else {
804 log.warn("directory not found for '-F{s}'", .{dir});674 log.warn("directory not found for '-F{s}'", .{dir});
675 }
805 }676 }
806 }
807677
808 var framework_not_found = false;678 var framework_not_found = false;
809 for (self.base.options.frameworks) |framework| {679 for (self.base.options.frameworks) |framework| {
810 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {680 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
811 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {681 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
812 try libs.append(full_path);682 try libs.append(full_path);
813 break;683 break;
684 }
685 } else {
686 log.warn("framework not found for '-framework {s}'", .{framework});
687 framework_not_found = true;
814 }688 }
815 } else {
816 log.warn("framework not found for '-framework {s}'", .{framework});
817 framework_not_found = true;
818 }689 }
819 }
820690
821 if (framework_not_found) {691 if (framework_not_found) {
822 log.warn("Framework search paths:", .{});692 log.warn("Framework search paths:", .{});
823 for (framework_dirs.items) |dir| {693 for (framework_dirs.items) |dir| {
824 log.warn(" {s}", .{dir});694 log.warn(" {s}", .{dir});
695 }
825 }696 }
826 }
827697
828 // rpaths698 // rpaths
829 var rpath_table = std.StringArrayHashMap(void).init(arena);699 var rpath_table = std.StringArrayHashMap(void).init(arena);
830 for (self.base.options.rpath_list) |rpath| {700 for (self.base.options.rpath_list) |rpath| {
831 if (rpath_table.contains(rpath)) continue;701 if (rpath_table.contains(rpath)) continue;
832 try rpath_table.putNoClobber(rpath, {});702 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
833 }703 u64,
704 @sizeOf(macho.rpath_command) + rpath.len + 1,
705 @sizeOf(u64),
706 ));
707 var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{
708 .cmd = macho.LC_RPATH,
709 .cmdsize = cmdsize,
710 .path = @sizeOf(macho.rpath_command),
711 });
712 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
713 mem.set(u8, rpath_cmd.data, 0);
714 mem.copy(u8, rpath_cmd.data, rpath);
715 try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd });
716 try rpath_table.putNoClobber(rpath, {});
717 self.load_commands_dirty = true;
718 }
834719
835 var rpaths = std.ArrayList([]const u8).init(arena);720 if (self.base.options.verbose_link) {
836 try rpaths.ensureCapacity(rpath_table.count());721 var argv = std.ArrayList([]const u8).init(arena);
837 for (rpath_table.keys()) |*key| {
838 rpaths.appendAssumeCapacity(key.*);
839 }
840722
841 if (self.base.options.verbose_link) {723 try argv.append("zig");
842 var argv = std.ArrayList([]const u8).init(arena);724 try argv.append("ld");
843725
844 try argv.append("zig");726 if (is_exe_or_dyn_lib) {
845 try argv.append("ld");727 try argv.append("-dynamic");
728 }
846729
847 if (is_exe_or_dyn_lib) {730 if (is_dyn_lib) {
848 try argv.append("-dynamic");731 try argv.append("-dylib");
849 }
850732
851 if (is_dyn_lib) {733 const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{
852 try argv.append("-dylib");734 self.base.options.emit.?.sub_path,
735 });
736 try argv.append("-install_name");
737 try argv.append(install_name);
738 }
853739
854 const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{740 if (self.base.options.sysroot) |syslibroot| {
855 self.base.options.emit.?.sub_path,741 try argv.append("-syslibroot");
856 });742 try argv.append(syslibroot);
857 try argv.append("-install_name");743 }
858 try argv.append(install_name);
859 }
860744
861 if (self.base.options.sysroot) |syslibroot| {745 for (rpath_table.keys()) |rpath| {
862 try argv.append("-syslibroot");746 try argv.append("-rpath");
863 try argv.append(syslibroot);747 try argv.append(rpath);
864 }748 }
865749
866 for (rpaths.items) |rpath| {750 try argv.appendSlice(positionals.items);
867 try argv.append("-rpath");
868 try argv.append(rpath);
869 }
870751
871 try argv.appendSlice(positionals.items);752 try argv.append("-o");
753 try argv.append(full_out_path);
872754
873 try argv.append("-o");755 try argv.append("-lSystem");
874 try argv.append(full_out_path);756 try argv.append("-lc");
875757
876 try argv.append("-lSystem");758 for (search_lib_names.items) |l_name| {
877 try argv.append("-lc");759 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
760 }
878761
879 for (search_lib_names.items) |l_name| {762 for (self.base.options.lib_dirs) |lib_dir| {
880 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));763 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
881 }764 }
882765
883 for (self.base.options.lib_dirs) |lib_dir| {766 for (self.base.options.frameworks) |framework| {
884 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));767 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));
885 }768 }
886769
887 for (self.base.options.frameworks) |framework| {770 for (self.base.options.framework_dirs) |framework_dir| {
888 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));771 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
889 }772 }
890773
891 for (self.base.options.framework_dirs) |framework_dir| {774 Compilation.dump_argv(argv.items);
892 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
893 }775 }
894776
895 Compilation.dump_argv(argv.items);777 try self.parseInputFiles(positionals.items, self.base.options.sysroot);
778 try self.parseLibs(libs.items, self.base.options.sysroot);
896 }779 }
897780
898 const sub_path = self.base.options.emit.?.sub_path;781 if (self.bss_section_index) |idx| {
899 self.base.file = try directory.handle.createFile(sub_path, .{782 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
900 .truncate = true,783 const sect = &seg.sections.items[idx];
901 .read = true,784 sect.offset = self.bss_file_offset;
902 .mode = link.determineMode(self.base.options),785 }
903 });786 if (self.tlv_bss_section_index) |idx| {
904787 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
905 // TODO mimicking insertion of null symbol from incremental linker.788 const sect = &seg.sections.items[idx];
906 // This will need to moved.789 sect.offset = self.tlv_bss_file_offset;
907 try self.locals.append(self.base.allocator, .{790 }
908 .n_strx = 0,
909 .n_type = macho.N_UNDF,
910 .n_sect = 0,
911 .n_desc = 0,
912 .n_value = 0,
913 });
914 try self.strtab.append(self.base.allocator, 0);
915
916 try self.populateMetadata();
917 try self.addRpathLCs(rpaths.items);
918 try self.parseInputFiles(positionals.items, self.base.options.sysroot);
919 try self.parseLibs(libs.items, self.base.options.sysroot);
920 try self.resolveSymbols();
921 try self.parseTextBlocks();
922 try self.addLoadDylibLCs();
923 try self.addDataInCodeLC();
924 try self.addCodeSignatureLC();
925791
926 {792 for (self.objects.items) |*object, object_id| {
927 // Add dyld_stub_binder as the final GOT entry.793 if (object.analyzed) continue;
928 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "dyld_stub_binder"), StringIndexAdapter{794 try self.resolveSymbolsInObject(@intCast(u16, object_id));
929 .bytes = &self.strtab,
930 }) orelse unreachable;
931 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
932 const got_index = @intCast(u32, self.got_entries.items.len);
933 const got_entry = GotIndirectionKey{
934 .where = .undef,
935 .where_index = resolv.where_index,
936 };
937 try self.got_entries.append(self.base.allocator, got_entry);
938 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index);
939 }795 }
940796
941 try self.sortSections();797 try self.resolveSymbolsInArchives();
942 try self.allocateTextSegment();798 try self.resolveDyldStubBinder();
943 try self.allocateDataConstSegment();799 try self.createDyldPrivateAtom();
944 try self.allocateDataSegment();800 try self.createStubHelperPreambleAtom();
945 self.allocateLinkeditSegment();801 try self.resolveSymbolsInDylibs();
946 try self.allocateTextBlocks();802 try self.createDsoHandleAtom();
947 try self.flushZld();803 try self.addCodeSignatureLC();
804
805 for (self.unresolved.keys()) |index| {
806 const sym = self.undefs.items[index];
807 const sym_name = self.getString(sym.n_strx);
808 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
809
810 log.err("undefined reference to symbol '{s}'", .{sym_name});
811 log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name});
812 }
813 if (self.unresolved.count() > 0) {
814 return error.UndefinedSymbolReference;
815 }
816
817 try self.createTentativeDefAtoms();
818 try self.parseObjectsIntoAtoms();
819 try self.allocateGlobalSymbols();
820 try self.writeAtoms();
821
822 if (self.bss_section_index) |idx| {
823 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
824 const sect = &seg.sections.items[idx];
825 self.bss_file_offset = sect.offset;
826 sect.offset = 0;
827 }
828 if (self.tlv_bss_section_index) |idx| {
829 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
830 const sect = &seg.sections.items[idx];
831 self.tlv_bss_file_offset = sect.offset;
832 sect.offset = 0;
833 }
834
835 try self.flushModule(comp);
948 }836 }
949837
950 if (!self.base.options.disable_lld_caching) {838 cache: {
839 if (use_stage1 and self.base.options.disable_lld_caching) break :cache;
951 // Update the file with the digest. If it fails we can continue; it only840 // Update the file with the digest. If it fails we can continue; it only
952 // means that the next invocation will have an unnecessary cache miss.841 // means that the next invocation will have an unnecessary cache miss.
953 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {842 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
954 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});843 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
955 };844 };
956 // Again failure here only means an unnecessary cache miss.845 // Again failure here only means an unnecessary cache miss.
957 man.writeManifest() catch |err| {846 man.writeManifest() catch |err| {
958 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});847 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
959 };848 };
960 // We hang on to this lock so that the output file path can be used without849 // We hang on to this lock so that the output file path can be used without
961 // other processes clobbering it.850 // other processes clobbering it.
962 self.base.lock = man.toOwnedLock();851 self.base.lock = man.toOwnedLock();
963 }852 }
853
854 self.cold_start = false;
855}
856
857pub fn flushModule(self: *MachO, comp: *Compilation) !void {
858 _ = comp;
859
860 const tracy = trace(@src());
861 defer tracy.end();
862
863 try self.setEntryPoint();
864 try self.updateSectionOrdinals();
865 try self.writeLinkeditSegment();
866
867 if (self.d_sym) |*ds| {
868 // Flush debug symbols bundle.
869 try ds.flushModule(self.base.allocator, self.base.options);
870 }
871
872 if (self.requires_adhoc_codesig) {
873 // Preallocate space for the code signature.
874 // We need to do this at this stage so that we have the load commands with proper values
875 // written out to the file.
876 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
877 // where the code signature goes into.
878 try self.writeCodeSignaturePadding();
879 }
880
881 try self.writeLoadCommands();
882 try self.writeHeader();
883
884 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
885 log.debug("flushing. no_entry_point_found = true", .{});
886 self.error_flags.no_entry_point_found = true;
887 } else {
888 log.debug("flushing. no_entry_point_found = false", .{});
889 self.error_flags.no_entry_point_found = false;
890 }
891
892 assert(!self.load_commands_dirty);
893
894 if (self.requires_adhoc_codesig) {
895 try self.writeCodeSignature(); // code signing always comes last
896 }
897}
898
899fn resolveSearchDir(
900 arena: *Allocator,
901 dir: []const u8,
902 syslibroot: ?[]const u8,
903) !?[]const u8 {
904 var candidates = std.ArrayList([]const u8).init(arena);
905
906 if (fs.path.isAbsolute(dir)) {
907 if (syslibroot) |root| {
908 const common_dir = if (std.Target.current.os.tag == .windows) blk: {
909 // We need to check for disk designator and strip it out from dir path so
910 // that we can concat dir with syslibroot.
911 // TODO we should backport this mechanism to 'MachO.Dylib.parseDependentLibs()'
912 const disk_designator = fs.path.diskDesignatorWindows(dir);
913
914 if (mem.indexOf(u8, dir, disk_designator)) |where| {
915 break :blk dir[where + disk_designator.len ..];
916 }
917
918 break :blk dir;
919 } else dir;
920 const full_path = try fs.path.join(arena, &[_][]const u8{ root, common_dir });
921 try candidates.append(full_path);
922 }
923 }
924
925 try candidates.append(dir);
926
927 for (candidates.items) |candidate| {
928 // Verify that search path actually exists
929 var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
930 error.FileNotFound => continue,
931 else => |e| return e,
932 };
933 defer tmp.close();
934
935 return candidate;
936 }
937
938 return null;
939}
940
941fn resolveLib(
942 arena: *Allocator,
943 search_dirs: []const []const u8,
944 name: []const u8,
945 ext: []const u8,
946) !?[]const u8 {
947 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
948
949 for (search_dirs) |dir| {
950 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
951
952 // Check if the file exists.
953 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
954 error.FileNotFound => continue,
955 else => |e| return e,
956 };
957 defer tmp.close();
958
959 return full_path;
960 }
961
962 return null;
963}
964
965fn resolveFramework(
966 arena: *Allocator,
967 search_dirs: []const []const u8,
968 name: []const u8,
969 ext: []const u8,
970) !?[]const u8 {
971 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
972 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
973
974 for (search_dirs) |dir| {
975 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
976
977 // Check if the file exists.
978 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
979 error.FileNotFound => continue,
980 else => |e| return e,
981 };
982 defer tmp.close();
983
984 return full_path;
985 }
986
987 return null;
964}988}
965989
966fn parseObject(self: *MachO, path: []const u8) !bool {990fn parseObject(self: *MachO, path: []const u8) !bool {
...@@ -1080,6 +1104,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1080,6 +1104,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1080 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);1104 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
10811105
1082 if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {1106 if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {
1107 try self.addLoadDylibLC(dylib_id);
1083 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});1108 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1084 }1109 }
10851110
...@@ -1098,6 +1123,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1098,6 +1123,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1098 break :full_path try self.base.allocator.dupe(u8, path);1123 break :full_path try self.base.allocator.dupe(u8, path);
1099 };1124 };
1100 defer self.base.allocator.free(full_path);1125 defer self.base.allocator.free(full_path);
1126 log.debug("parsing input file path '{s}'", .{full_path});
11011127
1102 if (try self.parseObject(full_path)) continue;1128 if (try self.parseObject(full_path)) continue;
1103 if (try self.parseArchive(full_path)) continue;1129 if (try self.parseArchive(full_path)) continue;
...@@ -1111,6 +1137,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1111,6 +1137,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
11111137
1112fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {1138fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
1113 for (libs) |lib| {1139 for (libs) |lib| {
1140 log.debug("parsing lib path '{s}'", .{lib});
1114 if (try self.parseDylib(lib, .{1141 if (try self.parseDylib(lib, .{
1115 .syslibroot = syslibroot,1142 .syslibroot = syslibroot,
1116 })) continue;1143 })) continue;
...@@ -1126,18 +1153,19 @@ pub const MatchingSection = struct {...@@ -1126,18 +1153,19 @@ pub const MatchingSection = struct {
1126};1153};
11271154
1128pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {1155pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
1129 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1130 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1131 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1132 const segname = commands.segmentName(sect);1156 const segname = commands.segmentName(sect);
1133 const sectname = commands.sectionName(sect);1157 const sectname = commands.sectionName(sect);
1134
1135 const res: ?MatchingSection = blk: {1158 const res: ?MatchingSection = blk: {
1136 switch (commands.sectionType(sect)) {1159 switch (commands.sectionType(sect)) {
1137 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {1160 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
1138 if (self.text_const_section_index == null) {1161 if (self.text_const_section_index == null) {
1139 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);1162 self.text_const_section_index = try self.allocateSection(
1140 try text_seg.addSection(self.base.allocator, "__const", .{});1163 self.text_segment_cmd_index.?,
1164 "__const",
1165 sect.size,
1166 sect.@"align",
1167 .{},
1168 );
1141 }1169 }
11421170
1143 break :blk .{1171 break :blk .{
...@@ -1150,10 +1178,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1150,10 +1178,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1150 // TODO it seems the common values within the sections in objects are deduplicated/merged1178 // TODO it seems the common values within the sections in objects are deduplicated/merged
1151 // on merging the sections' contents.1179 // on merging the sections' contents.
1152 if (self.objc_methname_section_index == null) {1180 if (self.objc_methname_section_index == null) {
1153 self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len);1181 self.objc_methname_section_index = try self.allocateSection(
1154 try text_seg.addSection(self.base.allocator, "__objc_methname", .{1182 self.text_segment_cmd_index.?,
1155 .flags = macho.S_CSTRING_LITERALS,1183 "__objc_methname",
1156 });1184 sect.size,
1185 sect.@"align",
1186 .{},
1187 );
1157 }1188 }
11581189
1159 break :blk .{1190 break :blk .{
...@@ -1162,10 +1193,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1162,10 +1193,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1162 };1193 };
1163 } else if (mem.eql(u8, sectname, "__objc_methtype")) {1194 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
1164 if (self.objc_methtype_section_index == null) {1195 if (self.objc_methtype_section_index == null) {
1165 self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len);1196 self.objc_methtype_section_index = try self.allocateSection(
1166 try text_seg.addSection(self.base.allocator, "__objc_methtype", .{1197 self.text_segment_cmd_index.?,
1167 .flags = macho.S_CSTRING_LITERALS,1198 "__objc_methtype",
1168 });1199 sect.size,
1200 sect.@"align",
1201 .{},
1202 );
1169 }1203 }
11701204
1171 break :blk .{1205 break :blk .{
...@@ -1174,8 +1208,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1174,8 +1208,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1174 };1208 };
1175 } else if (mem.eql(u8, sectname, "__objc_classname")) {1209 } else if (mem.eql(u8, sectname, "__objc_classname")) {
1176 if (self.objc_classname_section_index == null) {1210 if (self.objc_classname_section_index == null) {
1177 self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len);1211 self.objc_classname_section_index = try self.allocateSection(
1178 try text_seg.addSection(self.base.allocator, "__objc_classname", .{});1212 self.text_segment_cmd_index.?,
1213 "__objc_classname",
1214 sect.size,
1215 sect.@"align",
1216 .{},
1217 );
1179 }1218 }
11801219
1181 break :blk .{1220 break :blk .{
...@@ -1185,10 +1224,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1185,10 +1224,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1185 }1224 }
11861225
1187 if (self.cstring_section_index == null) {1226 if (self.cstring_section_index == null) {
1188 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);1227 self.cstring_section_index = try self.allocateSection(
1189 try text_seg.addSection(self.base.allocator, "__cstring", .{1228 self.text_segment_cmd_index.?,
1190 .flags = macho.S_CSTRING_LITERALS,1229 "__cstring",
1191 });1230 sect.size,
1231 sect.@"align",
1232 .{
1233 .flags = macho.S_CSTRING_LITERALS,
1234 },
1235 );
1192 }1236 }
11931237
1194 break :blk .{1238 break :blk .{
...@@ -1199,27 +1243,37 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1199,27 +1243,37 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1199 macho.S_LITERAL_POINTERS => {1243 macho.S_LITERAL_POINTERS => {
1200 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {1244 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
1201 if (self.objc_selrefs_section_index == null) {1245 if (self.objc_selrefs_section_index == null) {
1202 self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len);1246 self.objc_selrefs_section_index = try self.allocateSection(
1203 try data_seg.addSection(self.base.allocator, "__objc_selrefs", .{1247 self.data_segment_cmd_index.?,
1204 .flags = macho.S_LITERAL_POINTERS,1248 "__objc_selrefs",
1205 });1249 sect.size,
1250 sect.@"align",
1251 .{
1252 .flags = macho.S_LITERAL_POINTERS,
1253 },
1254 );
1206 }1255 }
12071256
1208 break :blk .{1257 break :blk .{
1209 .seg = self.data_segment_cmd_index.?,1258 .seg = self.data_segment_cmd_index.?,
1210 .sect = self.objc_selrefs_section_index.?,1259 .sect = self.objc_selrefs_section_index.?,
1211 };1260 };
1261 } else {
1262 // TODO investigate
1263 break :blk null;
1212 }1264 }
1213
1214 // TODO investigate
1215 break :blk null;
1216 },1265 },
1217 macho.S_MOD_INIT_FUNC_POINTERS => {1266 macho.S_MOD_INIT_FUNC_POINTERS => {
1218 if (self.mod_init_func_section_index == null) {1267 if (self.mod_init_func_section_index == null) {
1219 self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);1268 self.mod_init_func_section_index = try self.allocateSection(
1220 try data_const_seg.addSection(self.base.allocator, "__mod_init_func", .{1269 self.data_const_segment_cmd_index.?,
1221 .flags = macho.S_MOD_INIT_FUNC_POINTERS,1270 "__mod_init_func",
1222 });1271 sect.size,
1272 sect.@"align",
1273 .{
1274 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
1275 },
1276 );
1223 }1277 }
12241278
1225 break :blk .{1279 break :blk .{
...@@ -1229,10 +1283,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1229,10 +1283,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1229 },1283 },
1230 macho.S_MOD_TERM_FUNC_POINTERS => {1284 macho.S_MOD_TERM_FUNC_POINTERS => {
1231 if (self.mod_term_func_section_index == null) {1285 if (self.mod_term_func_section_index == null) {
1232 self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);1286 self.mod_term_func_section_index = try self.allocateSection(
1233 try data_const_seg.addSection(self.base.allocator, "__mod_term_func", .{1287 self.data_const_segment_cmd_index.?,
1234 .flags = macho.S_MOD_TERM_FUNC_POINTERS,1288 "__mod_term_func",
1235 });1289 sect.size,
1290 sect.@"align",
1291 .{
1292 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
1293 },
1294 );
1236 }1295 }
12371296
1238 break :blk .{1297 break :blk .{
...@@ -1241,38 +1300,34 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1241,38 +1300,34 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1241 };1300 };
1242 },1301 },
1243 macho.S_ZEROFILL => {1302 macho.S_ZEROFILL => {
1244 if (mem.eql(u8, sectname, "__common")) {1303 if (self.bss_section_index == null) {
1245 if (self.common_section_index == null) {1304 self.bss_section_index = try self.allocateSection(
1246 self.common_section_index = @intCast(u16, data_seg.sections.items.len);1305 self.data_segment_cmd_index.?,
1247 try data_seg.addSection(self.base.allocator, "__common", .{1306 "__bss",
1248 .flags = macho.S_ZEROFILL,1307 sect.size,
1249 });1308 sect.@"align",
1250 }1309 .{
1251
1252 break :blk .{
1253 .seg = self.data_segment_cmd_index.?,
1254 .sect = self.common_section_index.?,
1255 };
1256 } else {
1257 if (self.bss_section_index == null) {
1258 self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
1259 try data_seg.addSection(self.base.allocator, "__bss", .{
1260 .flags = macho.S_ZEROFILL,1310 .flags = macho.S_ZEROFILL,
1261 });1311 },
1262 }1312 );
1263
1264 break :blk .{
1265 .seg = self.data_segment_cmd_index.?,
1266 .sect = self.bss_section_index.?,
1267 };
1268 }1313 }
1314
1315 break :blk .{
1316 .seg = self.data_segment_cmd_index.?,
1317 .sect = self.bss_section_index.?,
1318 };
1269 },1319 },
1270 macho.S_THREAD_LOCAL_VARIABLES => {1320 macho.S_THREAD_LOCAL_VARIABLES => {
1271 if (self.tlv_section_index == null) {1321 if (self.tlv_section_index == null) {
1272 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);1322 self.tlv_section_index = try self.allocateSection(
1273 try data_seg.addSection(self.base.allocator, "__thread_vars", .{1323 self.data_segment_cmd_index.?,
1274 .flags = macho.S_THREAD_LOCAL_VARIABLES,1324 "__thread_vars",
1275 });1325 sect.size,
1326 sect.@"align",
1327 .{
1328 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1329 },
1330 );
1276 }1331 }
12771332
1278 break :blk .{1333 break :blk .{
...@@ -1282,10 +1337,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1282,10 +1337,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1282 },1337 },
1283 macho.S_THREAD_LOCAL_REGULAR => {1338 macho.S_THREAD_LOCAL_REGULAR => {
1284 if (self.tlv_data_section_index == null) {1339 if (self.tlv_data_section_index == null) {
1285 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);1340 self.tlv_data_section_index = try self.allocateSection(
1286 try data_seg.addSection(self.base.allocator, "__thread_data", .{1341 self.data_segment_cmd_index.?,
1287 .flags = macho.S_THREAD_LOCAL_REGULAR,1342 "__thread_data",
1288 });1343 sect.size,
1344 sect.@"align",
1345 .{
1346 .flags = macho.S_THREAD_LOCAL_REGULAR,
1347 },
1348 );
1289 }1349 }
12901350
1291 break :blk .{1351 break :blk .{
...@@ -1295,10 +1355,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1295,10 +1355,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1295 },1355 },
1296 macho.S_THREAD_LOCAL_ZEROFILL => {1356 macho.S_THREAD_LOCAL_ZEROFILL => {
1297 if (self.tlv_bss_section_index == null) {1357 if (self.tlv_bss_section_index == null) {
1298 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);1358 self.tlv_bss_section_index = try self.allocateSection(
1299 try data_seg.addSection(self.base.allocator, "__thread_bss", .{1359 self.data_segment_cmd_index.?,
1300 .flags = macho.S_THREAD_LOCAL_ZEROFILL,1360 "__thread_bss",
1301 });1361 sect.size,
1362 sect.@"align",
1363 .{
1364 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
1365 },
1366 );
1302 }1367 }
13031368
1304 break :blk .{1369 break :blk .{
...@@ -1311,8 +1376,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1311,8 +1376,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1311 // TODO I believe __eh_frame is currently part of __unwind_info section1376 // TODO I believe __eh_frame is currently part of __unwind_info section
1312 // in the latest ld64 output.1377 // in the latest ld64 output.
1313 if (self.eh_frame_section_index == null) {1378 if (self.eh_frame_section_index == null) {
1314 self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len);1379 self.eh_frame_section_index = try self.allocateSection(
1315 try text_seg.addSection(self.base.allocator, "__eh_frame", .{});1380 self.text_segment_cmd_index.?,
1381 "__eh_frame",
1382 sect.size,
1383 sect.@"align",
1384 .{},
1385 );
1316 }1386 }
13171387
1318 break :blk .{1388 break :blk .{
...@@ -1323,8 +1393,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1323,8 +1393,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13231393
1324 // TODO audit this: is this the right mapping?1394 // TODO audit this: is this the right mapping?
1325 if (self.data_const_section_index == null) {1395 if (self.data_const_section_index == null) {
1326 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);1396 self.data_const_section_index = try self.allocateSection(
1327 try data_const_seg.addSection(self.base.allocator, "__const", .{});1397 self.data_const_segment_cmd_index.?,
1398 "__const",
1399 sect.size,
1400 sect.@"align",
1401 .{},
1402 );
1328 }1403 }
13291404
1330 break :blk .{1405 break :blk .{
...@@ -1335,10 +1410,17 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1335,10 +1410,17 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1335 macho.S_REGULAR => {1410 macho.S_REGULAR => {
1336 if (commands.sectionIsCode(sect)) {1411 if (commands.sectionIsCode(sect)) {
1337 if (self.text_section_index == null) {1412 if (self.text_section_index == null) {
1338 self.text_section_index = @intCast(u16, text_seg.sections.items.len);1413 self.text_section_index = try self.allocateSection(
1339 try text_seg.addSection(self.base.allocator, "__text", .{1414 self.text_segment_cmd_index.?,
1340 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,1415 "__text",
1341 });1416 sect.size,
1417 sect.@"align",
1418 .{
1419 .flags = macho.S_REGULAR |
1420 macho.S_ATTR_PURE_INSTRUCTIONS |
1421 macho.S_ATTR_SOME_INSTRUCTIONS,
1422 },
1423 );
1342 }1424 }
13431425
1344 break :blk .{1426 break :blk .{
...@@ -1359,8 +1441,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1359,8 +1441,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1359 if (mem.eql(u8, segname, "__TEXT")) {1441 if (mem.eql(u8, segname, "__TEXT")) {
1360 if (mem.eql(u8, sectname, "__ustring")) {1442 if (mem.eql(u8, sectname, "__ustring")) {
1361 if (self.ustring_section_index == null) {1443 if (self.ustring_section_index == null) {
1362 self.ustring_section_index = @intCast(u16, text_seg.sections.items.len);1444 self.ustring_section_index = try self.allocateSection(
1363 try text_seg.addSection(self.base.allocator, "__ustring", .{});1445 self.text_segment_cmd_index.?,
1446 "__ustring",
1447 sect.size,
1448 sect.@"align",
1449 .{},
1450 );
1364 }1451 }
13651452
1366 break :blk .{1453 break :blk .{
...@@ -1369,8 +1456,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1369,8 +1456,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1369 };1456 };
1370 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {1457 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
1371 if (self.gcc_except_tab_section_index == null) {1458 if (self.gcc_except_tab_section_index == null) {
1372 self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len);1459 self.gcc_except_tab_section_index = try self.allocateSection(
1373 try text_seg.addSection(self.base.allocator, "__gcc_except_tab", .{});1460 self.text_segment_cmd_index.?,
1461 "__gcc_except_tab",
1462 sect.size,
1463 sect.@"align",
1464 .{},
1465 );
1374 }1466 }
13751467
1376 break :blk .{1468 break :blk .{
...@@ -1379,8 +1471,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1379,8 +1471,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1379 };1471 };
1380 } else if (mem.eql(u8, sectname, "__objc_methlist")) {1472 } else if (mem.eql(u8, sectname, "__objc_methlist")) {
1381 if (self.objc_methlist_section_index == null) {1473 if (self.objc_methlist_section_index == null) {
1382 self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len);1474 self.objc_methlist_section_index = try self.allocateSection(
1383 try text_seg.addSection(self.base.allocator, "__objc_methlist", .{});1475 self.text_segment_cmd_index.?,
1476 "__objc_methlist",
1477 sect.size,
1478 sect.@"align",
1479 .{},
1480 );
1384 }1481 }
13851482
1386 break :blk .{1483 break :blk .{
...@@ -1394,8 +1491,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1394,8 +1491,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1394 mem.eql(u8, sectname, "__gopclntab"))1491 mem.eql(u8, sectname, "__gopclntab"))
1395 {1492 {
1396 if (self.data_const_section_index == null) {1493 if (self.data_const_section_index == null) {
1397 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);1494 self.data_const_section_index = try self.allocateSection(
1398 try data_const_seg.addSection(self.base.allocator, "__const", .{});1495 self.data_const_segment_cmd_index.?,
1496 "__const",
1497 sect.size,
1498 sect.@"align",
1499 .{},
1500 );
1399 }1501 }
14001502
1401 break :blk .{1503 break :blk .{
...@@ -1404,8 +1506,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1404,8 +1506,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1404 };1506 };
1405 } else {1507 } else {
1406 if (self.text_const_section_index == null) {1508 if (self.text_const_section_index == null) {
1407 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);1509 self.text_const_section_index = try self.allocateSection(
1408 try text_seg.addSection(self.base.allocator, "__const", .{});1510 self.text_segment_cmd_index.?,
1511 "__const",
1512 sect.size,
1513 sect.@"align",
1514 .{},
1515 );
1409 }1516 }
14101517
1411 break :blk .{1518 break :blk .{
...@@ -1417,8 +1524,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1417,8 +1524,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14171524
1418 if (mem.eql(u8, segname, "__DATA_CONST")) {1525 if (mem.eql(u8, segname, "__DATA_CONST")) {
1419 if (self.data_const_section_index == null) {1526 if (self.data_const_section_index == null) {
1420 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);1527 self.data_const_section_index = try self.allocateSection(
1421 try data_const_seg.addSection(self.base.allocator, "__const", .{});1528 self.data_const_segment_cmd_index.?,
1529 "__const",
1530 sect.size,
1531 sect.@"align",
1532 .{},
1533 );
1422 }1534 }
14231535
1424 break :blk .{1536 break :blk .{
...@@ -1430,8 +1542,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1430,8 +1542,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1430 if (mem.eql(u8, segname, "__DATA")) {1542 if (mem.eql(u8, segname, "__DATA")) {
1431 if (mem.eql(u8, sectname, "__const")) {1543 if (mem.eql(u8, sectname, "__const")) {
1432 if (self.data_const_section_index == null) {1544 if (self.data_const_section_index == null) {
1433 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);1545 self.data_const_section_index = try self.allocateSection(
1434 try data_const_seg.addSection(self.base.allocator, "__const", .{});1546 self.data_const_segment_cmd_index.?,
1547 "__const",
1548 sect.size,
1549 sect.@"align",
1550 .{},
1551 );
1435 }1552 }
14361553
1437 break :blk .{1554 break :blk .{
...@@ -1440,8 +1557,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1440,8 +1557,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1440 };1557 };
1441 } else if (mem.eql(u8, sectname, "__cfstring")) {1558 } else if (mem.eql(u8, sectname, "__cfstring")) {
1442 if (self.objc_cfstring_section_index == null) {1559 if (self.objc_cfstring_section_index == null) {
1443 self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len);1560 self.objc_cfstring_section_index = try self.allocateSection(
1444 try data_const_seg.addSection(self.base.allocator, "__cfstring", .{});1561 self.data_const_segment_cmd_index.?,
1562 "__cfstring",
1563 sect.size,
1564 sect.@"align",
1565 .{},
1566 );
1445 }1567 }
14461568
1447 break :blk .{1569 break :blk .{
...@@ -1450,8 +1572,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1450,8 +1572,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1450 };1572 };
1451 } else if (mem.eql(u8, sectname, "__objc_classlist")) {1573 } else if (mem.eql(u8, sectname, "__objc_classlist")) {
1452 if (self.objc_classlist_section_index == null) {1574 if (self.objc_classlist_section_index == null) {
1453 self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len);1575 self.objc_classlist_section_index = try self.allocateSection(
1454 try data_const_seg.addSection(self.base.allocator, "__objc_classlist", .{});1576 self.data_const_segment_cmd_index.?,
1577 "__objc_classlist",
1578 sect.size,
1579 sect.@"align",
1580 .{},
1581 );
1455 }1582 }
14561583
1457 break :blk .{1584 break :blk .{
...@@ -1460,8 +1587,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1460,8 +1587,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1460 };1587 };
1461 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {1588 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
1462 if (self.objc_imageinfo_section_index == null) {1589 if (self.objc_imageinfo_section_index == null) {
1463 self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len);1590 self.objc_imageinfo_section_index = try self.allocateSection(
1464 try data_const_seg.addSection(self.base.allocator, "__objc_imageinfo", .{});1591 self.data_const_segment_cmd_index.?,
1592 "__objc_imageinfo",
1593 sect.size,
1594 sect.@"align",
1595 .{},
1596 );
1465 }1597 }
14661598
1467 break :blk .{1599 break :blk .{
...@@ -1470,8 +1602,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1470,8 +1602,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1470 };1602 };
1471 } else if (mem.eql(u8, sectname, "__objc_const")) {1603 } else if (mem.eql(u8, sectname, "__objc_const")) {
1472 if (self.objc_const_section_index == null) {1604 if (self.objc_const_section_index == null) {
1473 self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len);1605 self.objc_const_section_index = try self.allocateSection(
1474 try data_seg.addSection(self.base.allocator, "__objc_const", .{});1606 self.data_segment_cmd_index.?,
1607 "__objc_const",
1608 sect.size,
1609 sect.@"align",
1610 .{},
1611 );
1475 }1612 }
14761613
1477 break :blk .{1614 break :blk .{
...@@ -1480,8 +1617,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1480,8 +1617,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1480 };1617 };
1481 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {1618 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
1482 if (self.objc_classrefs_section_index == null) {1619 if (self.objc_classrefs_section_index == null) {
1483 self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len);1620 self.objc_classrefs_section_index = try self.allocateSection(
1484 try data_seg.addSection(self.base.allocator, "__objc_classrefs", .{});1621 self.data_segment_cmd_index.?,
1622 "__objc_classrefs",
1623 sect.size,
1624 sect.@"align",
1625 .{},
1626 );
1485 }1627 }
14861628
1487 break :blk .{1629 break :blk .{
...@@ -1490,8 +1632,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1490,8 +1632,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1490 };1632 };
1491 } else if (mem.eql(u8, sectname, "__objc_data")) {1633 } else if (mem.eql(u8, sectname, "__objc_data")) {
1492 if (self.objc_data_section_index == null) {1634 if (self.objc_data_section_index == null) {
1493 self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len);1635 self.objc_data_section_index = try self.allocateSection(
1494 try data_seg.addSection(self.base.allocator, "__objc_data", .{});1636 self.data_segment_cmd_index.?,
1637 "__objc_data",
1638 sect.size,
1639 sect.@"align",
1640 .{},
1641 );
1495 }1642 }
14961643
1497 break :blk .{1644 break :blk .{
...@@ -1500,8 +1647,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1500,8 +1647,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1500 };1647 };
1501 } else {1648 } else {
1502 if (self.data_section_index == null) {1649 if (self.data_section_index == null) {
1503 self.data_section_index = @intCast(u16, data_seg.sections.items.len);1650 self.data_section_index = try self.allocateSection(
1504 try data_seg.addSection(self.base.allocator, "__data", .{});1651 self.data_segment_cmd_index.?,
1652 "__data",
1653 sect.size,
1654 sect.@"align",
1655 .{},
1656 );
1505 }1657 }
15061658
1507 break :blk .{1659 break :blk .{
...@@ -1522,563 +1674,605 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1522,563 +1674,605 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1522 else => break :blk null,1674 else => break :blk null,
1523 }1675 }
1524 };1676 };
1525
1526 if (res) |match| {
1527 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
1528 }
1529
1530 return res;1677 return res;
1531}1678}
15321679
1533fn sortSections(self: *MachO) !void {1680pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*Atom {
1534 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);1681 const code = try self.base.allocator.alloc(u8, size);
1535 defer text_index_mapping.deinit();1682 defer self.base.allocator.free(code);
1536 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);1683 mem.set(u8, code, 0);
1537 defer data_const_index_mapping.deinit();1684
1538 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);1685 const atom = try self.base.allocator.create(Atom);
1539 defer data_index_mapping.deinit();1686 errdefer self.base.allocator.destroy(atom);
15401687 atom.* = Atom.empty;
1541 {1688 atom.local_sym_index = local_sym_index;
1542 // __TEXT segment1689 atom.size = size;
1543 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1690 atom.alignment = alignment;
1544 var sections = seg.sections.toOwnedSlice(self.base.allocator);1691 try atom.code.appendSlice(self.base.allocator, code);
1545 defer self.base.allocator.free(sections);1692 try self.managed_atoms.append(self.base.allocator, atom);
1546 try seg.sections.ensureCapacity(self.base.allocator, sections.len);1693
15471694 return atom;
1548 const indices = &[_]*?u16{1695}
1549 &self.text_section_index,
1550 &self.stubs_section_index,
1551 &self.stub_helper_section_index,
1552 &self.gcc_except_tab_section_index,
1553 &self.cstring_section_index,
1554 &self.ustring_section_index,
1555 &self.text_const_section_index,
1556 &self.objc_methlist_section_index,
1557 &self.objc_methname_section_index,
1558 &self.objc_methtype_section_index,
1559 &self.objc_classname_section_index,
1560 &self.eh_frame_section_index,
1561 };
1562 for (indices) |maybe_index| {
1563 const new_index: u16 = if (maybe_index.*) |index| blk: {
1564 const idx = @intCast(u16, seg.sections.items.len);
1565 seg.sections.appendAssumeCapacity(sections[index]);
1566 try text_index_mapping.putNoClobber(index, idx);
1567 break :blk idx;
1568 } else continue;
1569 maybe_index.* = new_index;
1570 }
1571 }
15721696
1573 {1697pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
1574 // __DATA_CONST segment1698 const seg = self.load_commands.items[match.seg].Segment;
1575 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1699 const sect = seg.sections.items[match.sect];
1576 var sections = seg.sections.toOwnedSlice(self.base.allocator);1700 const sym = self.locals.items[atom.local_sym_index];
1577 defer self.base.allocator.free(sections);1701 const file_offset = sect.offset + sym.n_value - sect.addr;
1578 try seg.sections.ensureCapacity(self.base.allocator, sections.len);1702 try atom.resolveRelocs(self);
15791703 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
1580 const indices = &[_]*?u16{1704 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1581 &self.got_section_index,1705}
1582 &self.mod_init_func_section_index,
1583 &self.mod_term_func_section_index,
1584 &self.data_const_section_index,
1585 &self.objc_cfstring_section_index,
1586 &self.objc_classlist_section_index,
1587 &self.objc_imageinfo_section_index,
1588 };
1589 for (indices) |maybe_index| {
1590 const new_index: u16 = if (maybe_index.*) |index| blk: {
1591 const idx = @intCast(u16, seg.sections.items.len);
1592 seg.sections.appendAssumeCapacity(sections[index]);
1593 try data_const_index_mapping.putNoClobber(index, idx);
1594 break :blk idx;
1595 } else continue;
1596 maybe_index.* = new_index;
1597 }
1598 }
15991706
1600 {1707fn allocateLocalSymbols(self: *MachO, match: MatchingSection, offset: i64) !void {
1601 // __DATA segment1708 var atom = self.atoms.get(match) orelse return;
1602 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1603 var sections = seg.sections.toOwnedSlice(self.base.allocator);
1604 defer self.base.allocator.free(sections);
1605 try seg.sections.ensureCapacity(self.base.allocator, sections.len);
1606
1607 // __DATA segment
1608 const indices = &[_]*?u16{
1609 &self.la_symbol_ptr_section_index,
1610 &self.objc_const_section_index,
1611 &self.objc_selrefs_section_index,
1612 &self.objc_classrefs_section_index,
1613 &self.objc_data_section_index,
1614 &self.data_section_index,
1615 &self.tlv_section_index,
1616 &self.tlv_data_section_index,
1617 &self.tlv_bss_section_index,
1618 &self.bss_section_index,
1619 &self.common_section_index,
1620 };
1621 for (indices) |maybe_index| {
1622 const new_index: u16 = if (maybe_index.*) |index| blk: {
1623 const idx = @intCast(u16, seg.sections.items.len);
1624 seg.sections.appendAssumeCapacity(sections[index]);
1625 try data_index_mapping.putNoClobber(index, idx);
1626 break :blk idx;
1627 } else continue;
1628 maybe_index.* = new_index;
1629 }
1630 }
16311709
1632 {1710 while (true) {
1633 var transient: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{};1711 const atom_sym = &self.locals.items[atom.local_sym_index];
1634 try transient.ensureCapacity(self.base.allocator, self.blocks.count());1712 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
16351713
1636 var it = self.blocks.iterator();1714 for (atom.aliases.items) |index| {
1637 while (it.next()) |entry| {1715 const alias_sym = &self.locals.items[index];
1638 const old = entry.key_ptr.*;1716 alias_sym.n_value = @intCast(u64, @intCast(i64, alias_sym.n_value) + offset);
1639 const sect = if (old.seg == self.text_segment_cmd_index.?)
1640 text_index_mapping.get(old.sect).?
1641 else if (old.seg == self.data_const_segment_cmd_index.?)
1642 data_const_index_mapping.get(old.sect).?
1643 else
1644 data_index_mapping.get(old.sect).?;
1645 transient.putAssumeCapacityNoClobber(.{
1646 .seg = old.seg,
1647 .sect = sect,
1648 }, entry.value_ptr.*);
1649 }1717 }
16501718
1651 self.blocks.clearAndFree(self.base.allocator);1719 for (atom.contained.items) |sym_at_off| {
1652 self.blocks.deinit(self.base.allocator);1720 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
1653 self.blocks = transient;1721 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
1654 }
1655
1656 {
1657 // Create new section ordinals.
1658 self.section_ordinals.clearRetainingCapacity();
1659 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1660 for (text_seg.sections.items) |_, sect_id| {
1661 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
1662 .seg = self.text_segment_cmd_index.?,
1663 .sect = @intCast(u16, sect_id),
1664 });
1665 assert(!res.found_existing);
1666 }
1667 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1668 for (data_const_seg.sections.items) |_, sect_id| {
1669 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
1670 .seg = self.data_const_segment_cmd_index.?,
1671 .sect = @intCast(u16, sect_id),
1672 });
1673 assert(!res.found_existing);
1674 }
1675 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1676 for (data_seg.sections.items) |_, sect_id| {
1677 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
1678 .seg = self.data_segment_cmd_index.?,
1679 .sect = @intCast(u16, sect_id),
1680 });
1681 assert(!res.found_existing);
1682 }1722 }
1723
1724 if (atom.prev) |prev| {
1725 atom = prev;
1726 } else break;
1683 }1727 }
1684}1728}
16851729
1686fn allocateTextSegment(self: *MachO) !void {1730fn allocateGlobalSymbols(self: *MachO) !void {
1687 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1731 var sym_it = self.symbol_resolver.valueIterator();
1688 const nstubs = @intCast(u32, self.stubs.items.len);1732 while (sym_it.next()) |resolv| {
1733 if (resolv.where != .global) continue;
16891734
1690 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;1735 assert(resolv.local_sym_index != 0);
1691 seg.inner.fileoff = 0;1736 const local_sym = self.locals.items[resolv.local_sym_index];
1692 seg.inner.vmaddr = base_vmaddr;1737 const sym = &self.globals.items[resolv.where_index];
1738 sym.n_value = local_sym.n_value;
1739 sym.n_sect = local_sym.n_sect;
1740 log.debug("allocating global symbol {s} at 0x{x}", .{ self.getString(sym.n_strx), local_sym.n_value });
1741 }
1742}
16931743
1694 // Set stubs and stub_helper sizes1744fn writeAtoms(self: *MachO) !void {
1695 const stubs = &seg.sections.items[self.stubs_section_index.?];1745 var buffer = std.ArrayList(u8).init(self.base.allocator);
1696 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];1746 defer buffer.deinit();
1697 stubs.size += nstubs * stubs.reserved2;1747 var file_offset: ?u64 = null;
16981748
1699 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {1749 var it = self.atoms.iterator();
1700 .x86_64 => 10,1750 while (it.next()) |entry| {
1701 .aarch64 => 3 * @sizeOf(u32),1751 const match = entry.key_ptr.*;
1702 else => unreachable,1752 const seg = self.load_commands.items[match.seg].Segment;
1703 };1753 const sect = seg.sections.items[match.sect];
1704 stub_helper.size += nstubs * stub_size;1754 var atom: *Atom = entry.value_ptr.*;
17051755
1706 var sizeofcmds: u64 = 0;1756 log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
1707 for (self.load_commands.items) |lc| {
1708 sizeofcmds += lc.cmdsize();
1709 }
17101757
1711 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);1758 while (atom.prev) |prev| {
1759 atom = prev;
1760 }
17121761
1713 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.1762 while (true) {
1714 var min_alignment: u32 = 0;1763 if (atom.dirty) {
1715 for (seg.sections.items) |sect| {1764 const atom_sym = self.locals.items[atom.local_sym_index];
1716 const alignment = try math.powi(u32, 2, sect.@"align");1765 const padding_size: u64 = if (atom.next) |next| blk: {
1717 min_alignment = math.max(min_alignment, alignment);1766 const next_sym = self.locals.items[next.local_sym_index];
1718 }1767 break :blk next_sym.n_value - (atom_sym.n_value + atom.size);
1768 } else 0;
1769
1770 log.debug(" (adding atom {s} to buffer: {})", .{ self.getString(atom_sym.n_strx), atom_sym });
1771
1772 try atom.resolveRelocs(self);
1773 try buffer.appendSlice(atom.code.items);
1774 try buffer.ensureUnusedCapacity(padding_size);
1775
1776 var i: usize = 0;
1777 while (i < padding_size) : (i += 1) {
1778 buffer.appendAssumeCapacity(0);
1779 }
17191780
1720 assert(min_alignment > 0);1781 if (file_offset == null) {
1721 const last_sect_idx = seg.sections.items.len - 1;1782 file_offset = sect.offset + atom_sym.n_value - sect.addr;
1722 const last_sect = seg.sections.items[last_sect_idx];1783 }
1723 const shift: u32 = blk: {1784 atom.dirty = false;
1724 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;1785 } else {
1725 const factor = @divTrunc(diff, min_alignment);1786 if (file_offset) |off| {
1726 break :blk @intCast(u32, factor * min_alignment);1787 try self.base.file.?.pwriteAll(buffer.items, off);
1727 };1788 }
1789 file_offset = null;
1790 buffer.clearRetainingCapacity();
1791 }
17281792
1729 if (shift > 0) {1793 if (atom.next) |next| {
1730 for (seg.sections.items) |*sect| {1794 atom = next;
1731 sect.offset += shift;1795 } else {
1732 sect.addr += shift;1796 if (file_offset) |off| {
1797 try self.base.file.?.pwriteAll(buffer.items, off);
1798 }
1799 file_offset = null;
1800 buffer.clearRetainingCapacity();
1801 break;
1802 }
1733 }1803 }
1734 }1804 }
1735}1805}
17361806
1737fn allocateDataConstSegment(self: *MachO) !void {1807pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
1738 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1808 const local_sym_index = @intCast(u32, self.locals.items.len);
1739 const nentries = @intCast(u32, self.got_entries.items.len);1809 try self.locals.append(self.base.allocator, .{
17401810 .n_strx = try self.makeString("l_zld_got_entry"),
1741 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;1811 .n_type = macho.N_SECT,
1742 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;1812 .n_sect = 0,
1743 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;1813 .n_desc = 0,
1814 .n_value = 0,
1815 });
1816 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
1817 switch (key.where) {
1818 .local => {
1819 try atom.relocs.append(self.base.allocator, .{
1820 .offset = 0,
1821 .where = .local,
1822 .where_index = key.where_index,
1823 .payload = .{
1824 .unsigned = .{
1825 .subtractor = null,
1826 .addend = 0,
1827 .is_64bit = true,
1828 },
1829 },
1830 });
1831 try atom.rebases.append(self.base.allocator, 0);
1832 },
1833 .undef => {
1834 try atom.bindings.append(self.base.allocator, .{
1835 .local_sym_index = key.where_index,
1836 .offset = 0,
1837 });
1838 },
1839 }
1840 return atom;
1841}
17441842
1745 // Set got size1843fn createDyldPrivateAtom(self: *MachO) !void {
1746 const got = &seg.sections.items[self.got_section_index.?];1844 if (self.dyld_private_atom != null) return;
1747 got.size += nentries * @sizeOf(u64);1845 const local_sym_index = @intCast(u32, self.locals.items.len);
17481846 const sym = try self.locals.addOne(self.base.allocator);
1749 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);1847 sym.* = .{
1750}1848 .n_strx = try self.makeString("l_zld_dyld_private"),
17511849 .n_type = macho.N_SECT,
1752fn allocateDataSegment(self: *MachO) !void {1850 .n_sect = 0,
1753 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;1851 .n_desc = 0,
1754 const nstubs = @intCast(u32, self.stubs.items.len);1852 .n_value = 0,
17551853 };
1756 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1854 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
1757 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;1855 self.dyld_private_atom = atom;
1758 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;1856 const match = MatchingSection{
17591857 .seg = self.data_segment_cmd_index.?,
1760 // Set la_symbol_ptr and data size1858 .sect = self.data_section_index.?,
1761 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];1859 };
1762 const data = &seg.sections.items[self.data_section_index.?];1860 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
1763 la_symbol_ptr.size += nstubs * @sizeOf(u64);1861 sym.n_value = vaddr;
1764 data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder1862 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
17651863 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
1766 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
1767}1864}
17681865
1769fn allocateLinkeditSegment(self: *MachO) void {1866fn createStubHelperPreambleAtom(self: *MachO) !void {
1770 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;1867 if (self.stub_helper_preamble_atom != null) return;
1771 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;1868 const arch = self.base.options.target.cpu.arch;
1772 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;1869 const size: u64 = switch (arch) {
1773 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;1870 .x86_64 => 15,
1871 .aarch64 => 6 * @sizeOf(u32),
1872 else => unreachable,
1873 };
1874 const alignment: u32 = switch (arch) {
1875 .x86_64 => 0,
1876 .aarch64 => 2,
1877 else => unreachable,
1878 };
1879 const local_sym_index = @intCast(u32, self.locals.items.len);
1880 const sym = try self.locals.addOne(self.base.allocator);
1881 sym.* = .{
1882 .n_strx = try self.makeString("l_zld_stub_preamble"),
1883 .n_type = macho.N_SECT,
1884 .n_sect = 0,
1885 .n_desc = 0,
1886 .n_value = 0,
1887 };
1888 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);
1889 const dyld_private_sym_index = self.dyld_private_atom.?.local_sym_index;
1890 switch (arch) {
1891 .x86_64 => {
1892 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
1893 // lea %r11, [rip + disp]
1894 atom.code.items[0] = 0x4c;
1895 atom.code.items[1] = 0x8d;
1896 atom.code.items[2] = 0x1d;
1897 atom.relocs.appendAssumeCapacity(.{
1898 .offset = 3,
1899 .where = .local,
1900 .where_index = dyld_private_sym_index,
1901 .payload = .{
1902 .signed = .{
1903 .addend = 0,
1904 .correction = 0,
1905 },
1906 },
1907 });
1908 // push %r11
1909 atom.code.items[7] = 0x41;
1910 atom.code.items[8] = 0x53;
1911 // jmp [rip + disp]
1912 atom.code.items[9] = 0xff;
1913 atom.code.items[10] = 0x25;
1914 atom.relocs.appendAssumeCapacity(.{
1915 .offset = 11,
1916 .where = .undef,
1917 .where_index = self.dyld_stub_binder_index.?,
1918 .payload = .{
1919 .load = .{
1920 .kind = .got,
1921 .addend = 0,
1922 },
1923 },
1924 });
1925 },
1926 .aarch64 => {
1927 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 4);
1928 // adrp x17, 0
1929 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
1930 atom.relocs.appendAssumeCapacity(.{
1931 .offset = 0,
1932 .where = .local,
1933 .where_index = dyld_private_sym_index,
1934 .payload = .{
1935 .page = .{
1936 .kind = .page,
1937 .addend = 0,
1938 },
1939 },
1940 });
1941 // add x17, x17, 0
1942 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
1943 atom.relocs.appendAssumeCapacity(.{
1944 .offset = 4,
1945 .where = .local,
1946 .where_index = dyld_private_sym_index,
1947 .payload = .{
1948 .page_off = .{
1949 .kind = .page,
1950 .addend = 0,
1951 .op_kind = .arithmetic,
1952 },
1953 },
1954 });
1955 // stp x16, x17, [sp, #-16]!
1956 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(
1957 .x16,
1958 .x17,
1959 aarch64.Register.sp,
1960 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
1961 ).toU32());
1962 // adrp x16, 0
1963 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1964 atom.relocs.appendAssumeCapacity(.{
1965 .offset = 12,
1966 .where = .undef,
1967 .where_index = self.dyld_stub_binder_index.?,
1968 .payload = .{
1969 .page = .{
1970 .kind = .got,
1971 .addend = 0,
1972 },
1973 },
1974 });
1975 // ldr x16, [x16, 0]
1976 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(.x16, .{
1977 .register = .{
1978 .rn = .x16,
1979 .offset = aarch64.Instruction.LoadStoreOffset.imm(0),
1980 },
1981 }).toU32());
1982 atom.relocs.appendAssumeCapacity(.{
1983 .offset = 16,
1984 .where = .undef,
1985 .where_index = self.dyld_stub_binder_index.?,
1986 .payload = .{
1987 .page_off = .{
1988 .kind = .got,
1989 .addend = 0,
1990 },
1991 },
1992 });
1993 // br x16
1994 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1995 },
1996 else => unreachable,
1997 }
1998 self.stub_helper_preamble_atom = atom;
1999 const match = MatchingSection{
2000 .seg = self.text_segment_cmd_index.?,
2001 .sect = self.stub_helper_section_index.?,
2002 };
2003 const alignment_pow_2 = try math.powi(u32, 2, atom.alignment);
2004 const vaddr = try self.allocateAtom(atom, atom.size, alignment_pow_2, match);
2005 sym.n_value = vaddr;
2006 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2007 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
1774}2008}
17752009
1776fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {2010pub fn createStubHelperAtom(self: *MachO) !*Atom {
1777 const seg = &self.load_commands.items[index].Segment;2011 const arch = self.base.options.target.cpu.arch;
2012 const stub_size: u4 = switch (arch) {
2013 .x86_64 => 10,
2014 .aarch64 => 3 * @sizeOf(u32),
2015 else => unreachable,
2016 };
2017 const alignment: u2 = switch (arch) {
2018 .x86_64 => 0,
2019 .aarch64 => 2,
2020 else => unreachable,
2021 };
2022 const local_sym_index = @intCast(u32, self.locals.items.len);
2023 try self.locals.append(self.base.allocator, .{
2024 .n_strx = try self.makeString("l_zld_stub_in_stub_helper"),
2025 .n_type = macho.N_SECT,
2026 .n_sect = 0,
2027 .n_desc = 0,
2028 .n_value = 0,
2029 });
2030 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);
2031 try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);
17782032
1779 // Allocate the sections according to their alignment at the beginning of the segment.2033 switch (arch) {
1780 var start: u64 = offset;2034 .x86_64 => {
1781 for (seg.sections.items) |*sect| {2035 // pushq
1782 const alignment = try math.powi(u32, 2, sect.@"align");2036 atom.code.items[0] = 0x68;
1783 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);2037 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1784 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);2038 // jmpq
1785 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);2039 atom.code.items[5] = 0xe9;
1786 sect.addr = seg.inner.vmaddr + start_aligned;2040 atom.relocs.appendAssumeCapacity(.{
1787 start = end_aligned;2041 .offset = 6,
2042 .where = .local,
2043 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,
2044 .payload = .{
2045 .branch = .{ .arch = arch },
2046 },
2047 });
2048 },
2049 .aarch64 => {
2050 const literal = blk: {
2051 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
2052 break :blk try math.cast(u18, div_res);
2053 };
2054 // ldr w16, literal
2055 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldr(.w16, .{
2056 .literal = literal,
2057 }).toU32());
2058 // b disp
2059 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2060 atom.relocs.appendAssumeCapacity(.{
2061 .offset = 4,
2062 .where = .local,
2063 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,
2064 .payload = .{
2065 .branch = .{ .arch = arch },
2066 },
2067 });
2068 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2069 },
2070 else => unreachable,
1788 }2071 }
17892072
1790 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size);2073 return atom;
1791 seg.inner.filesize = seg_size_aligned;
1792 seg.inner.vmsize = seg_size_aligned;
1793}2074}
17942075
1795fn allocateTextBlocks(self: *MachO) !void {2076pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {
1796 var it = self.blocks.iterator();2077 const local_sym_index = @intCast(u32, self.locals.items.len);
1797 while (it.next()) |entry| {2078 try self.locals.append(self.base.allocator, .{
1798 const match = entry.key_ptr.*;2079 .n_strx = try self.makeString("l_zld_lazy_ptr"),
1799 var block: *TextBlock = entry.value_ptr.*;2080 .n_type = macho.N_SECT,
18002081 .n_sect = 0,
1801 // Find the first block2082 .n_desc = 0,
1802 while (block.prev) |prev| {2083 .n_value = 0,
1803 block = prev;2084 });
1804 }2085 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
18052086 try atom.relocs.append(self.base.allocator, .{
1806 const seg = self.load_commands.items[match.seg].Segment;2087 .offset = 0,
1807 const sect = seg.sections.items[match.sect];2088 .where = .local,
18082089 .where_index = stub_sym_index,
1809 var base_addr: u64 = sect.addr;2090 .payload = .{
1810 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2091 .unsigned = .{
18112092 .subtractor = null,
1812 log.debug(" within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });2093 .addend = 0,
1813 log.debug(" {}", .{sect});2094 .is_64bit = true,
18142095 },
1815 while (true) {2096 },
1816 const block_alignment = try math.powi(u32, 2, block.alignment);2097 });
1817 base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment);2098 try atom.rebases.append(self.base.allocator, 0);
18182099 try atom.lazy_bindings.append(self.base.allocator, .{
1819 const sym = &self.locals.items[block.local_sym_index];2100 .local_sym_index = lazy_binding_sym_index,
1820 sym.n_value = base_addr;2101 .offset = 0,
1821 sym.n_sect = n_sect;2102 });
2103 return atom;
2104}
18222105
1823 log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{2106pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1824 self.getString(sym.n_strx),2107 const arch = self.base.options.target.cpu.arch;
1825 base_addr,2108 const alignment: u2 = switch (arch) {
1826 base_addr + block.size,2109 .x86_64 => 0,
1827 block.size,2110 .aarch64 => 2,
1828 block.alignment,2111 else => unreachable, // unhandled architecture type
2112 };
2113 const stub_size: u4 = switch (arch) {
2114 .x86_64 => 6,
2115 .aarch64 => 3 * @sizeOf(u32),
2116 else => unreachable, // unhandled architecture type
2117 };
2118 const local_sym_index = @intCast(u32, self.locals.items.len);
2119 try self.locals.append(self.base.allocator, .{
2120 .n_strx = try self.makeString("l_zld_stub"),
2121 .n_type = macho.N_SECT,
2122 .n_sect = 0,
2123 .n_desc = 0,
2124 .n_value = 0,
2125 });
2126 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);
2127 switch (arch) {
2128 .x86_64 => {
2129 // jmp
2130 atom.code.items[0] = 0xff;
2131 atom.code.items[1] = 0x25;
2132 try atom.relocs.append(self.base.allocator, .{
2133 .offset = 2,
2134 .where = .local,
2135 .where_index = laptr_sym_index,
2136 .payload = .{
2137 .branch = .{ .arch = arch },
2138 },
1829 });2139 });
18302140 },
1831 // Update each alias (if any)2141 .aarch64 => {
1832 for (block.aliases.items) |index| {2142 try atom.relocs.ensureTotalCapacity(self.base.allocator, 2);
1833 const alias_sym = &self.locals.items[index];2143 // adrp x16, pages
1834 alias_sym.n_value = base_addr;2144 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1835 alias_sym.n_sect = n_sect;2145 atom.relocs.appendAssumeCapacity(.{
1836 }2146 .offset = 0,
18372147 .where = .local,
1838 // Update each symbol contained within the TextBlock2148 .where_index = laptr_sym_index,
1839 for (block.contained.items) |sym_at_off| {2149 .payload = .{
1840 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];2150 .page = .{
1841 contained_sym.n_value = base_addr + sym_at_off.offset;2151 .kind = .page,
1842 contained_sym.n_sect = n_sect;2152 .addend = 0,
1843 }2153 },
18442154 },
1845 base_addr += block.size;2155 });
18462156 // ldr x16, x16, offset
1847 if (block.next) |next| {2157 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(.x16, .{
1848 block = next;2158 .register = .{
1849 } else break;2159 .rn = .x16,
1850 }2160 .offset = aarch64.Instruction.LoadStoreOffset.imm(0),
1851 }2161 },
18522162 }).toU32());
1853 // Update globals2163 atom.relocs.appendAssumeCapacity(.{
1854 {2164 .offset = 4,
1855 var sym_it = self.symbol_resolver.valueIterator();2165 .where = .local,
1856 while (sym_it.next()) |resolv| {2166 .where_index = laptr_sym_index,
1857 if (resolv.where != .global) continue;2167 .payload = .{
18582168 .page_off = .{
1859 assert(resolv.local_sym_index != 0);2169 .kind = .page,
1860 const local_sym = self.locals.items[resolv.local_sym_index];2170 .addend = 0,
1861 const sym = &self.globals.items[resolv.where_index];2171 .op_kind = .load,
1862 sym.n_value = local_sym.n_value;2172 },
1863 sym.n_sect = local_sym.n_sect;2173 },
1864 }2174 });
2175 // br x16
2176 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());
2177 },
2178 else => unreachable,
1865 }2179 }
2180 return atom;
1866}2181}
18672182
1868fn writeTextBlocks(self: *MachO) !void {2183fn createTentativeDefAtoms(self: *MachO) !void {
1869 var it = self.blocks.iterator();2184 if (self.tentatives.count() == 0) return;
1870 while (it.next()) |entry| {2185 // Convert any tentative definition into a regular symbol and allocate
1871 const match = entry.key_ptr.*;2186 // text blocks for each tentative defintion.
1872 var block: *TextBlock = entry.value_ptr.*;2187 while (self.tentatives.popOrNull()) |entry| {
18732188 const match = MatchingSection{
1874 while (block.prev) |prev| {2189 .seg = self.data_segment_cmd_index.?,
1875 block = prev;2190 .sect = self.bss_section_index.?,
1876 }2191 };
18772192 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
1878 const seg = self.load_commands.items[match.seg].Segment;
1879 const sect = seg.sections.items[match.sect];
1880 const sect_type = commands.sectionType(sect);
1881
1882 log.debug(" for section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
1883 log.debug(" {}", .{sect});
1884
1885 var code = try self.base.allocator.alloc(u8, sect.size);
1886 defer self.base.allocator.free(code);
1887
1888 if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) {
1889 mem.set(u8, code, 0);
1890 } else {
1891 var base_off: u64 = 0;
18922193
1893 while (true) {2194 const global_sym = &self.globals.items[entry.key];
1894 const block_alignment = try math.powi(u32, 2, block.alignment);2195 const size = global_sym.n_value;
1895 const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment);2196 const alignment = (global_sym.n_desc >> 8) & 0x0f;
18962197
1897 const sym = self.locals.items[block.local_sym_index];2198 global_sym.n_value = 0;
1898 log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{2199 global_sym.n_desc = 0;
1899 self.getString(sym.n_strx),2200 global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1900 aligned_base_off,
1901 aligned_base_off + block.size,
1902 block.size,
1903 block.alignment,
1904 });
19052201
1906 try block.resolveRelocs(self);2202 const local_sym_index = @intCast(u32, self.locals.items.len);
1907 mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items);2203 const local_sym = try self.locals.addOne(self.base.allocator);
2204 local_sym.* = .{
2205 .n_strx = global_sym.n_strx,
2206 .n_type = macho.N_SECT,
2207 .n_sect = global_sym.n_sect,
2208 .n_desc = 0,
2209 .n_value = 0,
2210 };
19082211
1909 // TODO NOP for machine code instead of just zeroing out2212 const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable;
1910 const padding_len = aligned_base_off - base_off;2213 resolv.local_sym_index = local_sym_index;
1911 mem.set(u8, code[base_off..][0..padding_len], 0);
19122214
1913 base_off = aligned_base_off + block.size;2215 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);
2216 const alignment_pow_2 = try math.powi(u32, 2, alignment);
2217 const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match);
2218 local_sym.n_value = vaddr;
2219 global_sym.n_value = vaddr;
2220 }
2221}
19142222
1915 if (block.next) |next| {2223fn createDsoHandleAtom(self: *MachO) !void {
1916 block = next;2224 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
1917 } else break;2225 .bytes = &self.strtab,
1918 }2226 })) |n_strx| blk: {
2227 const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk;
2228 if (resolv.where != .undef) break :blk;
19192229
1920 mem.set(u8, code[base_off..], 0);2230 const undef = &self.undefs.items[resolv.where_index];
1921 }2231 const match: MatchingSection = .{
2232 .seg = self.text_segment_cmd_index.?,
2233 .sect = self.text_section_index.?,
2234 };
2235 const local_sym_index = @intCast(u32, self.locals.items.len);
2236 var nlist = macho.nlist_64{
2237 .n_strx = undef.n_strx,
2238 .n_type = macho.N_SECT,
2239 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1),
2240 .n_desc = 0,
2241 .n_value = 0,
2242 };
2243 try self.locals.append(self.base.allocator, nlist);
2244 const global_sym_index = @intCast(u32, self.globals.items.len);
2245 nlist.n_type |= macho.N_EXT;
2246 nlist.n_desc = macho.N_WEAK_DEF;
2247 try self.globals.append(self.base.allocator, nlist);
19222248
1923 try self.base.file.?.pwriteAll(code, sect.offset);2249 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
1924 }
1925}
19262250
1927fn writeStubHelperCommon(self: *MachO) !void {2251 undef.* = .{
1928 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2252 .n_strx = 0,
1929 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];2253 .n_type = macho.N_UNDF,
1930 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2254 .n_sect = 0,
1931 const got = &data_const_segment.sections.items[self.got_section_index.?];2255 .n_desc = 0,
1932 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2256 .n_value = 0,
1933 const data = &data_segment.sections.items[self.data_section_index.?];2257 };
19342258 resolv.* = .{
1935 self.stub_helper_stubs_start_off = blk: {2259 .where = .global,
1936 switch (self.base.options.target.cpu.arch) {2260 .where_index = global_sym_index,
1937 .x86_64 => {2261 .local_sym_index = local_sym_index,
1938 const code_size = 15;2262 };
1939 var code: [code_size]u8 = undefined;
1940 // lea %r11, [rip + disp]
1941 code[0] = 0x4c;
1942 code[1] = 0x8d;
1943 code[2] = 0x1d;
1944 {
1945 const target_addr = data.addr + data.size - @sizeOf(u64);
1946 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
1947 mem.writeIntLittle(u32, code[3..7], displacement);
1948 }
1949 // push %r11
1950 code[7] = 0x41;
1951 code[8] = 0x53;
1952 // jmp [rip + disp]
1953 code[9] = 0xff;
1954 code[10] = 0x25;
1955 {
1956 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "dyld_stub_binder"), StringIndexAdapter{
1957 .bytes = &self.strtab,
1958 }) orelse unreachable;
1959 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
1960 const got_index = self.got_entries_map.get(.{
1961 .where = .undef,
1962 .where_index = resolv.where_index,
1963 }) orelse unreachable;
1964 const addr = got.addr + got_index * @sizeOf(u64);
1965 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
1966 mem.writeIntLittle(u32, code[11..], displacement);
1967 }
1968 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
1969 break :blk stub_helper.offset + code_size;
1970 },
1971 .aarch64 => {
1972 var code: [6 * @sizeOf(u32)]u8 = undefined;
1973 data_blk_outer: {
1974 const this_addr = stub_helper.addr;
1975 const target_addr = data.addr + data.size - @sizeOf(u64);
1976 data_blk: {
1977 const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk;
1978 // adr x17, disp
1979 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
1980 // nop
1981 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
1982 break :data_blk_outer;
1983 }
1984 data_blk: {
1985 const new_this_addr = this_addr + @sizeOf(u32);
1986 const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk;
1987 // nop
1988 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1989 // adr x17, disp
1990 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
1991 break :data_blk_outer;
1992 }
1993 // Jump is too big, replace adr with adrp and add.
1994 const this_page = @intCast(i32, this_addr >> 12);
1995 const target_page = @intCast(i32, target_addr >> 12);
1996 const pages = @intCast(i21, target_page - this_page);
1997 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
1998 const narrowed = @truncate(u12, target_addr);
1999 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
2000 }
2001 // stp x16, x17, [sp, #-16]!
2002 code[8] = 0xf0;
2003 code[9] = 0x47;
2004 code[10] = 0xbf;
2005 code[11] = 0xa9;
2006 binder_blk_outer: {
2007 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "dyld_stub_binder"), StringIndexAdapter{
2008 .bytes = &self.strtab,
2009 }) orelse unreachable;
2010 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
2011 const got_index = self.got_entries_map.get(.{
2012 .where = .undef,
2013 .where_index = resolv.where_index,
2014 }) orelse unreachable;
2015 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
2016 const target_addr = got.addr + got_index * @sizeOf(u64);
2017 binder_blk: {
2018 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk;
2019 const literal = math.cast(u18, displacement) catch break :binder_blk;
2020 // ldr x16, label
2021 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
2022 .literal = literal,
2023 }).toU32());
2024 // nop
2025 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
2026 break :binder_blk_outer;
2027 }
2028 binder_blk: {
2029 const new_this_addr = this_addr + @sizeOf(u32);
2030 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk;
2031 const literal = math.cast(u18, displacement) catch break :binder_blk;
2032 // Pad with nop to please division.
2033 // nop
2034 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
2035 // ldr x16, label
2036 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2037 .literal = literal,
2038 }).toU32());
2039 break :binder_blk_outer;
2040 }
2041 // Use adrp followed by ldr(immediate).
2042 const this_page = @intCast(i32, this_addr >> 12);
2043 const target_page = @intCast(i32, target_addr >> 12);
2044 const pages = @intCast(i21, target_page - this_page);
2045 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
2046 const narrowed = @truncate(u12, target_addr);
2047 const offset = try math.divExact(u12, narrowed, 8);
2048 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2049 .register = .{
2050 .rn = .x16,
2051 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2052 },
2053 }).toU32());
2054 }
2055 // br x16
2056 code[20] = 0x00;
2057 code[21] = 0x02;
2058 code[22] = 0x1f;
2059 code[23] = 0xd6;
2060 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2061 break :blk stub_helper.offset + 6 * @sizeOf(u32);
2062 },
2063 else => unreachable,
2064 }
2065 };
20662263
2067 for (self.stubs.items) |_, i| {2264 // We create an empty atom for this symbol.
2068 const index = @intCast(u32, i);2265 // TODO perhaps we should special-case special symbols? Create a separate
2069 // TODO weak bound pointers2266 // linked list of atoms?
2070 try self.writeLazySymbolPointer(index);2267 const atom = try self.createEmptyAtom(local_sym_index, 0, 0);
2071 try self.writeStub(index);2268 const sym = &self.locals.items[local_sym_index];
2072 try self.writeStubInStubHelper(index);2269 const vaddr = try self.allocateAtom(atom, 0, 1, match);
2270 sym.n_value = vaddr;
2271 atom.dirty = false; // We don't really want to write it to file.
2073 }2272 }
2074}2273}
20752274
2076fn resolveSymbolsInObject(2275fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2077 self: *MachO,
2078 object_id: u16,
2079 tentatives: *std.AutoArrayHashMap(u32, void),
2080 unresolved: *std.AutoArrayHashMap(u32, void),
2081) !void {
2082 const object = &self.objects.items[object_id];2276 const object = &self.objects.items[object_id];
20832277
2084 log.debug("resolving symbols in '{s}'", .{object.name});2278 log.debug("resolving symbols in '{s}'", .{object.name});
...@@ -2150,7 +2344,7 @@ fn resolveSymbolsInObject(...@@ -2150,7 +2344,7 @@ fn resolveSymbolsInObject(
2150 const global = &self.globals.items[resolv.where_index];2344 const global = &self.globals.items[resolv.where_index];
21512345
2152 if (symbolIsTentative(global.*)) {2346 if (symbolIsTentative(global.*)) {
2153 _ = tentatives.fetchSwapRemove(resolv.where_index);2347 _ = self.tentatives.fetchSwapRemove(resolv.where_index);
2154 } else if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and2348 } else if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and
2155 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))2349 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
2156 {2350 {
...@@ -2168,15 +2362,7 @@ fn resolveSymbolsInObject(...@@ -2168,15 +2362,7 @@ fn resolveSymbolsInObject(
2168 continue;2362 continue;
2169 },2363 },
2170 .undef => {2364 .undef => {
2171 const undef = &self.undefs.items[resolv.where_index];2365 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
2172 undef.* = .{
2173 .n_strx = 0,
2174 .n_type = macho.N_UNDF,
2175 .n_sect = 0,
2176 .n_desc = 0,
2177 .n_value = 0,
2178 };
2179 _ = unresolved.fetchSwapRemove(resolv.where_index);
2180 },2366 },
2181 }2367 }
21822368
...@@ -2210,7 +2396,7 @@ fn resolveSymbolsInObject(...@@ -2210,7 +2396,7 @@ fn resolveSymbolsInObject(
2210 .where_index = global_sym_index,2396 .where_index = global_sym_index,
2211 .file = object_id,2397 .file = object_id,
2212 });2398 });
2213 _ = try tentatives.getOrPut(global_sym_index);2399 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
2214 continue;2400 continue;
2215 };2401 };
22162402
...@@ -2234,7 +2420,7 @@ fn resolveSymbolsInObject(...@@ -2234,7 +2420,7 @@ fn resolveSymbolsInObject(
2234 .n_desc = sym.n_desc,2420 .n_desc = sym.n_desc,
2235 .n_value = sym.n_value,2421 .n_value = sym.n_value,
2236 });2422 });
2237 _ = try tentatives.getOrPut(global_sym_index);2423 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
2238 resolv.* = .{2424 resolv.* = .{
2239 .where = .global,2425 .where = .global,
2240 .where_index = global_sym_index,2426 .where_index = global_sym_index,
...@@ -2247,7 +2433,7 @@ fn resolveSymbolsInObject(...@@ -2247,7 +2433,7 @@ fn resolveSymbolsInObject(
2247 .n_desc = 0,2433 .n_desc = 0,
2248 .n_value = 0,2434 .n_value = 0,
2249 };2435 };
2250 _ = unresolved.fetchSwapRemove(resolv.where_index);2436 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
2251 },2437 },
2252 }2438 }
2253 } else {2439 } else {
...@@ -2267,27 +2453,17 @@ fn resolveSymbolsInObject(...@@ -2267,27 +2453,17 @@ fn resolveSymbolsInObject(
2267 .where_index = undef_sym_index,2453 .where_index = undef_sym_index,
2268 .file = object_id,2454 .file = object_id,
2269 });2455 });
2270 _ = try unresolved.getOrPut(undef_sym_index);2456 try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none);
2271 }2457 }
2272 }2458 }
2273}2459}
22742460
2275fn resolveSymbols(self: *MachO) !void {2461fn resolveSymbolsInArchives(self: *MachO) !void {
2276 var tentatives = std.AutoArrayHashMap(u32, void).init(self.base.allocator);2462 if (self.archives.items.len == 0) return;
2277 defer tentatives.deinit();
2278
2279 var unresolved = std.AutoArrayHashMap(u32, void).init(self.base.allocator);
2280 defer unresolved.deinit();
2281
2282 // First pass, resolve symbols in provided objects.
2283 for (self.objects.items) |_, object_id| {
2284 try self.resolveSymbolsInObject(@intCast(u16, object_id), &tentatives, &unresolved);
2285 }
22862463
2287 // Second pass, resolve symbols in static libraries.
2288 var next_sym: usize = 0;2464 var next_sym: usize = 0;
2289 loop: while (next_sym < unresolved.count()) {2465 loop: while (next_sym < self.unresolved.count()) {
2290 const sym = self.undefs.items[unresolved.keys()[next_sym]];2466 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
2291 const sym_name = self.getString(sym.n_strx);2467 const sym_name = self.getString(sym.n_strx);
22922468
2293 for (self.archives.items) |archive| {2469 for (self.archives.items) |archive| {
...@@ -2301,102 +2477,21 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2301,102 +2477,21 @@ fn resolveSymbols(self: *MachO) !void {
2301 const object_id = @intCast(u16, self.objects.items.len);2477 const object_id = @intCast(u16, self.objects.items.len);
2302 const object = try self.objects.addOne(self.base.allocator);2478 const object = try self.objects.addOne(self.base.allocator);
2303 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);2479 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);
2304 try self.resolveSymbolsInObject(object_id, &tentatives, &unresolved);2480 try self.resolveSymbolsInObject(object_id);
23052481
2306 continue :loop;2482 continue :loop;
2307 }2483 }
23082484
2309 next_sym += 1;2485 next_sym += 1;
2310 }2486 }
2487}
23112488
2312 // Convert any tentative definition into a regular symbol and allocate2489fn resolveSymbolsInDylibs(self: *MachO) !void {
2313 // text blocks for each tentative defintion.2490 if (self.dylibs.items.len == 0) return;
2314 while (tentatives.popOrNull()) |entry| {
2315 const sym = &self.globals.items[entry.key];
2316 const match: MatchingSection = blk: {
2317 if (self.common_section_index == null) {
2318 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2319 self.common_section_index = @intCast(u16, data_seg.sections.items.len);
2320 try data_seg.addSection(self.base.allocator, "__common", .{
2321 .flags = macho.S_ZEROFILL,
2322 });
2323 }
2324 break :blk .{
2325 .seg = self.data_segment_cmd_index.?,
2326 .sect = self.common_section_index.?,
2327 };
2328 };
2329 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
2330
2331 const size = sym.n_value;
2332 const code = try self.base.allocator.alloc(u8, size);
2333 defer self.base.allocator.free(code);
2334 mem.set(u8, code, 0);
2335 const alignment = (sym.n_desc >> 8) & 0x0f;
2336
2337 sym.n_value = 0;
2338 sym.n_desc = 0;
2339 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2340 var local_sym = sym.*;
2341 local_sym.n_type = macho.N_SECT;
2342
2343 const local_sym_index = @intCast(u32, self.locals.items.len);
2344 try self.locals.append(self.base.allocator, local_sym);
2345
2346 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;
2347 resolv.local_sym_index = local_sym_index;
2348
2349 const block = try self.base.allocator.create(TextBlock);
2350 block.* = TextBlock.empty;
2351 block.local_sym_index = local_sym_index;
2352 block.size = size;
2353 block.alignment = alignment;
2354 try self.managed_blocks.append(self.base.allocator, block);
2355
2356 try block.code.appendSlice(self.base.allocator, code);
2357
2358 // Update target section's metadata
2359 // TODO should we update segment's size here too?
2360 // How does it tie with incremental space allocs?
2361 const tseg = &self.load_commands.items[match.seg].Segment;
2362 const tsect = &tseg.sections.items[match.sect];
2363 const new_alignment = math.max(tsect.@"align", block.alignment);
2364 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
2365 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
2366 tsect.size = new_size;
2367 tsect.@"align" = new_alignment;
2368
2369 if (self.blocks.getPtr(match)) |last| {
2370 last.*.next = block;
2371 block.prev = last.*;
2372 last.* = block;
2373 } else {
2374 try self.blocks.putNoClobber(self.base.allocator, match, block);
2375 }
2376 }
2377
2378 // Third pass, resolve symbols in dynamic libraries.
2379 {
2380 // Put dyld_stub_binder as an undefined special symbol.
2381 const n_strx = try self.makeString("dyld_stub_binder");
2382 const undef_sym_index = @intCast(u32, self.undefs.items.len);
2383 try self.undefs.append(self.base.allocator, .{
2384 .n_strx = n_strx,
2385 .n_type = macho.N_UNDF,
2386 .n_sect = 0,
2387 .n_desc = 0,
2388 .n_value = 0,
2389 });
2390 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
2391 .where = .undef,
2392 .where_index = undef_sym_index,
2393 });
2394 _ = try unresolved.getOrPut(undef_sym_index);
2395 }
23962491
2397 next_sym = 0;2492 var next_sym: usize = 0;
2398 loop: while (next_sym < unresolved.count()) {2493 loop: while (next_sym < self.unresolved.count()) {
2399 const sym = self.undefs.items[unresolved.keys()[next_sym]];2494 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
2400 const sym_name = self.getString(sym.n_strx);2495 const sym_name = self.getString(sym.n_strx);
24012496
2402 for (self.dylibs.items) |dylib, id| {2497 for (self.dylibs.items) |dylib, id| {
...@@ -2404,6 +2499,7 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2404,6 +2499,7 @@ fn resolveSymbols(self: *MachO) !void {
24042499
2405 const dylib_id = @intCast(u16, id);2500 const dylib_id = @intCast(u16, id);
2406 if (!self.referenced_dylibs.contains(dylib_id)) {2501 if (!self.referenced_dylibs.contains(dylib_id)) {
2502 try self.addLoadDylibLC(dylib_id);
2407 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});2503 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
2408 }2504 }
24092505
...@@ -2413,3115 +2509,1859 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2413,3115 +2509,1859 @@ fn resolveSymbols(self: *MachO) !void {
2413 undef.n_type |= macho.N_EXT;2509 undef.n_type |= macho.N_EXT;
2414 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;2510 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
24152511
2416 _ = unresolved.fetchSwapRemove(resolv.where_index);2512 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {
2513 switch (entry.value) {
2514 .none => {},
2515 .got => return error.TODOGotHint,
2516 .stub => {
2517 if (self.stubs_map.contains(resolv.where_index)) break :outer_blk;
2518 const stub_helper_atom = blk: {
2519 const match = MatchingSection{
2520 .seg = self.text_segment_cmd_index.?,
2521 .sect = self.stub_helper_section_index.?,
2522 };
2523 const atom = try self.createStubHelperAtom();
2524 const atom_sym = &self.locals.items[atom.local_sym_index];
2525 const alignment = try math.powi(u32, 2, atom.alignment);
2526 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
2527 atom_sym.n_value = vaddr;
2528 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2529 break :blk atom;
2530 };
2531 const laptr_atom = blk: {
2532 const match = MatchingSection{
2533 .seg = self.data_segment_cmd_index.?,
2534 .sect = self.la_symbol_ptr_section_index.?,
2535 };
2536 const atom = try self.createLazyPointerAtom(
2537 stub_helper_atom.local_sym_index,
2538 resolv.where_index,
2539 );
2540 const atom_sym = &self.locals.items[atom.local_sym_index];
2541 const alignment = try math.powi(u32, 2, atom.alignment);
2542 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
2543 atom_sym.n_value = vaddr;
2544 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2545 break :blk atom;
2546 };
2547 const stub_atom = blk: {
2548 const match = MatchingSection{
2549 .seg = self.text_segment_cmd_index.?,
2550 .sect = self.stubs_section_index.?,
2551 };
2552 const atom = try self.createStubAtom(laptr_atom.local_sym_index);
2553 const atom_sym = &self.locals.items[atom.local_sym_index];
2554 const alignment = try math.powi(u32, 2, atom.alignment);
2555 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
2556 atom_sym.n_value = vaddr;
2557 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2558 break :blk atom;
2559 };
2560 try self.stubs_map.putNoClobber(self.base.allocator, resolv.where_index, stub_atom);
2561 },
2562 }
2563 }
24172564
2418 continue :loop;2565 continue :loop;
2419 }2566 }
24202567
2421 next_sym += 1;2568 next_sym += 1;
2422 }2569 }
2570}
24232571
2424 // Fourth pass, handle synthetic symbols and flag any undefined references.2572fn resolveDyldStubBinder(self: *MachO) !void {
2425 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{2573 if (self.dyld_stub_binder_index != null) return;
2426 .bytes = &self.strtab,
2427 })) |n_strx| blk: {
2428 const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk;
2429 if (resolv.where != .undef) break :blk;
2430
2431 const undef = &self.undefs.items[resolv.where_index];
2432 const match: MatchingSection = .{
2433 .seg = self.text_segment_cmd_index.?,
2434 .sect = self.text_section_index.?,
2435 };
2436 const local_sym_index = @intCast(u32, self.locals.items.len);
2437 var nlist = macho.nlist_64{
2438 .n_strx = undef.n_strx,
2439 .n_type = macho.N_SECT,
2440 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1),
2441 .n_desc = 0,
2442 .n_value = 0,
2443 };
2444 try self.locals.append(self.base.allocator, nlist);
2445 const global_sym_index = @intCast(u32, self.globals.items.len);
2446 nlist.n_type |= macho.N_EXT;
2447 nlist.n_desc = macho.N_WEAK_DEF;
2448 try self.globals.append(self.base.allocator, nlist);
24492574
2450 _ = unresolved.fetchSwapRemove(resolv.where_index);2575 const n_strx = try self.makeString("dyld_stub_binder");
2576 const sym_index = @intCast(u32, self.undefs.items.len);
2577 try self.undefs.append(self.base.allocator, .{
2578 .n_strx = n_strx,
2579 .n_type = macho.N_UNDF,
2580 .n_sect = 0,
2581 .n_desc = 0,
2582 .n_value = 0,
2583 });
2584 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
2585 .where = .undef,
2586 .where_index = sym_index,
2587 });
2588 const sym = &self.undefs.items[sym_index];
2589 const sym_name = self.getString(n_strx);
24512590
2452 undef.* = .{2591 for (self.dylibs.items) |dylib, id| {
2453 .n_strx = 0,2592 if (!dylib.symbols.contains(sym_name)) continue;
2454 .n_type = macho.N_UNDF,
2455 .n_sect = 0,
2456 .n_desc = 0,
2457 .n_value = 0,
2458 };
2459 resolv.* = .{
2460 .where = .global,
2461 .where_index = global_sym_index,
2462 .local_sym_index = local_sym_index,
2463 };
24642593
2465 // We create an empty atom for this symbol.2594 const dylib_id = @intCast(u16, id);
2466 // TODO perhaps we should special-case special symbols? Create a separate2595 if (!self.referenced_dylibs.contains(dylib_id)) {
2467 // linked list of atoms?2596 try self.addLoadDylibLC(dylib_id);
2468 const block = try self.base.allocator.create(TextBlock);2597 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
2469 block.* = TextBlock.empty;
2470 block.local_sym_index = local_sym_index;
2471 block.size = 0;
2472 block.alignment = 0;
2473 try self.managed_blocks.append(self.base.allocator, block);
2474
2475 if (self.blocks.getPtr(match)) |last| {
2476 last.*.next = block;
2477 block.prev = last.*;
2478 last.* = block;
2479 } else {
2480 try self.blocks.putNoClobber(self.base.allocator, match, block);
2481 }2598 }
2482 }
24832599
2484 for (unresolved.keys()) |index| {2600 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
2485 const sym = self.undefs.items[index];2601 sym.n_type |= macho.N_EXT;
2486 const sym_name = self.getString(sym.n_strx);2602 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
2487 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;2603 self.dyld_stub_binder_index = sym_index;
24882604
2489 log.err("undefined reference to symbol '{s}'", .{sym_name});2605 break;
2490 log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name});
2491 }2606 }
24922607
2493 if (unresolved.count() > 0)2608 if (self.dyld_stub_binder_index == null) {
2609 log.err("undefined reference to symbol '{s}'", .{sym_name});
2494 return error.UndefinedSymbolReference;2610 return error.UndefinedSymbolReference;
2611 }
2612
2613 // Add dyld_stub_binder as the final GOT entry.
2614 const got_entry = GotIndirectionKey{
2615 .where = .undef,
2616 .where_index = self.dyld_stub_binder_index.?,
2617 };
2618 const atom = try self.createGotAtom(got_entry);
2619 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, atom);
2620 const match = MatchingSection{
2621 .seg = self.data_const_segment_cmd_index.?,
2622 .sect = self.got_section_index.?,
2623 };
2624 const atom_sym = &self.locals.items[atom.local_sym_index];
2625 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
2626 atom_sym.n_value = vaddr;
2627 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2628 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
2495}2629}
24962630
2497fn parseTextBlocks(self: *MachO) !void {2631fn parseObjectsIntoAtoms(self: *MachO) !void {
2632 const tracy = trace(@src());
2633 defer tracy.end();
2634
2635 var parsed_atoms = Object.ParsedAtoms.init(self.base.allocator);
2636 defer parsed_atoms.deinit();
2637
2638 var first_atoms = Object.ParsedAtoms.init(self.base.allocator);
2639 defer first_atoms.deinit();
2640
2641 var section_metadata = std.AutoHashMap(MatchingSection, struct {
2642 size: u64,
2643 alignment: u32,
2644 }).init(self.base.allocator);
2645 defer section_metadata.deinit();
2646
2498 for (self.objects.items) |*object, object_id| {2647 for (self.objects.items) |*object, object_id| {
2499 try object.parseTextBlocks(self.base.allocator, @intCast(u16, object_id), self);2648 if (object.analyzed) continue;
2500 }
2501}
25022649
2503fn populateMetadata(self: *MachO) !void {2650 var atoms_in_objects = try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_id), self);
2504 if (self.pagezero_segment_cmd_index == null) {2651 defer atoms_in_objects.deinit();
2505 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2506 try self.load_commands.append(self.base.allocator, .{
2507 .Segment = SegmentCommand.empty("__PAGEZERO", .{
2508 .vmsize = 0x100000000, // size always set to 4GB
2509 }),
2510 });
2511 }
25122652
2513 if (self.text_segment_cmd_index == null) {2653 var it = atoms_in_objects.iterator();
2514 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);2654 while (it.next()) |entry| {
2515 try self.load_commands.append(self.base.allocator, .{2655 const match = entry.key_ptr.*;
2516 .Segment = SegmentCommand.empty("__TEXT", .{2656 const last_atom = entry.value_ptr.*;
2517 .vmaddr = 0x100000000, // always starts at 4GB2657 var atom = last_atom;
2518 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,2658
2519 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,2659 const metadata = try section_metadata.getOrPut(match);
2520 }),2660 if (!metadata.found_existing) {
2521 });2661 metadata.value_ptr.* = .{
2522 }2662 .size = 0,
2663 .alignment = 0,
2664 };
2665 }
25232666
2524 if (self.text_section_index == null) {2667 while (true) {
2525 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2668 const alignment = try math.powi(u32, 2, atom.alignment);
2526 self.text_section_index = @intCast(u16, text_seg.sections.items.len);2669 metadata.value_ptr.size += mem.alignForwardGeneric(u64, atom.size, alignment);
2527 const alignment: u2 = switch (self.base.options.target.cpu.arch) {2670 metadata.value_ptr.alignment = math.max(metadata.value_ptr.alignment, atom.alignment);
2528 .x86_64 => 0,
2529 .aarch64 => 2,
2530 else => unreachable, // unhandled architecture type
2531 };
2532 try text_seg.addSection(self.base.allocator, "__text", .{
2533 .@"align" = alignment,
2534 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2535 });
2536 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2537 .seg = self.text_segment_cmd_index.?,
2538 .sect = self.text_section_index.?,
2539 });
2540 }
25412671
2542 if (self.stubs_section_index == null) {2672 const sym = self.locals.items[atom.local_sym_index];
2543 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2673 log.debug(" {s}: n_value=0x{x}, size=0x{x}, alignment=0x{x}", .{
2544 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);2674 self.getString(sym.n_strx),
2545 const alignment: u2 = switch (self.base.options.target.cpu.arch) {2675 sym.n_value,
2546 .x86_64 => 0,2676 atom.size,
2547 .aarch64 => 2,2677 atom.alignment,
2548 else => unreachable, // unhandled architecture type2678 });
2549 };
2550 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2551 .x86_64 => 6,
2552 .aarch64 => 3 * @sizeOf(u32),
2553 else => unreachable, // unhandled architecture type
2554 };
2555 try text_seg.addSection(self.base.allocator, "__stubs", .{
2556 .@"align" = alignment,
2557 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2558 .reserved2 = stub_size,
2559 });
2560 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2561 .seg = self.text_segment_cmd_index.?,
2562 .sect = self.stubs_section_index.?,
2563 });
2564 }
25652679
2566 if (self.stub_helper_section_index == null) {2680 if (atom.prev) |prev| {
2567 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2681 atom = prev;
2568 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);2682 } else break;
2569 const alignment: u2 = switch (self.base.options.target.cpu.arch) {2683 }
2570 .x86_64 => 0,
2571 .aarch64 => 2,
2572 else => unreachable, // unhandled architecture type
2573 };
2574 const stub_helper_size: u6 = switch (self.base.options.target.cpu.arch) {
2575 .x86_64 => 15,
2576 .aarch64 => 6 * @sizeOf(u32),
2577 else => unreachable,
2578 };
2579 try text_seg.addSection(self.base.allocator, "__stub_helper", .{
2580 .size = stub_helper_size,
2581 .@"align" = alignment,
2582 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2583 });
2584 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2585 .seg = self.text_segment_cmd_index.?,
2586 .sect = self.stub_helper_section_index.?,
2587 });
2588 }
25892684
2590 if (self.data_const_segment_cmd_index == null) {2685 if (parsed_atoms.getPtr(match)) |last| {
2591 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);2686 last.*.next = atom;
2592 try self.load_commands.append(self.base.allocator, .{2687 atom.prev = last.*;
2593 .Segment = SegmentCommand.empty("__DATA_CONST", .{2688 last.* = atom;
2594 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,2689 }
2595 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,2690 _ = try parsed_atoms.put(match, last_atom);
2596 }),
2597 });
2598 }
25992691
2600 if (self.got_section_index == null) {2692 if (!first_atoms.contains(match)) {
2601 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2693 try first_atoms.putNoClobber(match, atom);
2602 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);2694 }
2603 try data_const_seg.addSection(self.base.allocator, "__got", .{2695 }
2604 .@"align" = 3, // 2^3 = @sizeOf(u64)
2605 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2606 });
2607 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2608 .seg = self.data_const_segment_cmd_index.?,
2609 .sect = self.got_section_index.?,
2610 });
2611 }
26122696
2613 if (self.data_segment_cmd_index == null) {2697 object.analyzed = true;
2614 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2615 try self.load_commands.append(self.base.allocator, .{
2616 .Segment = SegmentCommand.empty("__DATA", .{
2617 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2618 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2619 }),
2620 });
2621 }2698 }
26222699
2623 if (self.la_symbol_ptr_section_index == null) {2700 var it = section_metadata.iterator();
2624 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2701 while (it.next()) |entry| {
2625 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);2702 const match = entry.key_ptr.*;
2626 try data_seg.addSection(self.base.allocator, "__la_symbol_ptr", .{2703 const metadata = entry.value_ptr.*;
2627 .@"align" = 3, // 2^3 = @sizeOf(u64)2704 const seg = &self.load_commands.items[match.seg].Segment;
2628 .flags = macho.S_LAZY_SYMBOL_POINTERS,2705 const sect = &seg.sections.items[match.sect];
2629 });2706 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
2630 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{2707 commands.segmentName(sect.*),
2631 .seg = self.data_segment_cmd_index.?,2708 commands.sectionName(sect.*),
2632 .sect = self.la_symbol_ptr_section_index.?,2709 metadata.size,
2710 metadata.alignment,
2633 });2711 });
2634 }
26352712
2636 if (self.data_section_index == null) {2713 const sect_size = if (self.atoms.get(match)) |last| blk: {
2637 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2714 const last_atom_sym = self.locals.items[last.local_sym_index];
2638 self.data_section_index = @intCast(u16, data_seg.sections.items.len);2715 break :blk last_atom_sym.n_value + last.size - sect.addr;
2639 try data_seg.addSection(self.base.allocator, "__data", .{2716 } else 0;
2640 .@"align" = 3, // 2^3 = @sizeOf(u64)
2641 });
2642 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2643 .seg = self.data_segment_cmd_index.?,
2644 .sect = self.data_section_index.?,
2645 });
2646 }
26472717
2648 if (self.linkedit_segment_cmd_index == null) {2718 sect.@"align" = math.max(sect.@"align", metadata.alignment);
2649 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);2719 const needed_size = @intCast(u32, metadata.size + sect_size);
2650 try self.load_commands.append(self.base.allocator, .{2720 try self.growSection(match, needed_size);
2651 .Segment = SegmentCommand.empty("__LINKEDIT", .{2721 sect.size = needed_size;
2652 .maxprot = macho.VM_PROT_READ,
2653 .initprot = macho.VM_PROT_READ,
2654 }),
2655 });
2656 }
26572722
2658 if (self.dyld_info_cmd_index == null) {2723 var base_vaddr = if (self.atoms.get(match)) |last| blk: {
2659 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);2724 const last_atom_sym = self.locals.items[last.local_sym_index];
2660 try self.load_commands.append(self.base.allocator, .{2725 break :blk last_atom_sym.n_value + last.size;
2661 .DyldInfoOnly = .{2726 } else sect.addr;
2662 .cmd = macho.LC_DYLD_INFO_ONLY,2727 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2663 .cmdsize = @sizeOf(macho.dyld_info_command),
2664 .rebase_off = 0,
2665 .rebase_size = 0,
2666 .bind_off = 0,
2667 .bind_size = 0,
2668 .weak_bind_off = 0,
2669 .weak_bind_size = 0,
2670 .lazy_bind_off = 0,
2671 .lazy_bind_size = 0,
2672 .export_off = 0,
2673 .export_size = 0,
2674 },
2675 });
2676 }
26772728
2678 if (self.symtab_cmd_index == null) {2729 var atom = first_atoms.get(match).?;
2679 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);2730 while (true) {
2680 try self.load_commands.append(self.base.allocator, .{2731 const alignment = try math.powi(u32, 2, atom.alignment);
2681 .Symtab = .{2732 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
2682 .cmd = macho.LC_SYMTAB,2733
2683 .cmdsize = @sizeOf(macho.symtab_command),2734 const sym = &self.locals.items[atom.local_sym_index];
2684 .symoff = 0,2735 sym.n_value = base_vaddr;
2685 .nsyms = 0,2736 sym.n_sect = n_sect;
2686 .stroff = 0,2737
2687 .strsize = 0,2738 log.debug(" {s}: start=0x{x}, end=0x{x}, size=0x{x}, alignment=0x{x}", .{
2688 },2739 self.getString(sym.n_strx),
2689 });2740 base_vaddr,
2741 base_vaddr + atom.size,
2742 atom.size,
2743 atom.alignment,
2744 });
2745
2746 // Update each alias (if any)
2747 for (atom.aliases.items) |index| {
2748 const alias_sym = &self.locals.items[index];
2749 alias_sym.n_value = base_vaddr;
2750 alias_sym.n_sect = n_sect;
2751 }
2752
2753 // Update each symbol contained within the atom
2754 for (atom.contained.items) |sym_at_off| {
2755 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
2756 contained_sym.n_value = base_vaddr + sym_at_off.offset;
2757 contained_sym.n_sect = n_sect;
2758 }
2759
2760 base_vaddr += atom.size;
2761
2762 if (atom.next) |next| {
2763 atom = next;
2764 } else break;
2765 }
2766
2767 if (self.atoms.getPtr(match)) |last| {
2768 const first_atom = first_atoms.get(match).?;
2769 last.*.next = first_atom;
2770 first_atom.prev = last.*;
2771 last.* = first_atom;
2772 }
2773 _ = try self.atoms.put(self.base.allocator, match, parsed_atoms.get(match).?);
2690 }2774 }
2775}
26912776
2692 if (self.dysymtab_cmd_index == null) {2777fn addLoadDylibLC(self: *MachO, id: u16) !void {
2693 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);2778 const dylib = self.dylibs.items[id];
2694 try self.load_commands.append(self.base.allocator, .{2779 const dylib_id = dylib.id orelse unreachable;
2695 .Dysymtab = .{2780 var dylib_cmd = try commands.createLoadDylibCommand(
2696 .cmd = macho.LC_DYSYMTAB,2781 self.base.allocator,
2697 .cmdsize = @sizeOf(macho.dysymtab_command),2782 dylib_id.name,
2698 .ilocalsym = 0,2783 dylib_id.timestamp,
2699 .nlocalsym = 0,2784 dylib_id.current_version,
2700 .iextdefsym = 0,2785 dylib_id.compatibility_version,
2701 .nextdefsym = 0,2786 );
2702 .iundefsym = 0,2787 errdefer dylib_cmd.deinit(self.base.allocator);
2703 .nundefsym = 0,2788 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
2704 .tocoff = 0,2789 self.load_commands_dirty = true;
2705 .ntoc = 0,2790}
2706 .modtaboff = 0,
2707 .nmodtab = 0,
2708 .extrefsymoff = 0,
2709 .nextrefsyms = 0,
2710 .indirectsymoff = 0,
2711 .nindirectsyms = 0,
2712 .extreloff = 0,
2713 .nextrel = 0,
2714 .locreloff = 0,
2715 .nlocrel = 0,
2716 },
2717 });
2718 }
27192791
2720 if (self.dylinker_cmd_index == null) {2792fn addCodeSignatureLC(self: *MachO) !void {
2721 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);2793 if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;
2722 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2794 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2723 u64,2795 try self.load_commands.append(self.base.allocator, .{
2724 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),2796 .LinkeditData = .{
2725 @sizeOf(u64),2797 .cmd = macho.LC_CODE_SIGNATURE,
2726 ));2798 .cmdsize = @sizeOf(macho.linkedit_data_command),
2727 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{2799 .dataoff = 0,
2728 .cmd = macho.LC_LOAD_DYLINKER,2800 .datasize = 0,
2729 .cmdsize = cmdsize,2801 },
2730 .name = @sizeOf(macho.dylinker_command),2802 });
2731 });2803 self.load_commands_dirty = true;
2732 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);2804}
2733 mem.set(u8, dylinker_cmd.data, 0);
2734 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
2735 try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
2736 }
27372805
2738 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {2806fn setEntryPoint(self: *MachO) !void {
2739 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);2807 if (self.base.options.output_mode != .Exe) return;
2740 try self.load_commands.append(self.base.allocator, .{
2741 .Main = .{
2742 .cmd = macho.LC_MAIN,
2743 .cmdsize = @sizeOf(macho.entry_point_command),
2744 .entryoff = 0x0,
2745 .stacksize = 0,
2746 },
2747 });
2748 }
27492808
2750 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {2809 // TODO we should respect the -entry flag passed in by the user to set a custom
2751 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);2810 // entrypoint. For now, assume default of `_main`.
2752 const install_name = try std.fmt.allocPrint(self.base.allocator, "@rpath/{s}", .{2811 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2753 self.base.options.emit.?.sub_path,2812 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
2754 });2813 .bytes = &self.strtab,
2755 defer self.base.allocator.free(install_name);2814 }) orelse {
2756 var dylib_cmd = try commands.createLoadDylibCommand(2815 log.err("'_main' export not found", .{});
2757 self.base.allocator,2816 return error.MissingMainEntrypoint;
2758 install_name,2817 };
2759 2,2818 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
2760 0x10000, // TODO forward user-provided versions2819 assert(resolv.where == .global);
2761 0x10000,2820 const sym = self.globals.items[resolv.where_index];
2762 );2821 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2763 errdefer dylib_cmd.deinit(self.base.allocator);2822 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
2764 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;2823 ec.stacksize = self.base.options.stack_size_override orelse 0;
2765 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });2824 self.entry_addr = sym.n_value;
2825 self.load_commands_dirty = true;
2826}
2827
2828pub fn deinit(self: *MachO) void {
2829 if (build_options.have_llvm) {
2830 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
2766 }2831 }
27672832
2768 if (self.source_version_cmd_index == null) {2833 if (self.d_sym) |*ds| {
2769 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);2834 ds.deinit(self.base.allocator);
2770 try self.load_commands.append(self.base.allocator, .{
2771 .SourceVersion = .{
2772 .cmd = macho.LC_SOURCE_VERSION,
2773 .cmdsize = @sizeOf(macho.source_version_command),
2774 .version = 0x0,
2775 },
2776 });
2777 }2835 }
27782836
2779 if (self.build_version_cmd_index == null) {2837 self.section_ordinals.deinit(self.base.allocator);
2780 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);2838 self.got_entries_map.deinit(self.base.allocator);
2781 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2839 self.stubs_map.deinit(self.base.allocator);
2782 u64,2840 self.strtab_dir.deinit(self.base.allocator);
2783 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),2841 self.strtab.deinit(self.base.allocator);
2784 @sizeOf(u64),2842 self.undefs.deinit(self.base.allocator);
2785 ));2843 self.globals.deinit(self.base.allocator);
2786 const ver = self.base.options.target.os.version_range.semver.min;2844 self.globals_free_list.deinit(self.base.allocator);
2787 const version = ver.major << 16 | ver.minor << 8 | ver.patch;2845 self.locals.deinit(self.base.allocator);
2788 const is_simulator_abi = self.base.options.target.abi == .simulator;2846 self.locals_free_list.deinit(self.base.allocator);
2789 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{2847 self.symbol_resolver.deinit(self.base.allocator);
2790 .cmd = macho.LC_BUILD_VERSION,2848 self.unresolved.deinit(self.base.allocator);
2791 .cmdsize = cmdsize,2849 self.tentatives.deinit(self.base.allocator);
2792 .platform = switch (self.base.options.target.os.tag) {2850
2793 .macos => macho.PLATFORM_MACOS,2851 for (self.objects.items) |*object| {
2794 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,2852 object.deinit(self.base.allocator);
2795 .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS,
2796 .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
2797 else => unreachable,
2798 },
2799 .minos = version,
2800 .sdk = version,
2801 .ntools = 1,
2802 });
2803 const ld_ver = macho.build_tool_version{
2804 .tool = macho.TOOL_LD,
2805 .version = 0x0,
2806 };
2807 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
2808 mem.set(u8, cmd.data, 0);
2809 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
2810 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
2811 }2853 }
2854 self.objects.deinit(self.base.allocator);
28122855
2813 if (self.uuid_cmd_index == null) {2856 for (self.archives.items) |*archive| {
2814 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);2857 archive.deinit(self.base.allocator);
2815 var uuid_cmd: macho.uuid_command = .{
2816 .cmd = macho.LC_UUID,
2817 .cmdsize = @sizeOf(macho.uuid_command),
2818 .uuid = undefined,
2819 };
2820 std.crypto.random.bytes(&uuid_cmd.uuid);
2821 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
2822 }2858 }
2823}2859 self.archives.deinit(self.base.allocator);
28242860
2825fn addDataInCodeLC(self: *MachO) !void {2861 for (self.dylibs.items) |*dylib| {
2826 if (self.data_in_code_cmd_index == null) {2862 dylib.deinit(self.base.allocator);
2827 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
2828 try self.load_commands.append(self.base.allocator, .{
2829 .LinkeditData = .{
2830 .cmd = macho.LC_DATA_IN_CODE,
2831 .cmdsize = @sizeOf(macho.linkedit_data_command),
2832 .dataoff = 0,
2833 .datasize = 0,
2834 },
2835 });
2836 }2863 }
2837}2864 self.dylibs.deinit(self.base.allocator);
2865 self.dylibs_map.deinit(self.base.allocator);
2866 self.referenced_dylibs.deinit(self.base.allocator);
28382867
2839fn addCodeSignatureLC(self: *MachO) !void {2868 for (self.load_commands.items) |*lc| {
2840 if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) {2869 lc.deinit(self.base.allocator);
2841 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2842 try self.load_commands.append(self.base.allocator, .{
2843 .LinkeditData = .{
2844 .cmd = macho.LC_CODE_SIGNATURE,
2845 .cmdsize = @sizeOf(macho.linkedit_data_command),
2846 .dataoff = 0,
2847 .datasize = 0,
2848 },
2849 });
2850 }2870 }
2851}2871 self.load_commands.deinit(self.base.allocator);
28522872
2853fn addRpathLCs(self: *MachO, rpaths: []const []const u8) !void {2873 for (self.managed_atoms.items) |atom| {
2854 for (rpaths) |rpath| {2874 atom.deinit(self.base.allocator);
2855 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2875 self.base.allocator.destroy(atom);
2856 u64,2876 }
2857 @sizeOf(macho.rpath_command) + rpath.len + 1,2877 self.managed_atoms.deinit(self.base.allocator);
2858 @sizeOf(u64),2878 self.atoms.deinit(self.base.allocator);
2859 ));2879 {
2860 var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{2880 var it = self.atom_free_lists.valueIterator();
2861 .cmd = macho.LC_RPATH,2881 while (it.next()) |free_list| {
2862 .cmdsize = cmdsize,2882 free_list.deinit(self.base.allocator);
2863 .path = @sizeOf(macho.rpath_command),2883 }
2864 });2884 self.atom_free_lists.deinit(self.base.allocator);
2865 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);2885 }
2866 mem.set(u8, rpath_cmd.data, 0);2886 for (self.decls.keys()) |decl| {
2867 mem.copy(u8, rpath_cmd.data, rpath);2887 decl.link.macho.deinit(self.base.allocator);
2868 try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd });
2869 }2888 }
2889 self.decls.deinit(self.base.allocator);
2870}2890}
28712891
2872fn addLoadDylibLCs(self: *MachO) !void {2892pub fn closeFiles(self: MachO) void {
2873 for (self.referenced_dylibs.keys()) |id| {2893 for (self.objects.items) |object| {
2874 const dylib = self.dylibs.items[id];2894 object.file.close();
2875 const dylib_id = dylib.id orelse unreachable;2895 }
2876 var dylib_cmd = try commands.createLoadDylibCommand(2896 for (self.archives.items) |archive| {
2877 self.base.allocator,2897 archive.file.close();
2878 dylib_id.name,2898 }
2879 dylib_id.timestamp,2899 for (self.dylibs.items) |dylib| {
2880 dylib_id.current_version,2900 dylib.file.close();
2881 dylib_id.compatibility_version,
2882 );
2883 errdefer dylib_cmd.deinit(self.base.allocator);
2884 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
2885 }2901 }
2886}2902}
28872903
2888fn flushZld(self: *MachO) !void {2904fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection) void {
2889 self.load_commands_dirty = true;2905 log.debug("freeAtom {*}", .{atom});
2890 try self.writeTextBlocks();2906 atom.deinit(self.base.allocator);
2891 try self.writeStubHelperCommon();
28922907
2893 if (self.common_section_index) |index| {2908 const free_list = self.atom_free_lists.getPtr(match).?;
2894 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2909 var already_have_free_list_node = false;
2895 const sect = &seg.sections.items[index];2910 {
2896 sect.offset = 0;2911 var i: usize = 0;
2912 // TODO turn free_list into a hash map
2913 while (i < free_list.items.len) {
2914 if (free_list.items[i] == atom) {
2915 _ = free_list.swapRemove(i);
2916 continue;
2917 }
2918 if (free_list.items[i] == atom.prev) {
2919 already_have_free_list_node = true;
2920 }
2921 i += 1;
2922 }
2897 }2923 }
2924 // TODO process free list for dbg info just like we do above for vaddrs
28982925
2899 if (self.bss_section_index) |index| {2926 if (self.atoms.getPtr(match)) |last_atom| {
2900 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2927 if (last_atom.* == atom) {
2901 const sect = &seg.sections.items[index];2928 if (atom.prev) |prev| {
2902 sect.offset = 0;2929 // TODO shrink the section size here
2930 last_atom.* = prev;
2931 } else {
2932 _ = self.atoms.fetchRemove(match);
2933 }
2934 }
2903 }2935 }
29042936
2905 if (self.tlv_bss_section_index) |index| {2937 if (self.d_sym) |*ds| {
2906 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2938 if (ds.dbg_info_decl_first == atom) {
2907 const sect = &seg.sections.items[index];2939 ds.dbg_info_decl_first = atom.dbg_info_next;
2908 sect.offset = 0;2940 }
2941 if (ds.dbg_info_decl_last == atom) {
2942 // TODO shrink the .debug_info section size here
2943 ds.dbg_info_decl_last = atom.dbg_info_prev;
2944 }
2909 }2945 }
29102946
2911 try self.writeGotEntries();2947 if (atom.prev) |prev| {
2912 try self.setEntryPoint();2948 prev.next = atom.next;
2913 try self.writeRebaseInfoTableZld();
2914 try self.writeBindInfoTableZld();
2915 try self.writeLazyBindInfoTableZld();
2916 try self.writeExportInfoZld();
2917 try self.writeDices();
29182949
2919 {2950 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
2920 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2951 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
2921 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2952 // the OOM here.
2922 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);2953 free_list.append(self.base.allocator, prev) catch {};
2954 }
2955 } else {
2956 atom.prev = null;
2923 }2957 }
29242958
2925 try self.writeSymbolTable();2959 if (atom.next) |next| {
2926 try self.writeStringTableZld();2960 next.prev = atom.prev;
2961 } else {
2962 atom.next = null;
2963 }
29272964
2928 {2965 if (atom.dbg_info_prev) |prev| {
2929 // Seal __LINKEDIT size2966 prev.dbg_info_next = atom.dbg_info_next;
2930 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2967
2931 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);2968 // TODO the free list logic like we do for atoms above
2969 } else {
2970 atom.dbg_info_prev = null;
2932 }2971 }
29332972
2934 if (self.requires_adhoc_codesig) {2973 if (atom.dbg_info_next) |next| {
2935 try self.writeCodeSignaturePadding();2974 next.dbg_info_prev = atom.dbg_info_prev;
2975 } else {
2976 atom.dbg_info_next = null;
2936 }2977 }
2978}
29372979
2938 try self.writeLoadCommands();2980fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSection) void {
2939 try self.writeHeader();2981 _ = self;
29402982 _ = atom;
2941 if (self.requires_adhoc_codesig) {2983 _ = new_block_size;
2942 try self.writeCodeSignature();2984 _ = match;
2943 }2985 // TODO check the new capacity, and if it crosses the size threshold into a big enough
2986 // capacity, insert a free list node for it.
2944}2987}
29452988
2946fn writeGotEntries(self: *MachO) !void {2989fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
2947 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2990 const sym = self.locals.items[atom.local_sym_index];
2948 const sect = seg.sections.items[self.got_section_index.?];2991 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
2992 const need_realloc = !align_ok or new_atom_size > atom.capacity(self.*);
2993 if (!need_realloc) return sym.n_value;
2994 return self.allocateAtom(atom, new_atom_size, alignment, match);
2995}
29492996
2950 var buffer = try self.base.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64));2997pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
2951 defer self.base.allocator.free(buffer);2998 if (decl.link.macho.local_sym_index != 0) return;
29522999
2953 var stream = std.io.fixedBufferStream(buffer);3000 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
2954 var writer = stream.writer();3001 try self.decls.putNoClobber(self.base.allocator, decl, {});
29553002
2956 for (self.got_entries.items) |key| {3003 if (self.locals_free_list.popOrNull()) |i| {
2957 const address: u64 = switch (key.where) {3004 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
2958 .local => self.locals.items[key.where_index].n_value,3005 decl.link.macho.local_sym_index = i;
2959 .undef => 0,3006 } else {
2960 };3007 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
2961 try writer.writeIntLittle(u64, address);3008 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
3009 _ = self.locals.addOneAssumeCapacity();
2962 }3010 }
29633011
2964 log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len });3012 self.locals.items[decl.link.macho.local_sym_index] = .{
29653013 .n_strx = 0,
2966 try self.base.file.?.pwriteAll(buffer, sect.offset);3014 .n_type = 0,
2967}3015 .n_sect = 0,
29683016 .n_desc = 0,
2969fn setEntryPoint(self: *MachO) !void {3017 .n_value = 0,
2970 if (self.base.options.output_mode != .Exe) return;3018 };
29713019
2972 // TODO we should respect the -entry flag passed in by the user to set a custom3020 // TODO try popping from free list first before allocating a new GOT atom.
2973 // entrypoint. For now, assume default of `_main`.3021 const key = GotIndirectionKey{
2974 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;3022 .where = .local,
2975 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{3023 .where_index = decl.link.macho.local_sym_index,
2976 .bytes = &self.strtab,
2977 }) orelse {
2978 log.err("'_main' export not found", .{});
2979 return error.MissingMainEntrypoint;
2980 };3024 };
2981 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;3025 const got_atom = try self.createGotAtom(key);
2982 assert(resolv.where == .global);3026 try self.got_entries_map.put(self.base.allocator, key, got_atom);
2983 const sym = self.globals.items[resolv.where_index];
2984 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2985 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
2986 ec.stacksize = self.base.options.stack_size_override orelse 0;
2987}3027}
29883028
2989fn writeRebaseInfoTableZld(self: *MachO) !void {3029pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
2990 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);3030 if (build_options.skip_non_native and builtin.object_format != .macho) {
2991 defer pointers.deinit();3031 @panic("Attempted to compile for object format that was disabled by build configuration");
29923032 }
2993 {3033 if (build_options.have_llvm) {
2994 var it = self.blocks.iterator();3034 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
2995 while (it.next()) |entry| {3035 }
2996 const match = entry.key_ptr.*;3036 const tracy = trace(@src());
2997 var block: *TextBlock = entry.value_ptr.*;3037 defer tracy.end();
2998
2999 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
3000
3001 const seg = self.load_commands.items[match.seg].Segment;
30023038
3003 while (true) {3039 const decl = func.owner_decl;
3004 const sym = self.locals.items[block.local_sym_index];3040 // TODO clearing the code and relocs buffer should probably be orchestrated
3005 const base_offset = sym.n_value - seg.inner.vmaddr;3041 // in a different, smarter, more automatic way somewhere else, in a more centralised
3042 // way than this.
3043 // If we don't clear the buffers here, we are up for some nasty surprises when
3044 // this atom is reused later on and was not freed by freeAtom().
3045 decl.link.macho.clearRetainingCapacity();
30063046
3007 for (block.rebases.items) |offset| {3047 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3008 try pointers.append(.{3048 defer code_buffer.deinit();
3009 .offset = base_offset + offset,
3010 .segment_id = match.seg,
3011 });
3012 }
30133049
3014 if (block.prev) |prev| {3050 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3015 block = prev;3051 const debug_buffers = if (self.d_sym) |*ds| blk: {
3016 } else break;3052 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3053 break :blk &debug_buffers_buf;
3054 } else null;
3055 defer {
3056 if (debug_buffers) |dbg| {
3057 dbg.dbg_line_buffer.deinit();
3058 dbg.dbg_info_buffer.deinit();
3059 var it = dbg.dbg_info_type_relocs.valueIterator();
3060 while (it.next()) |value| {
3061 value.relocs.deinit(self.base.allocator);
3017 }3062 }
3063 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
3018 }3064 }
3019 }3065 }
30203066
3021 if (self.got_section_index) |idx| {3067 self.active_decl = decl;
3022 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3023 const sect = seg.sections.items[idx];
3024 const base_offset = sect.addr - seg.inner.vmaddr;
3025 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
3026
3027 for (self.got_entries.items) |entry, i| {
3028 if (entry.where == .undef) continue;
3029
3030 try pointers.append(.{
3031 .offset = base_offset + i * @sizeOf(u64),
3032 .segment_id = segment_id,
3033 });
3034 }
3035 }
30363068
3037 if (self.la_symbol_ptr_section_index) |idx| {3069 const res = if (debug_buffers) |dbg|
3038 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;3070 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
3039 const sect = seg.sections.items[idx];3071 .dwarf = .{
3040 const base_offset = sect.addr - seg.inner.vmaddr;3072 .dbg_line = &dbg.dbg_line_buffer,
3041 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);3073 .dbg_info = &dbg.dbg_info_buffer,
30423074 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3043 try pointers.ensureUnusedCapacity(self.stubs.items.len);3075 },
3044 for (self.stubs.items) |_, i| {3076 })
3045 pointers.appendAssumeCapacity(.{3077 else
3046 .offset = base_offset + i * @sizeOf(u64),3078 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
3047 .segment_id = segment_id,3079 switch (res) {
3048 });3080 .appended => {
3049 }3081 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3082 },
3083 .fail => |em| {
3084 decl.analysis = .codegen_failure;
3085 try module.failed_decls.put(module.gpa, decl, em);
3086 return;
3087 },
3050 }3088 }
30513089
3052 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);3090 _ = try self.placeDecl(decl, decl.link.macho.code.items.len);
3053
3054 const size = try bind.rebaseInfoSize(pointers.items);
3055 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
3056 defer self.base.allocator.free(buffer);
3057
3058 var stream = std.io.fixedBufferStream(buffer);
3059 try bind.writeRebaseInfo(pointers.items, stream.writer());
3060
3061 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3062 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3063 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
3064 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
3065 seg.inner.filesize += dyld_info.rebase_size;
30663091
3067 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });3092 if (debug_buffers) |db| {
3093 try self.d_sym.?.commitDeclDebugInfo(
3094 self.base.allocator,
3095 module,
3096 decl,
3097 db,
3098 self.base.options.target,
3099 );
3100 }
30683101
3069 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);3102 // Since we updated the vaddr and the size, each corresponding export symbol also
3103 // needs to be updated.
3104 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3105 try self.updateDeclExports(module, decl, decl_exports);
3070}3106}
30713107
3072fn writeBindInfoTableZld(self: *MachO) !void {3108pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3073 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);3109 if (build_options.skip_non_native and builtin.object_format != .macho) {
3074 defer pointers.deinit();3110 @panic("Attempted to compile for object format that was disabled by build configuration");
3075
3076 if (self.got_section_index) |idx| {
3077 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3078 const sect = seg.sections.items[idx];
3079 const base_offset = sect.addr - seg.inner.vmaddr;
3080 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
3081
3082 for (self.got_entries.items) |entry, i| {
3083 if (entry.where == .local) continue;
3084
3085 const sym = self.undefs.items[entry.where_index];
3086 try pointers.append(.{
3087 .offset = base_offset + i * @sizeOf(u64),
3088 .segment_id = segment_id,
3089 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
3090 .name = self.getString(sym.n_strx),
3091 });
3092 }
3093 }3111 }
3112 if (build_options.have_llvm) {
3113 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
3114 }
3115 const tracy = trace(@src());
3116 defer tracy.end();
30943117
3095 {3118 if (decl.val.tag() == .extern_fn) {
3096 var it = self.blocks.iterator();3119 return; // TODO Should we do more when front-end analyzed extern decl?
3097 while (it.next()) |entry| {3120 }
3098 const match = entry.key_ptr.*;
3099 var block: *TextBlock = entry.value_ptr.*;
3100
3101 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
3102
3103 const seg = self.load_commands.items[match.seg].Segment;
3104
3105 while (true) {
3106 const sym = self.locals.items[block.local_sym_index];
3107 const base_offset = sym.n_value - seg.inner.vmaddr;
31083121
3109 for (block.bindings.items) |binding| {3122 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3110 const bind_sym = self.undefs.items[binding.local_sym_index];3123 defer code_buffer.deinit();
3111 try pointers.append(.{
3112 .offset = binding.offset + base_offset,
3113 .segment_id = match.seg,
3114 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
3115 .name = self.getString(bind_sym.n_strx),
3116 });
3117 }
31183124
3119 if (block.prev) |prev| {3125 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3120 block = prev;3126 const debug_buffers = if (self.d_sym) |*ds| blk: {
3121 } else break;3127 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3128 break :blk &debug_buffers_buf;
3129 } else null;
3130 defer {
3131 if (debug_buffers) |dbg| {
3132 dbg.dbg_line_buffer.deinit();
3133 dbg.dbg_info_buffer.deinit();
3134 var it = dbg.dbg_info_type_relocs.valueIterator();
3135 while (it.next()) |value| {
3136 value.relocs.deinit(self.base.allocator);
3122 }3137 }
3138 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
3123 }3139 }
3124 }3140 }
31253141
3126 const size = try bind.bindInfoSize(pointers.items);3142 self.active_decl = decl;
3127 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
3128 defer self.base.allocator.free(buffer);
3129
3130 var stream = std.io.fixedBufferStream(buffer);
3131 try bind.writeBindInfo(pointers.items, stream.writer());
31323143
3133 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;3144 const res = if (debug_buffers) |dbg|
3134 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3145 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3135 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);3146 .ty = decl.ty,
3136 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));3147 .val = decl.val,
3137 seg.inner.filesize += dyld_info.bind_size;3148 }, &code_buffer, .{
3149 .dwarf = .{
3150 .dbg_line = &dbg.dbg_line_buffer,
3151 .dbg_info = &dbg.dbg_info_buffer,
3152 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3153 },
3154 })
3155 else
3156 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3157 .ty = decl.ty,
3158 .val = decl.val,
3159 }, &code_buffer, .none);
31383160
3139 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });3161 const code = blk: {
3162 switch (res) {
3163 .externally_managed => |x| break :blk x,
3164 .appended => {
3165 // TODO clearing the code and relocs buffer should probably be orchestrated
3166 // in a different, smarter, more automatic way somewhere else, in a more centralised
3167 // way than this.
3168 // If we don't clear the buffers here, we are up for some nasty surprises when
3169 // this atom is reused later on and was not freed by freeAtom().
3170 decl.link.macho.code.clearAndFree(self.base.allocator);
3171 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3172 break :blk decl.link.macho.code.items;
3173 },
3174 .fail => |em| {
3175 decl.analysis = .codegen_failure;
3176 try module.failed_decls.put(module.gpa, decl, em);
3177 return;
3178 },
3179 }
3180 };
3181 _ = try self.placeDecl(decl, code.len);
31403182
3141 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);3183 // Since we updated the vaddr and the size, each corresponding export symbol also
3184 // needs to be updated.
3185 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3186 try self.updateDeclExports(module, decl, decl_exports);
3142}3187}
31433188
3144fn writeLazyBindInfoTableZld(self: *MachO) !void {3189fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
3145 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);3190 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
3146 defer pointers.deinit();3191 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3192 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
31473193
3148 if (self.la_symbol_ptr_section_index) |idx| {3194 if (decl.link.macho.size != 0) {
3149 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;3195 const capacity = decl.link.macho.capacity(self.*);
3150 const sect = seg.sections.items[idx];3196 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3151 const base_offset = sect.addr - seg.inner.vmaddr;3197 if (need_realloc) {
3152 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);3198 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, .{
31533199 .seg = self.text_segment_cmd_index.?,
3154 try pointers.ensureUnusedCapacity(self.stubs.items.len);3200 .sect = self.text_section_index.?,
3155
3156 for (self.stubs.items) |import_id, i| {
3157 const sym = self.undefs.items[import_id];
3158 pointers.appendAssumeCapacity(.{
3159 .offset = base_offset + i * @sizeOf(u64),
3160 .segment_id = segment_id,
3161 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
3162 .name = self.getString(sym.n_strx),
3163 });3201 });
3164 }
3165 }
3166
3167 const size = try bind.lazyBindInfoSize(pointers.items);
3168 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
3169 defer self.base.allocator.free(buffer);
31703202
3171 var stream = std.io.fixedBufferStream(buffer);3203 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
3172 try bind.writeLazyBindInfo(pointers.items, stream.writer());
31733204
3174 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;3205 if (vaddr != symbol.n_value) {
3175 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3206 log.debug(" (writing new GOT entry)", .{});
3176 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);3207 const got_atom = self.got_entries_map.get(.{
3177 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));3208 .where = .local,
3178 seg.inner.filesize += dyld_info.lazy_bind_size;3209 .where_index = decl.link.macho.local_sym_index,
3210 }) orelse unreachable;
3211 const got_sym = &self.locals.items[got_atom.local_sym_index];
3212 const got_vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
3213 .seg = self.data_const_segment_cmd_index.?,
3214 .sect = self.got_section_index.?,
3215 });
3216 got_sym.n_value = got_vaddr;
3217 got_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{
3218 .seg = self.data_const_segment_cmd_index.?,
3219 .sect = self.got_section_index.?,
3220 }).? + 1);
3221 }
31793222
3180 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 });3223 symbol.n_value = vaddr;
3224 } else if (code_len < decl.link.macho.size) {
3225 self.shrinkAtom(&decl.link.macho, code_len, .{
3226 .seg = self.text_segment_cmd_index.?,
3227 .sect = self.text_section_index.?,
3228 });
3229 }
3230 decl.link.macho.size = code_len;
3231 decl.link.macho.dirty = true;
31813232
3182 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);3233 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
3183 try self.populateLazyBindOffsetsInStubHelper(buffer);3234 defer self.base.allocator.free(new_name);
3184}
31853235
3186fn writeExportInfoZld(self: *MachO) !void {3236 symbol.n_strx = try self.makeString(new_name);
3187 var trie: Trie = .{};3237 symbol.n_type = macho.N_SECT;
3188 defer trie.deinit(self.base.allocator);3238 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
3239 symbol.n_desc = 0;
3240 } else {
3241 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
3242 defer self.base.allocator.free(decl_name);
31893243
3190 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;3244 const name_str_index = try self.makeString(decl_name);
3191 const base_address = text_segment.inner.vmaddr;3245 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, .{
3246 .seg = self.text_segment_cmd_index.?,
3247 .sect = self.text_section_index.?,
3248 });
31923249
3193 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.3250 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, addr });
3194 log.debug("writing export trie", .{});
31953251
3196 for (self.globals.items) |sym| {3252 errdefer self.freeAtom(&decl.link.macho, .{
3197 const sym_name = self.getString(sym.n_strx);3253 .seg = self.text_segment_cmd_index.?,
3198 log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });3254 .sect = self.text_section_index.?,
3255 });
31993256
3200 try trie.put(self.base.allocator, .{3257 symbol.* = .{
3201 .name = sym_name,3258 .n_strx = name_str_index,
3202 .vmaddr_offset = sym.n_value - base_address,3259 .n_type = macho.N_SECT,
3203 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,3260 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3261 .n_desc = 0,
3262 .n_value = addr,
3263 };
3264 const got_atom = self.got_entries_map.get(.{
3265 .where = .local,
3266 .where_index = decl.link.macho.local_sym_index,
3267 }) orelse unreachable;
3268 const got_sym = &self.locals.items[got_atom.local_sym_index];
3269 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
3270 .seg = self.data_const_segment_cmd_index.?,
3271 .sect = self.got_section_index.?,
3204 });3272 });
3273 got_sym.n_value = vaddr;
3274 got_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{
3275 .seg = self.data_const_segment_cmd_index.?,
3276 .sect = self.got_section_index.?,
3277 }).? + 1);
3205 }3278 }
32063279
3207 try trie.finalize(self.base.allocator);3280 return symbol;
3208
3209 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size));
3210 defer self.base.allocator.free(buffer);
3211
3212 var stream = std.io.fixedBufferStream(buffer);
3213 const nwritten = try trie.write(stream.writer());
3214 assert(nwritten == trie.size);
3215
3216 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3217 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3218 dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3219 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
3220 seg.inner.filesize += dyld_info.export_size;
3221
3222 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
3223
3224 try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);
3225}3281}
32263282
3227fn writeSymbolTable(self: *MachO) !void {3283pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
3228 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;3284 if (self.d_sym) |*ds| {
3229 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;3285 try ds.updateDeclLineNumber(module, decl);
3286 }
3287}
32303288
3231 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);3289pub fn updateDeclExports(
3232 defer locals.deinit();3290 self: *MachO,
3233 try locals.appendSlice(self.locals.items);3291 module: *Module,
3292 decl: *Module.Decl,
3293 exports: []const *Module.Export,
3294) !void {
3295 // TODO If we are exporting with global linkage, check for already defined globals and flag
3296 // symbol duplicate/collision!
3297 if (build_options.skip_non_native and builtin.object_format != .macho) {
3298 @panic("Attempted to compile for object format that was disabled by build configuration");
3299 }
3300 if (build_options.have_llvm) {
3301 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
3302 }
3303 const tracy = trace(@src());
3304 defer tracy.end();
32343305
3235 if (self.has_stabs) {3306 try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len);
3236 for (self.objects.items) |object| {3307 if (decl.link.macho.local_sym_index == 0) return;
3237 if (object.debug_info == null) continue;3308 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
32383309
3239 // Open scope3310 for (exports) |exp| {
3240 try locals.ensureUnusedCapacity(3);3311 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
3241 locals.appendAssumeCapacity(.{3312 defer self.base.allocator.free(exp_name);
3242 .n_strx = try self.makeString(object.tu_comp_dir.?),
3243 .n_type = macho.N_SO,
3244 .n_sect = 0,
3245 .n_desc = 0,
3246 .n_value = 0,
3247 });
3248 locals.appendAssumeCapacity(.{
3249 .n_strx = try self.makeString(object.tu_name.?),
3250 .n_type = macho.N_SO,
3251 .n_sect = 0,
3252 .n_desc = 0,
3253 .n_value = 0,
3254 });
3255 locals.appendAssumeCapacity(.{
3256 .n_strx = try self.makeString(object.name),
3257 .n_type = macho.N_OSO,
3258 .n_sect = 0,
3259 .n_desc = 1,
3260 .n_value = object.mtime orelse 0,
3261 });
32623313
3263 for (object.text_blocks.items) |block| {3314 if (exp.options.section) |section_name| {
3264 if (block.stab) |stab| {3315 if (!mem.eql(u8, section_name, "__text")) {
3265 const nlists = try stab.asNlists(block.local_sym_index, self);3316 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3266 defer self.base.allocator.free(nlists);3317 module.failed_exports.putAssumeCapacityNoClobber(
3267 try locals.appendSlice(nlists);3318 exp,
3268 } else {3319 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
3269 for (block.contained.items) |sym_at_off| {3320 );
3270 const stab = sym_at_off.stab orelse continue;3321 continue;
3271 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
3272 defer self.base.allocator.free(nlists);
3273 try locals.appendSlice(nlists);
3274 }
3275 }
3276 }3322 }
3277
3278 // Close scope
3279 try locals.append(.{
3280 .n_strx = 0,
3281 .n_type = macho.N_SO,
3282 .n_sect = 0,
3283 .n_desc = 0,
3284 .n_value = 0,
3285 });
3286 }3323 }
3287 }
3288
3289 const nlocals = locals.items.len;
3290 const nexports = self.globals.items.len;
3291 const nundefs = self.undefs.items.len;
3292
3293 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
3294 const locals_size = nlocals * @sizeOf(macho.nlist_64);
3295 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
3296 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
3297
3298 const exports_off = locals_off + locals_size;
3299 const exports_size = nexports * @sizeOf(macho.nlist_64);
3300 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
3301 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
3302
3303 const undefs_off = exports_off + exports_size;
3304 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
3305 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
3306 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
3307
3308 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
3309 seg.inner.filesize += locals_size + exports_size + undefs_size;
3310
3311 // Update dynamic symbol table.
3312 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3313 dysymtab.nlocalsym += @intCast(u32, nlocals);
3314 dysymtab.iextdefsym = dysymtab.nlocalsym;
3315 dysymtab.nextdefsym = @intCast(u32, nexports);
3316 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
3317 dysymtab.nundefsym = @intCast(u32, nundefs);
3318
3319 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3320 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
3321 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3322 const got = &data_const_segment.sections.items[self.got_section_index.?];
3323 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3324 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
3325
3326 const nstubs = @intCast(u32, self.stubs.items.len);
3327 const ngot_entries = @intCast(u32, self.got_entries.items.len);
3328
3329 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3330 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
3331
3332 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
3333 seg.inner.filesize += needed_size;
3334
3335 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
3336 dysymtab.indirectsymoff,
3337 dysymtab.indirectsymoff + needed_size,
3338 });
33393324
3340 var buf = try self.base.allocator.alloc(u8, needed_size);3325 var n_type: u8 = macho.N_SECT | macho.N_EXT;
3341 defer self.base.allocator.free(buf);3326 var n_desc: u16 = 0;
3342
3343 var stream = std.io.fixedBufferStream(buf);
3344 var writer = stream.writer();
3345
3346 stubs.reserved1 = 0;
3347 for (self.stubs.items) |id| {
3348 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3349 }
33503327
3351 got.reserved1 = nstubs;3328 switch (exp.options.linkage) {
3352 for (self.got_entries.items) |entry| {3329 .Internal => {
3353 switch (entry.where) {3330 // Symbol should be hidden, or in MachO lingo, private extern.
3354 .undef => {3331 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
3355 try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index);3332 // TODO work out when to add N_WEAK_REF.
3333 n_type |= macho.N_PEXT;
3334 n_desc |= macho.N_WEAK_DEF;
3356 },3335 },
3357 .local => {3336 .Strong => {},
3358 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);3337 .Weak => {
3338 // Weak linkage is specified as part of n_desc field.
3339 // Symbol's n_type is like for a symbol with strong linkage.
3340 n_desc |= macho.N_WEAK_DEF;
3341 },
3342 .LinkOnce => {
3343 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3344 module.failed_exports.putAssumeCapacityNoClobber(
3345 exp,
3346 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
3347 );
3348 continue;
3359 },3349 },
3360 }3350 }
3361 }
33623351
3363 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;3352 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
3364 for (self.stubs.items) |id| {3353 const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
3365 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);3354 _ = self.globals.addOneAssumeCapacity();
3366 }3355 break :inner @intCast(u32, self.globals.items.len - 1);
3356 };
3357 break :blk i;
3358 };
33673359
3368 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);3360 const n_strx = try self.makeString(exp_name);
3369}3361 const sym = &self.globals.items[global_sym_index];
3362 sym.* = .{
3363 .n_strx = try self.makeString(exp_name),
3364 .n_type = n_type,
3365 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3366 .n_desc = n_desc,
3367 .n_value = decl_sym.n_value,
3368 };
3369 exp.link.macho.sym_index = global_sym_index;
33703370
3371pub fn deinit(self: *MachO) void {3371 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, n_strx);
3372 if (build_options.have_llvm) {3372 resolv.value_ptr.* = .{
3373 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);3373 .where = .global,
3374 .where_index = global_sym_index,
3375 .local_sym_index = decl.link.macho.local_sym_index,
3376 };
3374 }3377 }
3378}
33753379
3376 if (self.d_sym) |*ds| {3380pub fn deleteExport(self: *MachO, exp: Export) void {
3377 ds.deinit(self.base.allocator);3381 const sym_index = exp.sym_index orelse return;
3378 }3382 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3383 const global = &self.globals.items[sym_index];
3384 global.n_type = 0;
3385 assert(self.symbol_resolver.remove(global.n_strx));
3386}
33793387
3380 self.section_ordinals.deinit(self.base.allocator);3388pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3381 self.pending_updates.deinit(self.base.allocator);3389 log.debug("freeDecl {*}", .{decl});
3382 self.got_entries.deinit(self.base.allocator);3390 _ = self.decls.swapRemove(decl);
3383 self.got_entries_map.deinit(self.base.allocator);3391 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3384 self.got_entries_free_list.deinit(self.base.allocator);3392 self.freeAtom(&decl.link.macho, .{
3385 self.stubs.deinit(self.base.allocator);3393 .seg = self.text_segment_cmd_index.?,
3386 self.stubs_map.deinit(self.base.allocator);3394 .sect = self.text_section_index.?,
3387 self.strtab_dir.deinit(self.base.allocator);3395 });
3388 self.strtab.deinit(self.base.allocator);3396 if (decl.link.macho.local_sym_index != 0) {
3389 self.undefs.deinit(self.base.allocator);3397 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
3390 self.globals.deinit(self.base.allocator);
3391 self.globals_free_list.deinit(self.base.allocator);
3392 self.locals.deinit(self.base.allocator);
3393 self.locals_free_list.deinit(self.base.allocator);
3394 self.symbol_resolver.deinit(self.base.allocator);
33953398
3396 for (self.objects.items) |*object| {3399 // TODO free GOT atom here.
3397 object.deinit(self.base.allocator);
3398 }
3399 self.objects.deinit(self.base.allocator);
34003400
3401 for (self.archives.items) |*archive| {3401 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
3402 archive.deinit(self.base.allocator);3402 decl.link.macho.local_sym_index = 0;
3403 }3403 }
3404 self.archives.deinit(self.base.allocator);3404 if (self.d_sym) |*ds| {
34053405 // TODO make this logic match freeAtom. Maybe abstract the logic
3406 for (self.dylibs.items) |*dylib| {3406 // out since the same thing is desired for both.
3407 dylib.deinit(self.base.allocator);3407 _ = ds.dbg_line_fn_free_list.remove(&decl.fn_link.macho);
3408 if (decl.fn_link.macho.prev) |prev| {
3409 ds.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
3410 prev.next = decl.fn_link.macho.next;
3411 if (decl.fn_link.macho.next) |next| {
3412 next.prev = prev;
3413 } else {
3414 ds.dbg_line_fn_last = prev;
3415 }
3416 } else if (decl.fn_link.macho.next) |next| {
3417 ds.dbg_line_fn_first = next;
3418 next.prev = null;
3419 }
3420 if (ds.dbg_line_fn_first == &decl.fn_link.macho) {
3421 ds.dbg_line_fn_first = decl.fn_link.macho.next;
3422 }
3423 if (ds.dbg_line_fn_last == &decl.fn_link.macho) {
3424 ds.dbg_line_fn_last = decl.fn_link.macho.prev;
3425 }
3408 }3426 }
3409 self.dylibs.deinit(self.base.allocator);3427}
3410 self.dylibs_map.deinit(self.base.allocator);
3411 self.referenced_dylibs.deinit(self.base.allocator);
34123428
3413 for (self.load_commands.items) |*lc| {3429pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
3414 lc.deinit(self.base.allocator);3430 assert(decl.link.macho.local_sym_index != 0);
3415 }3431 return self.locals.items[decl.link.macho.local_sym_index].n_value;
3416 self.load_commands.deinit(self.base.allocator);3432}
34173433
3418 for (self.managed_blocks.items) |block| {3434pub fn populateMissingMetadata(self: *MachO) !void {
3419 block.deinit(self.base.allocator);3435 if (self.pagezero_segment_cmd_index == null) {
3420 self.base.allocator.destroy(block);3436 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3437 try self.load_commands.append(self.base.allocator, .{
3438 .Segment = .{
3439 .inner = .{
3440 .segname = makeStaticString("__PAGEZERO"),
3441 .vmsize = pagezero_vmsize,
3442 },
3443 },
3444 });
3445 self.load_commands_dirty = true;
3421 }3446 }
3422 self.managed_blocks.deinit(self.base.allocator);
3423 self.blocks.deinit(self.base.allocator);
3424 self.text_block_free_list.deinit(self.base.allocator);
34253447
3426 for (self.decls.keys()) |decl| {3448 if (self.text_segment_cmd_index == null) {
3427 decl.link.macho.deinit(self.base.allocator);3449 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3450 const program_code_size_hint = self.base.options.program_code_size_hint;
3451 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
3452 const ideal_size = self.header_pad + program_code_size_hint + got_size_hint;
3453 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3454
3455 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
3456
3457 try self.load_commands.append(self.base.allocator, .{
3458 .Segment = .{
3459 .inner = .{
3460 .segname = makeStaticString("__TEXT"),
3461 .vmaddr = pagezero_vmsize,
3462 .vmsize = needed_size,
3463 .filesize = needed_size,
3464 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
3465 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
3466 },
3467 },
3468 });
3469 self.load_commands_dirty = true;
3428 }3470 }
3429 self.decls.deinit(self.base.allocator);
3430}
34313471
3432pub fn closeFiles(self: MachO) void {3472 if (self.text_section_index == null) {
3433 for (self.objects.items) |object| {3473 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
3434 object.file.close();3474 .x86_64 => 0,
3475 .aarch64 => 2,
3476 else => unreachable, // unhandled architecture type
3477 };
3478 const needed_size = self.base.options.program_code_size_hint;
3479 self.text_section_index = try self.allocateSection(
3480 self.text_segment_cmd_index.?,
3481 "__text",
3482 needed_size,
3483 alignment,
3484 .{
3485 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3486 },
3487 );
3435 }3488 }
3436 for (self.archives.items) |archive| {3489
3437 archive.file.close();3490 if (self.stubs_section_index == null) {
3491 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
3492 .x86_64 => 0,
3493 .aarch64 => 2,
3494 else => unreachable, // unhandled architecture type
3495 };
3496 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
3497 .x86_64 => 6,
3498 .aarch64 => 3 * @sizeOf(u32),
3499 else => unreachable, // unhandled architecture type
3500 };
3501 const needed_size = stub_size * self.base.options.symbol_count_hint;
3502 self.stubs_section_index = try self.allocateSection(
3503 self.text_segment_cmd_index.?,
3504 "__stubs",
3505 needed_size,
3506 alignment,
3507 .{
3508 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3509 .reserved2 = stub_size,
3510 },
3511 );
3438 }3512 }
3439 for (self.dylibs.items) |dylib| {3513
3440 dylib.file.close();3514 if (self.stub_helper_section_index == null) {
3515 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
3516 .x86_64 => 0,
3517 .aarch64 => 2,
3518 else => unreachable, // unhandled architecture type
3519 };
3520 const preamble_size: u6 = switch (self.base.options.target.cpu.arch) {
3521 .x86_64 => 15,
3522 .aarch64 => 6 * @sizeOf(u32),
3523 else => unreachable,
3524 };
3525 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
3526 .x86_64 => 10,
3527 .aarch64 => 3 * @sizeOf(u32),
3528 else => unreachable,
3529 };
3530 const needed_size = stub_size * self.base.options.symbol_count_hint + preamble_size;
3531 self.stub_helper_section_index = try self.allocateSection(
3532 self.text_segment_cmd_index.?,
3533 "__stub_helper",
3534 needed_size,
3535 alignment,
3536 .{
3537 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3538 },
3539 );
3441 }3540 }
3442}
34433541
3444fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {3542 if (self.data_const_segment_cmd_index == null) {
3445 log.debug("freeTextBlock {*}", .{text_block});3543 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3446 text_block.deinit(self.base.allocator);3544 const address_and_offset = self.nextSegmentAddressAndOffset();
3545 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3546 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
34473547
3448 var already_have_free_list_node = false;3548 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
3449 {3549 address_and_offset.offset,
3450 var i: usize = 0;3550 address_and_offset.offset + needed_size,
3451 // TODO turn text_block_free_list into a hash map3551 });
3452 while (i < self.text_block_free_list.items.len) {
3453 if (self.text_block_free_list.items[i] == text_block) {
3454 _ = self.text_block_free_list.swapRemove(i);
3455 continue;
3456 }
3457 if (self.text_block_free_list.items[i] == text_block.prev) {
3458 already_have_free_list_node = true;
3459 }
3460 i += 1;
3461 }
3462 }
3463 // TODO process free list for dbg info just like we do above for vaddrs
34643552
3465 if (self.last_text_block == text_block) {3553 try self.load_commands.append(self.base.allocator, .{
3466 // TODO shrink the __text section size here3554 .Segment = .{
3467 self.last_text_block = text_block.prev;3555 .inner = .{
3556 .segname = makeStaticString("__DATA_CONST"),
3557 .vmaddr = address_and_offset.address,
3558 .vmsize = needed_size,
3559 .fileoff = address_and_offset.offset,
3560 .filesize = needed_size,
3561 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
3562 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
3563 },
3564 },
3565 });
3566 self.load_commands_dirty = true;
3468 }3567 }
3469 if (self.d_sym) |*ds| {3568
3470 if (ds.dbg_info_decl_first == text_block) {3569 if (self.got_section_index == null) {
3471 ds.dbg_info_decl_first = text_block.dbg_info_next;3570 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3472 }3571 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3473 if (ds.dbg_info_decl_last == text_block) {3572 self.got_section_index = try self.allocateSection(
3474 // TODO shrink the .debug_info section size here3573 self.data_const_segment_cmd_index.?,
3475 ds.dbg_info_decl_last = text_block.dbg_info_prev;3574 "__got",
3476 }3575 needed_size,
3576 alignment,
3577 .{
3578 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3579 },
3580 );
3477 }3581 }
34783582
3479 if (text_block.prev) |prev| {3583 if (self.data_segment_cmd_index == null) {
3480 prev.next = text_block.next;3584 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3585 const address_and_offset = self.nextSegmentAddressAndOffset();
3586 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
3587 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
34813588
3482 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {3589 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
3483 // The free list is heuristics, it doesn't have to be perfect, so we can ignore3590
3484 // the OOM here.3591 try self.load_commands.append(self.base.allocator, .{
3485 self.text_block_free_list.append(self.base.allocator, prev) catch {};3592 .Segment = .{
3486 }3593 .inner = .{
3487 } else {3594 .segname = makeStaticString("__DATA"),
3488 text_block.prev = null;3595 .vmaddr = address_and_offset.address,
3596 .vmsize = needed_size,
3597 .fileoff = address_and_offset.offset,
3598 .filesize = needed_size,
3599 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
3600 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
3601 },
3602 },
3603 });
3604 self.load_commands_dirty = true;
3489 }3605 }
34903606
3491 if (text_block.next) |next| {3607 if (self.la_symbol_ptr_section_index == null) {
3492 next.prev = text_block.prev;3608 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3493 } else {3609 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3494 text_block.next = null;3610 self.la_symbol_ptr_section_index = try self.allocateSection(
3611 self.data_segment_cmd_index.?,
3612 "__la_symbol_ptr",
3613 needed_size,
3614 alignment,
3615 .{
3616 .flags = macho.S_LAZY_SYMBOL_POINTERS,
3617 },
3618 );
3495 }3619 }
34963620
3497 if (text_block.dbg_info_prev) |prev| {3621 if (self.data_section_index == null) {
3498 prev.dbg_info_next = text_block.dbg_info_next;3622 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3623 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3624 self.data_section_index = try self.allocateSection(
3625 self.data_segment_cmd_index.?,
3626 "__data",
3627 needed_size,
3628 alignment,
3629 .{},
3630 );
3631 }
34993632
3500 // TODO the free list logic like we do for text blocks above3633 if (self.tlv_section_index == null) {
3501 } else {3634 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3502 text_block.dbg_info_prev = null;3635 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3636 self.tlv_section_index = try self.allocateSection(
3637 self.data_segment_cmd_index.?,
3638 "__thread_vars",
3639 needed_size,
3640 alignment,
3641 .{
3642 .flags = macho.S_THREAD_LOCAL_VARIABLES,
3643 },
3644 );
3503 }3645 }
35043646
3505 if (text_block.dbg_info_next) |next| {3647 if (self.tlv_data_section_index == null) {
3506 next.dbg_info_prev = text_block.dbg_info_prev;3648 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3507 } else {3649 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3508 text_block.dbg_info_next = null;3650 self.tlv_data_section_index = try self.allocateSection(
3651 self.data_segment_cmd_index.?,
3652 "__thread_data",
3653 needed_size,
3654 alignment,
3655 .{
3656 .flags = macho.S_THREAD_LOCAL_REGULAR,
3657 },
3658 );
3509 }3659 }
3510}
35113660
3512fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void {3661 if (self.tlv_bss_section_index == null) {
3513 _ = self;3662 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3514 _ = text_block;3663 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3515 _ = new_block_size;3664 self.tlv_bss_section_index = try self.allocateSection(
3516 // TODO check the new capacity, and if it crosses the size threshold into a big enough3665 self.data_segment_cmd_index.?,
3517 // capacity, insert a free list node for it.3666 "__thread_bss",
3518}3667 needed_size,
3668 alignment,
3669 .{
3670 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
3671 },
3672 );
3673 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3674 const sect = seg.sections.items[self.tlv_bss_section_index.?];
3675 self.tlv_bss_file_offset = sect.offset;
3676 }
35193677
3520fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {3678 if (self.bss_section_index == null) {
3521 const sym = self.locals.items[text_block.local_sym_index];3679 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3522 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;3680 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3523 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);3681 self.bss_section_index = try self.allocateSection(
3524 if (!need_realloc) return sym.n_value;3682 self.data_segment_cmd_index.?,
3525 return self.allocateTextBlock(text_block, new_block_size, alignment);3683 "__bss",
3526}3684 needed_size,
3685 alignment,
3686 .{
3687 .flags = macho.S_ZEROFILL,
3688 },
3689 );
3690 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3691 const sect = seg.sections.items[self.bss_section_index.?];
3692 self.bss_file_offset = sect.offset;
3693 }
35273694
3528pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {3695 if (self.linkedit_segment_cmd_index == null) {
3529 if (decl.link.macho.local_sym_index != 0) return;3696 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3697 const address_and_offset = self.nextSegmentAddressAndOffset();
35303698
3531 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);3699 log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset});
3532 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);
35333700
3534 try self.decls.putNoClobber(self.base.allocator, decl, {});3701 try self.load_commands.append(self.base.allocator, .{
3702 .Segment = .{
3703 .inner = .{
3704 .segname = makeStaticString("__LINKEDIT"),
3705 .vmaddr = address_and_offset.address,
3706 .fileoff = address_and_offset.offset,
3707 .maxprot = macho.VM_PROT_READ,
3708 .initprot = macho.VM_PROT_READ,
3709 },
3710 },
3711 });
3712 self.load_commands_dirty = true;
3713 }
35353714
3536 if (self.locals_free_list.popOrNull()) |i| {3715 if (self.dyld_info_cmd_index == null) {
3537 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });3716 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
3538 decl.link.macho.local_sym_index = i;3717 try self.load_commands.append(self.base.allocator, .{
3539 } else {3718 .DyldInfoOnly = .{
3540 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });3719 .cmd = macho.LC_DYLD_INFO_ONLY,
3541 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);3720 .cmdsize = @sizeOf(macho.dyld_info_command),
3542 _ = self.locals.addOneAssumeCapacity();3721 .rebase_off = 0,
3722 .rebase_size = 0,
3723 .bind_off = 0,
3724 .bind_size = 0,
3725 .weak_bind_off = 0,
3726 .weak_bind_size = 0,
3727 .lazy_bind_off = 0,
3728 .lazy_bind_size = 0,
3729 .export_off = 0,
3730 .export_size = 0,
3731 },
3732 });
3733 self.load_commands_dirty = true;
3543 }3734 }
35443735
3545 const got_index: u32 = blk: {3736 if (self.symtab_cmd_index == null) {
3546 if (self.got_entries_free_list.popOrNull()) |i| {3737 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
3547 log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name });3738 try self.load_commands.append(self.base.allocator, .{
3548 break :blk i;3739 .Symtab = .{
3549 } else {3740 .cmd = macho.LC_SYMTAB,
3550 const got_index = @intCast(u32, self.got_entries.items.len);3741 .cmdsize = @sizeOf(macho.symtab_command),
3551 log.debug("allocating GOT entry index {d} for {s}", .{ got_index, decl.name });3742 .symoff = 0,
3552 _ = self.got_entries.addOneAssumeCapacity();3743 .nsyms = 0,
3553 self.got_entries_count_dirty = true;3744 .stroff = 0,
3554 self.rebase_info_dirty = true;3745 .strsize = 0,
3555 break :blk got_index;3746 },
3556 }3747 });
3557 };3748 self.load_commands_dirty = true;
3558
3559 self.locals.items[decl.link.macho.local_sym_index] = .{
3560 .n_strx = 0,
3561 .n_type = 0,
3562 .n_sect = 0,
3563 .n_desc = 0,
3564 .n_value = 0,
3565 };
3566 const got_entry = GotIndirectionKey{
3567 .where = .local,
3568 .where_index = decl.link.macho.local_sym_index,
3569 };
3570 self.got_entries.items[got_index] = got_entry;
3571 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index);
3572}
3573
3574pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
3575 if (build_options.skip_non_native and builtin.object_format != .macho) {
3576 @panic("Attempted to compile for object format that was disabled by build configuration");
3577 }
3578 if (build_options.have_llvm) {
3579 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
3580 }
3581 const tracy = trace(@src());
3582 defer tracy.end();
3583
3584 const decl = func.owner_decl;
3585
3586 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3587 defer code_buffer.deinit();
3588
3589 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3590 const debug_buffers = if (self.d_sym) |*ds| blk: {
3591 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3592 break :blk &debug_buffers_buf;
3593 } else null;
3594 defer {
3595 if (debug_buffers) |dbg| {
3596 dbg.dbg_line_buffer.deinit();
3597 dbg.dbg_info_buffer.deinit();
3598 var it = dbg.dbg_info_type_relocs.valueIterator();
3599 while (it.next()) |value| {
3600 value.relocs.deinit(self.base.allocator);
3601 }
3602 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
3603 }
3604 }
3605
3606 self.active_decl = decl;
3607
3608 const res = if (debug_buffers) |dbg|
3609 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
3610 .dwarf = .{
3611 .dbg_line = &dbg.dbg_line_buffer,
3612 .dbg_info = &dbg.dbg_info_buffer,
3613 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3614 },
3615 })
3616 else
3617 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
3618 switch (res) {
3619 .appended => {
3620 // TODO clearing the code and relocs buffer should probably be orchestrated
3621 // in a different, smarter, more automatic way somewhere else, in a more centralised
3622 // way than this.
3623 // If we don't clear the buffers here, we are up for some nasty surprises when
3624 // this TextBlock is reused later on and was not freed by freeTextBlock().
3625 decl.link.macho.code.clearAndFree(self.base.allocator);
3626 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3627 },
3628 .fail => |em| {
3629 decl.analysis = .codegen_failure;
3630 try module.failed_decls.put(module.gpa, decl, em);
3631 return;
3632 },
3633 }
3634
3635 const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len);
3636
3637 try self.writeCode(symbol, decl.link.macho.code.items);
3638
3639 if (debug_buffers) |db| {
3640 try self.d_sym.?.commitDeclDebugInfo(
3641 self.base.allocator,
3642 module,
3643 decl,
3644 db,
3645 self.base.options.target,
3646 );
3647 }
3648
3649 // Since we updated the vaddr and the size, each corresponding export symbol also
3650 // needs to be updated.
3651 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3652 try self.updateDeclExports(module, decl, decl_exports);
3653}
3654
3655pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3656 if (build_options.skip_non_native and builtin.object_format != .macho) {
3657 @panic("Attempted to compile for object format that was disabled by build configuration");
3658 }
3659 if (build_options.have_llvm) {
3660 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
3661 }
3662 const tracy = trace(@src());
3663 defer tracy.end();
3664
3665 if (decl.val.tag() == .extern_fn) {
3666 return; // TODO Should we do more when front-end analyzed extern decl?
3667 }
3668
3669 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3670 defer code_buffer.deinit();
3671
3672 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3673 const debug_buffers = if (self.d_sym) |*ds| blk: {
3674 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3675 break :blk &debug_buffers_buf;
3676 } else null;
3677 defer {
3678 if (debug_buffers) |dbg| {
3679 dbg.dbg_line_buffer.deinit();
3680 dbg.dbg_info_buffer.deinit();
3681 var it = dbg.dbg_info_type_relocs.valueIterator();
3682 while (it.next()) |value| {
3683 value.relocs.deinit(self.base.allocator);
3684 }
3685 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
3686 }
3687 }
3688
3689 self.active_decl = decl;
3690
3691 const res = if (debug_buffers) |dbg|
3692 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3693 .ty = decl.ty,
3694 .val = decl.val,
3695 }, &code_buffer, .{
3696 .dwarf = .{
3697 .dbg_line = &dbg.dbg_line_buffer,
3698 .dbg_info = &dbg.dbg_info_buffer,
3699 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3700 },
3701 })
3702 else
3703 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3704 .ty = decl.ty,
3705 .val = decl.val,
3706 }, &code_buffer, .none);
3707
3708 const code = blk: {
3709 switch (res) {
3710 .externally_managed => |x| break :blk x,
3711 .appended => {
3712 // TODO clearing the code and relocs buffer should probably be orchestrated
3713 // in a different, smarter, more automatic way somewhere else, in a more centralised
3714 // way than this.
3715 // If we don't clear the buffers here, we are up for some nasty surprises when
3716 // this TextBlock is reused later on and was not freed by freeTextBlock().
3717 decl.link.macho.code.clearAndFree(self.base.allocator);
3718 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3719 break :blk decl.link.macho.code.items;
3720 },
3721 .fail => |em| {
3722 decl.analysis = .codegen_failure;
3723 try module.failed_decls.put(module.gpa, decl, em);
3724 return;
3725 },
3726 }
3727 };
3728 const symbol = try self.placeDecl(decl, code.len);
3729
3730 try self.writeCode(symbol, code);
3731
3732 // Since we updated the vaddr and the size, each corresponding export symbol also
3733 // needs to be updated.
3734 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3735 try self.updateDeclExports(module, decl, decl_exports);
3736}
3737
3738fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
3739 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
3740 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3741 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
3742
3743 if (decl.link.macho.size != 0) {
3744 const capacity = decl.link.macho.capacity(self.*);
3745 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3746 if (need_realloc) {
3747 const vaddr = try self.growTextBlock(&decl.link.macho, code_len, required_alignment);
3748
3749 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
3750
3751 if (vaddr != symbol.n_value) {
3752 log.debug(" (writing new GOT entry)", .{});
3753 const got_index = self.got_entries_map.get(.{
3754 .where = .local,
3755 .where_index = decl.link.macho.local_sym_index,
3756 }) orelse unreachable;
3757 try self.writeGotEntry(got_index);
3758 }
3759
3760 symbol.n_value = vaddr;
3761 } else if (code_len < decl.link.macho.size) {
3762 self.shrinkTextBlock(&decl.link.macho, code_len);
3763 }
3764 decl.link.macho.size = code_len;
3765
3766 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
3767 defer self.base.allocator.free(new_name);
3768
3769 symbol.n_strx = try self.makeString(new_name);
3770 symbol.n_type = macho.N_SECT;
3771 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
3772 symbol.n_desc = 0;
3773
3774 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
3775 if (self.d_sym) |*ds|
3776 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
3777 } else {
3778 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
3779 defer self.base.allocator.free(decl_name);
3780
3781 const name_str_index = try self.makeString(decl_name);
3782 const addr = try self.allocateTextBlock(&decl.link.macho, code_len, required_alignment);
3783
3784 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
3785
3786 errdefer self.freeTextBlock(&decl.link.macho);
3787
3788 symbol.* = .{
3789 .n_strx = name_str_index,
3790 .n_type = macho.N_SECT,
3791 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3792 .n_desc = 0,
3793 .n_value = addr,
3794 };
3795 const got_index = self.got_entries_map.get(.{
3796 .where = .local,
3797 .where_index = decl.link.macho.local_sym_index,
3798 }) orelse unreachable;
3799 try self.writeGotEntry(got_index);
3800
3801 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
3802 if (self.d_sym) |*ds|
3803 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
3804 }
3805
3806 // Resolve relocations
3807 try decl.link.macho.resolveRelocs(self);
3808 // TODO this requires further investigation: should we dispose of resolved relocs, or keep them
3809 // so that we can reapply them when moving/growing sections?
3810 decl.link.macho.relocs.clearAndFree(self.base.allocator);
3811
3812 // Apply pending updates
3813 while (self.pending_updates.popOrNull()) |update| {
3814 switch (update.kind) {
3815 .got => unreachable,
3816 .stub => {
3817 try self.writeStub(update.index);
3818 try self.writeStubInStubHelper(update.index);
3819 try self.writeLazySymbolPointer(update.index);
3820 self.rebase_info_dirty = true;
3821 self.lazy_binding_info_dirty = true;
3822 },
3823 }
3824 }
3825
3826 return symbol;
3827}
3828
3829fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void {
3830 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3831 const text_section = text_segment.sections.items[self.text_section_index.?];
3832 const section_offset = symbol.n_value - text_section.addr;
3833 const file_offset = text_section.offset + section_offset;
3834 log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(symbol.n_strx), file_offset });
3835 try self.base.file.?.pwriteAll(code, file_offset);
3836}
3837
3838pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
3839 if (self.d_sym) |*ds| {
3840 try ds.updateDeclLineNumber(module, decl);
3841 }
3842}
3843
3844pub fn updateDeclExports(
3845 self: *MachO,
3846 module: *Module,
3847 decl: *Module.Decl,
3848 exports: []const *Module.Export,
3849) !void {
3850 if (build_options.skip_non_native and builtin.object_format != .macho) {
3851 @panic("Attempted to compile for object format that was disabled by build configuration");
3852 }
3853 if (build_options.have_llvm) {
3854 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
3855 }
3856 const tracy = trace(@src());
3857 defer tracy.end();
3858
3859 try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len);
3860 if (decl.link.macho.local_sym_index == 0) return;
3861 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
3862
3863 for (exports) |exp| {
3864 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
3865 defer self.base.allocator.free(exp_name);
3866
3867 if (exp.options.section) |section_name| {
3868 if (!mem.eql(u8, section_name, "__text")) {
3869 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3870 module.failed_exports.putAssumeCapacityNoClobber(
3871 exp,
3872 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
3873 );
3874 continue;
3875 }
3876 }
3877
3878 var n_type: u8 = macho.N_SECT | macho.N_EXT;
3879 var n_desc: u16 = 0;
3880
3881 switch (exp.options.linkage) {
3882 .Internal => {
3883 // Symbol should be hidden, or in MachO lingo, private extern.
3884 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
3885 // TODO work out when to add N_WEAK_REF.
3886 n_type |= macho.N_PEXT;
3887 n_desc |= macho.N_WEAK_DEF;
3888 },
3889 .Strong => {
3890 // Check if the export is _main, and note if os.
3891 // Otherwise, don't do anything since we already have all the flags
3892 // set that we need for global (strong) linkage.
3893 // n_type == N_SECT | N_EXT
3894 if (mem.eql(u8, exp_name, "_main")) {
3895 self.entry_addr = decl_sym.n_value;
3896 }
3897 },
3898 .Weak => {
3899 // Weak linkage is specified as part of n_desc field.
3900 // Symbol's n_type is like for a symbol with strong linkage.
3901 n_desc |= macho.N_WEAK_DEF;
3902 },
3903 .LinkOnce => {
3904 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3905 module.failed_exports.putAssumeCapacityNoClobber(
3906 exp,
3907 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
3908 );
3909 continue;
3910 },
3911 }
3912
3913 if (exp.link.macho.sym_index) |i| {
3914 const sym = &self.globals.items[i];
3915 sym.* = .{
3916 .n_strx = sym.n_strx,
3917 .n_type = n_type,
3918 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3919 .n_desc = n_desc,
3920 .n_value = decl_sym.n_value,
3921 };
3922 } else {
3923 const name_str_index = try self.makeString(exp_name);
3924 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
3925 _ = self.globals.addOneAssumeCapacity();
3926 self.export_info_dirty = true;
3927 break :blk @intCast(u32, self.globals.items.len - 1);
3928 };
3929 self.globals.items[i] = .{
3930 .n_strx = name_str_index,
3931 .n_type = n_type,
3932 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3933 .n_desc = n_desc,
3934 .n_value = decl_sym.n_value,
3935 };
3936 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, name_str_index);
3937 resolv.value_ptr.* = .{
3938 .where = .global,
3939 .where_index = i,
3940 .local_sym_index = decl.link.macho.local_sym_index,
3941 };
3942
3943 exp.link.macho.sym_index = @intCast(u32, i);
3944 }
3945 }
3946}
3947
3948pub fn deleteExport(self: *MachO, exp: Export) void {
3949 const sym_index = exp.sym_index orelse return;
3950 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3951 self.globals.items[sym_index].n_type = 0;
3952}
3953
3954pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3955 log.debug("freeDecl {*}", .{decl});
3956 _ = self.decls.swapRemove(decl);
3957 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3958 self.freeTextBlock(&decl.link.macho);
3959 if (decl.link.macho.local_sym_index != 0) {
3960 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
3961
3962 const got_key = GotIndirectionKey{
3963 .where = .local,
3964 .where_index = decl.link.macho.local_sym_index,
3965 };
3966 const got_index = self.got_entries_map.get(got_key) orelse unreachable;
3967 _ = self.got_entries_map.remove(got_key);
3968 self.got_entries_free_list.append(self.base.allocator, got_index) catch {};
3969
3970 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
3971 decl.link.macho.local_sym_index = 0;
3972 }
3973 if (self.d_sym) |*ds| {
3974 // TODO make this logic match freeTextBlock. Maybe abstract the logic
3975 // out since the same thing is desired for both.
3976 _ = ds.dbg_line_fn_free_list.remove(&decl.fn_link.macho);
3977 if (decl.fn_link.macho.prev) |prev| {
3978 ds.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
3979 prev.next = decl.fn_link.macho.next;
3980 if (decl.fn_link.macho.next) |next| {
3981 next.prev = prev;
3982 } else {
3983 ds.dbg_line_fn_last = prev;
3984 }
3985 } else if (decl.fn_link.macho.next) |next| {
3986 ds.dbg_line_fn_first = next;
3987 next.prev = null;
3988 }
3989 if (ds.dbg_line_fn_first == &decl.fn_link.macho) {
3990 ds.dbg_line_fn_first = decl.fn_link.macho.next;
3991 }
3992 if (ds.dbg_line_fn_last == &decl.fn_link.macho) {
3993 ds.dbg_line_fn_last = decl.fn_link.macho.prev;
3994 }
3995 }
3996}
3997
3998pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
3999 assert(decl.link.macho.local_sym_index != 0);
4000 return self.locals.items[decl.link.macho.local_sym_index].n_value;
4001}
4002
4003pub fn populateMissingMetadata(self: *MachO) !void {
4004 switch (self.base.options.output_mode) {
4005 .Exe => {},
4006 .Obj => return error.TODOImplementWritingObjFiles,
4007 .Lib => return error.TODOImplementWritingLibFiles,
4008 }
4009
4010 if (self.pagezero_segment_cmd_index == null) {
4011 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4012 try self.load_commands.append(self.base.allocator, .{
4013 .Segment = SegmentCommand.empty("__PAGEZERO", .{
4014 .vmsize = 0x100000000, // size always set to 4GB
4015 }),
4016 });
4017 self.load_commands_dirty = true;
4018 }
4019 if (self.text_segment_cmd_index == null) {
4020 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4021 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
4022 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
4023
4024 const program_code_size_hint = self.base.options.program_code_size_hint;
4025 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
4026 const ideal_size = self.header_pad + program_code_size_hint + 3 * got_size_hint;
4027 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4028
4029 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
4030
4031 try self.load_commands.append(self.base.allocator, .{
4032 .Segment = SegmentCommand.empty("__TEXT", .{
4033 .vmaddr = 0x100000000, // always starts at 4GB
4034 .vmsize = needed_size,
4035 .filesize = needed_size,
4036 .maxprot = maxprot,
4037 .initprot = initprot,
4038 }),
4039 });
4040 self.load_commands_dirty = true;
4041 }
4042 if (self.text_section_index == null) {
4043 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4044 self.text_section_index = @intCast(u16, text_segment.sections.items.len);
4045
4046 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
4047 .x86_64 => 0,
4048 .aarch64 => 2,
4049 else => unreachable, // unhandled architecture type
4050 };
4051 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
4052 const needed_size = self.base.options.program_code_size_hint;
4053 const off = text_segment.findFreeSpace(needed_size, @as(u16, 1) << alignment, self.header_pad);
4054
4055 log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4056
4057 try text_segment.addSection(self.base.allocator, "__text", .{
4058 .addr = text_segment.inner.vmaddr + off,
4059 .size = @intCast(u32, needed_size),
4060 .offset = @intCast(u32, off),
4061 .@"align" = alignment,
4062 .flags = flags,
4063 });
4064 self.load_commands_dirty = true;
4065 }
4066 if (self.stubs_section_index == null) {
4067 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4068 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
4069
4070 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
4071 .x86_64 => 0,
4072 .aarch64 => 2,
4073 else => unreachable, // unhandled architecture type
4074 };
4075 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
4076 .x86_64 => 6,
4077 .aarch64 => 3 * @sizeOf(u32),
4078 else => unreachable, // unhandled architecture type
4079 };
4080 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
4081 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4082 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
4083 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
4084
4085 log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4086
4087 try text_segment.addSection(self.base.allocator, "__stubs", .{
4088 .addr = text_segment.inner.vmaddr + off,
4089 .size = needed_size,
4090 .offset = @intCast(u32, off),
4091 .@"align" = alignment,
4092 .flags = flags,
4093 .reserved2 = stub_size,
4094 });
4095 self.load_commands_dirty = true;
4096 }
4097 if (self.stub_helper_section_index == null) {
4098 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4099 self.stub_helper_section_index = @intCast(u16, text_segment.sections.items.len);
4100
4101 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
4102 .x86_64 => 0,
4103 .aarch64 => 2,
4104 else => unreachable, // unhandled architecture type
4105 };
4106 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
4107 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4108 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
4109 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
4110
4111 log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4112
4113 try text_segment.addSection(self.base.allocator, "__stub_helper", .{
4114 .addr = text_segment.inner.vmaddr + off,
4115 .size = needed_size,
4116 .offset = @intCast(u32, off),
4117 .@"align" = alignment,
4118 .flags = flags,
4119 });
4120 self.load_commands_dirty = true;
4121 }
4122 if (self.data_const_segment_cmd_index == null) {
4123 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4124 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
4125 const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
4126 const address_and_offset = self.nextSegmentAddressAndOffset();
4127
4128 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4129 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4130
4131 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
4132
4133 try self.load_commands.append(self.base.allocator, .{
4134 .Segment = SegmentCommand.empty("__DATA_CONST", .{
4135 .vmaddr = address_and_offset.address,
4136 .vmsize = needed_size,
4137 .fileoff = address_and_offset.offset,
4138 .filesize = needed_size,
4139 .maxprot = maxprot,
4140 .initprot = initprot,
4141 }),
4142 });
4143 self.load_commands_dirty = true;
4144 }
4145 if (self.got_section_index == null) {
4146 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4147 self.got_section_index = @intCast(u16, dc_segment.sections.items.len);
4148
4149 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
4150 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4151 const off = dc_segment.findFreeSpace(needed_size, @alignOf(u64), null);
4152 assert(off + needed_size <= dc_segment.inner.fileoff + dc_segment.inner.filesize); // TODO Must expand __DATA_CONST segment.
4153
4154 log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4155
4156 try dc_segment.addSection(self.base.allocator, "__got", .{
4157 .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff,
4158 .size = needed_size,
4159 .offset = @intCast(u32, off),
4160 .@"align" = 3, // 2^3 = @sizeOf(u64)
4161 .flags = flags,
4162 });
4163 self.load_commands_dirty = true;
4164 }
4165 if (self.data_segment_cmd_index == null) {
4166 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4167 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
4168 const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
4169 const address_and_offset = self.nextSegmentAddressAndOffset();
4170
4171 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
4172 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4173
4174 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
4175
4176 try self.load_commands.append(self.base.allocator, .{
4177 .Segment = SegmentCommand.empty("__DATA", .{
4178 .vmaddr = address_and_offset.address,
4179 .vmsize = needed_size,
4180 .fileoff = address_and_offset.offset,
4181 .filesize = needed_size,
4182 .maxprot = maxprot,
4183 .initprot = initprot,
4184 }),
4185 });
4186 self.load_commands_dirty = true;
4187 }
4188 if (self.la_symbol_ptr_section_index == null) {
4189 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4190 self.la_symbol_ptr_section_index = @intCast(u16, data_segment.sections.items.len);
4191
4192 const flags = macho.S_LAZY_SYMBOL_POINTERS;
4193 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4194 const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null);
4195 assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment.
4196
4197 log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4198
4199 try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", .{
4200 .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
4201 .size = needed_size,
4202 .offset = @intCast(u32, off),
4203 .@"align" = 3, // 2^3 = @sizeOf(u64)
4204 .flags = flags,
4205 });
4206 self.load_commands_dirty = true;
4207 }
4208 if (self.data_section_index == null) {
4209 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4210 self.data_section_index = @intCast(u16, data_segment.sections.items.len);
4211
4212 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4213 const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null);
4214 assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment.
4215
4216 log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
4217
4218 try data_segment.addSection(self.base.allocator, "__data", .{
4219 .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
4220 .size = needed_size,
4221 .offset = @intCast(u32, off),
4222 .@"align" = 3, // 2^3 = @sizeOf(u64)
4223 });
4224 self.load_commands_dirty = true;
4225 }
4226 if (self.linkedit_segment_cmd_index == null) {
4227 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4228
4229 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
4230 const initprot = macho.VM_PROT_READ;
4231 const address_and_offset = self.nextSegmentAddressAndOffset();
4232
4233 log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset});
4234
4235 try self.load_commands.append(self.base.allocator, .{
4236 .Segment = SegmentCommand.empty("__LINKEDIT", .{
4237 .vmaddr = address_and_offset.address,
4238 .fileoff = address_and_offset.offset,
4239 .maxprot = maxprot,
4240 .initprot = initprot,
4241 }),
4242 });
4243 self.load_commands_dirty = true;
4244 }
4245 if (self.dyld_info_cmd_index == null) {
4246 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
4247
4248 try self.load_commands.append(self.base.allocator, .{
4249 .DyldInfoOnly = .{
4250 .cmd = macho.LC_DYLD_INFO_ONLY,
4251 .cmdsize = @sizeOf(macho.dyld_info_command),
4252 .rebase_off = 0,
4253 .rebase_size = 0,
4254 .bind_off = 0,
4255 .bind_size = 0,
4256 .weak_bind_off = 0,
4257 .weak_bind_size = 0,
4258 .lazy_bind_off = 0,
4259 .lazy_bind_size = 0,
4260 .export_off = 0,
4261 .export_size = 0,
4262 },
4263 });
4264
4265 const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
4266
4267 // Preallocate rebase, binding, lazy binding info, and export info.
4268 const expected_size = 48; // TODO This is totally random.
4269 const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
4270 log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size });
4271 dyld.rebase_off = @intCast(u32, rebase_off);
4272 dyld.rebase_size = expected_size;
4273
4274 const bind_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
4275 log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size });
4276 dyld.bind_off = @intCast(u32, bind_off);
4277 dyld.bind_size = expected_size;
4278
4279 const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
4280 log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size });
4281 dyld.lazy_bind_off = @intCast(u32, lazy_bind_off);
4282 dyld.lazy_bind_size = expected_size;
4283
4284 const export_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
4285 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size });
4286 dyld.export_off = @intCast(u32, export_off);
4287 dyld.export_size = expected_size;
4288
4289 self.load_commands_dirty = true;
4290 }
4291 if (self.symtab_cmd_index == null) {
4292 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4293
4294 try self.load_commands.append(self.base.allocator, .{
4295 .Symtab = .{
4296 .cmd = macho.LC_SYMTAB,
4297 .cmdsize = @sizeOf(macho.symtab_command),
4298 .symoff = 0,
4299 .nsyms = 0,
4300 .stroff = 0,
4301 .strsize = 0,
4302 },
4303 });
4304
4305 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
4306
4307 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
4308 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64), null);
4309 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
4310 symtab.symoff = @intCast(u32, symtab_off);
4311 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
4312
4313 try self.strtab.append(self.base.allocator, 0);
4314 const strtab_size = self.strtab.items.len;
4315 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off);
4316 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
4317 symtab.stroff = @intCast(u32, strtab_off);
4318 symtab.strsize = @intCast(u32, strtab_size);
4319
4320 self.load_commands_dirty = true;
4321 self.strtab_dirty = true;
4322 }
4323 if (self.dysymtab_cmd_index == null) {
4324 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4325
4326 // Preallocate space for indirect symbol table.
4327 const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64.
4328 const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64), null);
4329
4330 log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size });
4331
4332 try self.load_commands.append(self.base.allocator, .{
4333 .Dysymtab = .{
4334 .cmd = macho.LC_DYSYMTAB,
4335 .cmdsize = @sizeOf(macho.dysymtab_command),
4336 .ilocalsym = 0,
4337 .nlocalsym = 0,
4338 .iextdefsym = 0,
4339 .nextdefsym = 0,
4340 .iundefsym = 0,
4341 .nundefsym = 0,
4342 .tocoff = 0,
4343 .ntoc = 0,
4344 .modtaboff = 0,
4345 .nmodtab = 0,
4346 .extrefsymoff = 0,
4347 .nextrefsyms = 0,
4348 .indirectsymoff = @intCast(u32, indsymtab_off),
4349 .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint),
4350 .extreloff = 0,
4351 .nextrel = 0,
4352 .locreloff = 0,
4353 .nlocrel = 0,
4354 },
4355 });
4356 self.load_commands_dirty = true;
4357 }
4358 if (self.dylinker_cmd_index == null) {
4359 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
4360 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4361 u64,
4362 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
4363 @sizeOf(u64),
4364 ));
4365 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
4366 .cmd = macho.LC_LOAD_DYLINKER,
4367 .cmdsize = cmdsize,
4368 .name = @sizeOf(macho.dylinker_command),
4369 });
4370 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
4371 mem.set(u8, dylinker_cmd.data, 0);
4372 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
4373 try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
4374 self.load_commands_dirty = true;
4375 }
4376 if (self.libsystem_cmd_index == null) {
4377 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
4378
4379 var dylib_cmd = try commands.createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0);
4380 errdefer dylib_cmd.deinit(self.base.allocator);
4381
4382 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
4383
4384 self.load_commands_dirty = true;
4385 }
4386 if (self.main_cmd_index == null) {
4387 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
4388 try self.load_commands.append(self.base.allocator, .{
4389 .Main = .{
4390 .cmd = macho.LC_MAIN,
4391 .cmdsize = @sizeOf(macho.entry_point_command),
4392 .entryoff = 0x0,
4393 .stacksize = 0,
4394 },
4395 });
4396 self.load_commands_dirty = true;
4397 }
4398 if (self.build_version_cmd_index == null) {
4399 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4400 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4401 u64,
4402 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
4403 @sizeOf(u64),
4404 ));
4405 const ver = self.base.options.target.os.version_range.semver.min;
4406 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
4407 const is_simulator_abi = self.base.options.target.abi == .simulator;
4408 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
4409 .cmd = macho.LC_BUILD_VERSION,
4410 .cmdsize = cmdsize,
4411 .platform = switch (self.base.options.target.os.tag) {
4412 .macos => macho.PLATFORM_MACOS,
4413 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,
4414 .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS,
4415 .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
4416 else => unreachable,
4417 },
4418 .minos = version,
4419 .sdk = version,
4420 .ntools = 1,
4421 });
4422 const ld_ver = macho.build_tool_version{
4423 .tool = macho.TOOL_LD,
4424 .version = 0x0,
4425 };
4426 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4427 mem.set(u8, cmd.data, 0);
4428 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4429 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
4430 }
4431 if (self.source_version_cmd_index == null) {
4432 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4433 try self.load_commands.append(self.base.allocator, .{
4434 .SourceVersion = .{
4435 .cmd = macho.LC_SOURCE_VERSION,
4436 .cmdsize = @sizeOf(macho.source_version_command),
4437 .version = 0x0,
4438 },
4439 });
4440 self.load_commands_dirty = true;
4441 }
4442 if (self.uuid_cmd_index == null) {
4443 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
4444 var uuid_cmd: macho.uuid_command = .{
4445 .cmd = macho.LC_UUID,
4446 .cmdsize = @sizeOf(macho.uuid_command),
4447 .uuid = undefined,
4448 };
4449 std.crypto.random.bytes(&uuid_cmd.uuid);
4450 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
4451 self.load_commands_dirty = true;
4452 }
4453 if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) {
4454 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
4455 try self.load_commands.append(self.base.allocator, .{
4456 .LinkeditData = .{
4457 .cmd = macho.LC_CODE_SIGNATURE,
4458 .cmdsize = @sizeOf(macho.linkedit_data_command),
4459 .dataoff = 0,
4460 .datasize = 0,
4461 },
4462 });
4463 self.load_commands_dirty = true;
4464 }
4465 if (!self.strtab_dir.containsAdapted(@as([]const u8, "dyld_stub_binder"), StringIndexAdapter{
4466 .bytes = &self.strtab,
4467 })) {
4468 const import_sym_index = @intCast(u32, self.undefs.items.len);
4469 const n_strx = try self.makeString("dyld_stub_binder");
4470 try self.undefs.append(self.base.allocator, .{
4471 .n_strx = n_strx,
4472 .n_type = macho.N_UNDF | macho.N_EXT,
4473 .n_sect = 0,
4474 .n_desc = @intCast(u8, 1) * macho.N_SYMBOL_RESOLVER,
4475 .n_value = 0,
4476 });
4477 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4478 .where = .undef,
4479 .where_index = import_sym_index,
4480 });
4481 const got_key = GotIndirectionKey{
4482 .where = .undef,
4483 .where_index = import_sym_index,
4484 };
4485 const got_index = @intCast(u32, self.got_entries.items.len);
4486 try self.got_entries.append(self.base.allocator, got_key);
4487 try self.got_entries_map.putNoClobber(self.base.allocator, got_key, got_index);
4488 try self.writeGotEntry(got_index);
4489 self.binding_info_dirty = true;
4490 }
4491 if (self.stub_helper_stubs_start_off == null) {
4492 try self.writeStubHelperPreamble();
4493 }
4494}
4495
4496fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
4497 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4498 const text_section = &text_segment.sections.items[self.text_section_index.?];
4499 const new_block_ideal_capacity = padToIdeal(new_block_size);
4500
4501 // We use these to indicate our intention to update metadata, placing the new block,
4502 // and possibly removing a free list node.
4503 // It would be simpler to do it inside the for loop below, but that would cause a
4504 // problem if an error was returned later in the function. So this action
4505 // is actually carried out at the end of the function, when errors are no longer possible.
4506 var block_placement: ?*TextBlock = null;
4507 var free_list_removal: ?usize = null;
4508
4509 // First we look for an appropriately sized free list node.
4510 // The list is unordered. We'll just take the first thing that works.
4511 const vaddr = blk: {
4512 var i: usize = 0;
4513 while (i < self.text_block_free_list.items.len) {
4514 const big_block = self.text_block_free_list.items[i];
4515 // We now have a pointer to a live text block that has too much capacity.
4516 // Is it enough that we could fit this new text block?
4517 const sym = self.locals.items[big_block.local_sym_index];
4518 const capacity = big_block.capacity(self.*);
4519 const ideal_capacity = padToIdeal(capacity);
4520 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
4521 const capacity_end_vaddr = sym.n_value + capacity;
4522 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
4523 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
4524 if (new_start_vaddr < ideal_capacity_end_vaddr) {
4525 // Additional bookkeeping here to notice if this free list node
4526 // should be deleted because the block that it points to has grown to take up
4527 // more of the extra capacity.
4528 if (!big_block.freeListEligible(self.*)) {
4529 const bl = self.text_block_free_list.swapRemove(i);
4530 bl.deinit(self.base.allocator);
4531 } else {
4532 i += 1;
4533 }
4534 continue;
4535 }
4536 // At this point we know that we will place the new block here. But the
4537 // remaining question is whether there is still yet enough capacity left
4538 // over for there to still be a free list node.
4539 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
4540 const keep_free_list_node = remaining_capacity >= min_text_capacity;
4541
4542 // Set up the metadata to be updated, after errors are no longer possible.
4543 block_placement = big_block;
4544 if (!keep_free_list_node) {
4545 free_list_removal = i;
4546 }
4547 break :blk new_start_vaddr;
4548 } else if (self.last_text_block) |last| {
4549 const last_symbol = self.locals.items[last.local_sym_index];
4550 // TODO We should pad out the excess capacity with NOPs. For executables,
4551 // no padding seems to be OK, but it will probably not be for objects.
4552 const ideal_capacity = padToIdeal(last.size);
4553 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
4554 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
4555 block_placement = last;
4556 break :blk new_start_vaddr;
4557 } else {
4558 break :blk text_section.addr;
4559 }
4560 };
4561
4562 const expand_text_section = block_placement == null or block_placement.?.next == null;
4563 if (expand_text_section) {
4564 const needed_size = (vaddr + new_block_size) - text_section.addr;
4565 assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section.
4566
4567 self.last_text_block = text_block;
4568 text_section.size = needed_size;
4569 self.load_commands_dirty = true; // TODO Make more granular.
4570
4571 if (self.d_sym) |*ds| {
4572 const debug_text_seg = &ds.load_commands.items[ds.text_segment_cmd_index.?].Segment;
4573 const debug_text_sect = &debug_text_seg.sections.items[ds.text_section_index.?];
4574 debug_text_sect.size = needed_size;
4575 ds.load_commands_dirty = true;
4576 }
4577 }
4578 text_block.size = new_block_size;
4579
4580 if (text_block.prev) |prev| {
4581 prev.next = text_block.next;
4582 }
4583 if (text_block.next) |next| {
4584 next.prev = text_block.prev;
4585 }
4586
4587 if (block_placement) |big_block| {
4588 text_block.prev = big_block;
4589 text_block.next = big_block.next;
4590 big_block.next = text_block;
4591 } else {
4592 text_block.prev = null;
4593 text_block.next = null;
4594 }
4595 if (free_list_removal) |i| {
4596 _ = self.text_block_free_list.swapRemove(i);
4597 }
4598
4599 return vaddr;
4600}
4601
4602pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
4603 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4604 defer self.base.allocator.free(sym_name);
4605
4606 if (self.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
4607 .bytes = &self.strtab,
4608 })) |n_strx| {
4609 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
4610 return resolv.where_index;
4611 }
4612
4613 log.debug("adding new extern function '{s}' with dylib ordinal 1", .{sym_name});
4614 const import_sym_index = @intCast(u32, self.undefs.items.len);
4615 const n_strx = try self.makeString(sym_name);
4616 try self.undefs.append(self.base.allocator, .{
4617 .n_strx = n_strx,
4618 .n_type = macho.N_UNDF | macho.N_EXT,
4619 .n_sect = 0,
4620 .n_desc = @intCast(u8, 1) * macho.N_SYMBOL_RESOLVER,
4621 .n_value = 0,
4622 });
4623 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4624 .where = .undef,
4625 .where_index = import_sym_index,
4626 });
4627
4628 const stubs_index = @intCast(u32, self.stubs.items.len);
4629 try self.stubs.append(self.base.allocator, import_sym_index);
4630 try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index);
4631
4632 // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail },
4633 // which obviously doesn't include file writing op errors. So instead of trying to write the stub
4634 // entry right here and now, queue it up and dispose of when updating decl.
4635 try self.pending_updates.append(self.base.allocator, .{
4636 .kind = .stub,
4637 .index = stubs_index,
4638 });
4639
4640 return import_sym_index;
4641}
4642
4643const NextSegmentAddressAndOffset = struct {
4644 address: u64,
4645 offset: u64,
4646};
4647
4648fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
4649 var prev_segment_idx: ?usize = null; // We use optional here for safety.
4650 for (self.load_commands.items) |cmd, i| {
4651 if (cmd == .Segment) {
4652 prev_segment_idx = i;
4653 }
4654 }
4655 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
4656 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
4657 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
4658 return .{
4659 .address = address,
4660 .offset = offset,
4661 };
4662}
4663
4664fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 {
4665 assert(start > 0);
4666 var min_pos: u64 = std.math.maxInt(u64);
4667
4668 // __LINKEDIT is a weird segment where sections get their own load commands so we
4669 // special-case it.
4670 if (self.dyld_info_cmd_index) |idx| {
4671 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
4672 if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off;
4673 if (dyld_info.bind_off > start and dyld_info.bind_off < min_pos) min_pos = dyld_info.bind_off;
4674 if (dyld_info.weak_bind_off > start and dyld_info.weak_bind_off < min_pos) min_pos = dyld_info.weak_bind_off;
4675 if (dyld_info.lazy_bind_off > start and dyld_info.lazy_bind_off < min_pos) min_pos = dyld_info.lazy_bind_off;
4676 if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;
4677 }
4678
4679 if (self.function_starts_cmd_index) |idx| {
4680 const fstart = self.load_commands.items[idx].LinkeditData;
4681 if (fstart.dataoff > start and fstart.dataoff < min_pos) min_pos = fstart.dataoff;
4682 }
4683
4684 if (self.data_in_code_cmd_index) |idx| {
4685 const dic = self.load_commands.items[idx].LinkeditData;
4686 if (dic.dataoff > start and dic.dataoff < min_pos) min_pos = dic.dataoff;
4687 }
4688
4689 if (self.dysymtab_cmd_index) |idx| {
4690 const dysymtab = self.load_commands.items[idx].Dysymtab;
4691 if (dysymtab.indirectsymoff > start and dysymtab.indirectsymoff < min_pos) min_pos = dysymtab.indirectsymoff;
4692 // TODO Handle more dynamic symbol table sections.
4693 }
4694
4695 if (self.symtab_cmd_index) |idx| {
4696 const symtab = self.load_commands.items[idx].Symtab;
4697 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
4698 if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
4699 }
4700
4701 return min_pos - start;
4702}
4703inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {
4704 const increased_size = padToIdeal(size);
4705 const test_end = off + increased_size;
4706 if (end > off and start < test_end) {
4707 return test_end;
4708 }
4709 return null;
4710}
4711
4712fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {
4713 const end = start + padToIdeal(size);
4714
4715 // __LINKEDIT is a weird segment where sections get their own load commands so we
4716 // special-case it.
4717 if (self.dyld_info_cmd_index) |idx| outer: {
4718 if (self.load_commands.items.len == idx) break :outer;
4719 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
4720 if (checkForCollision(start, end, dyld_info.rebase_off, dyld_info.rebase_size)) |pos| {
4721 return pos;
4722 }
4723 // Binding info
4724 if (checkForCollision(start, end, dyld_info.bind_off, dyld_info.bind_size)) |pos| {
4725 return pos;
4726 }
4727 // Weak binding info
4728 if (checkForCollision(start, end, dyld_info.weak_bind_off, dyld_info.weak_bind_size)) |pos| {
4729 return pos;
4730 }
4731 // Lazy binding info
4732 if (checkForCollision(start, end, dyld_info.lazy_bind_off, dyld_info.lazy_bind_size)) |pos| {
4733 return pos;
4734 }
4735 // Export info
4736 if (checkForCollision(start, end, dyld_info.export_off, dyld_info.export_size)) |pos| {
4737 return pos;
4738 }
4739 }
4740
4741 if (self.function_starts_cmd_index) |idx| outer: {
4742 if (self.load_commands.items.len == idx) break :outer;
4743 const fstart = self.load_commands.items[idx].LinkeditData;
4744 if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| {
4745 return pos;
4746 }
4747 }
4748
4749 if (self.data_in_code_cmd_index) |idx| outer: {
4750 if (self.load_commands.items.len == idx) break :outer;
4751 const dic = self.load_commands.items[idx].LinkeditData;
4752 if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| {
4753 return pos;
4754 }
4755 }
4756
4757 if (self.dysymtab_cmd_index) |idx| outer: {
4758 if (self.load_commands.items.len == idx) break :outer;
4759 const dysymtab = self.load_commands.items[idx].Dysymtab;
4760 // Indirect symbol table
4761 const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32);
4762 if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| {
4763 return pos;
4764 }
4765 // TODO Handle more dynamic symbol table sections.
4766 }
4767
4768 if (self.symtab_cmd_index) |idx| outer: {
4769 if (self.load_commands.items.len == idx) break :outer;
4770 const symtab = self.load_commands.items[idx].Symtab;
4771 // Symbol table
4772 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
4773 if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| {
4774 return pos;
4775 }
4776 // String table
4777 if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| {
4778 return pos;
4779 }
4780 }
4781
4782 return null;
4783}
4784
4785fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, start: ?u64) u64 {
4786 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4787 var st: u64 = start orelse linkedit.inner.fileoff;
4788 while (self.detectAllocCollisionLinkedit(st, object_size)) |item_end| {
4789 st = mem.alignForwardGeneric(u64, item_end, min_alignment);
4790 }
4791 return st;
4792}
4793
4794fn writeGotEntry(self: *MachO, index: usize) !void {
4795 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4796 const sect = &seg.sections.items[self.got_section_index.?];
4797 const off = sect.offset + @sizeOf(u64) * index;
4798
4799 if (self.got_entries_count_dirty) {
4800 // TODO relocate.
4801 self.got_entries_count_dirty = false;
4802 }
4803
4804 const got_entry = self.got_entries.items[index];
4805 const sym = switch (got_entry.where) {
4806 .local => self.locals.items[got_entry.where_index],
4807 .undef => self.undefs.items[got_entry.where_index],
4808 };
4809 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{
4810 off,
4811 sym.n_value,
4812 self.getString(sym.n_strx),
4813 });
4814 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
4815}
4816
4817fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
4818 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4819 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
4820 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4821 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
4822
4823 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
4824 .x86_64 => 10,
4825 .aarch64 => 3 * @sizeOf(u32),
4826 else => unreachable,
4827 };
4828 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
4829 const end = stub_helper.addr + stub_off - stub_helper.offset;
4830 var buf: [@sizeOf(u64)]u8 = undefined;
4831 mem.writeIntLittle(u64, &buf, end);
4832 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
4833 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
4834 try self.base.file.?.pwriteAll(&buf, off);
4835}
4836
4837fn writeStubHelperPreamble(self: *MachO) !void {
4838 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4839 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
4840 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4841 const got = &data_const_segment.sections.items[self.got_section_index.?];
4842 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4843 const data = &data_segment.sections.items[self.data_section_index.?];
4844
4845 switch (self.base.options.target.cpu.arch) {
4846 .x86_64 => {
4847 const code_size = 15;
4848 var code: [code_size]u8 = undefined;
4849 // lea %r11, [rip + disp]
4850 code[0] = 0x4c;
4851 code[1] = 0x8d;
4852 code[2] = 0x1d;
4853 {
4854 const target_addr = data.addr;
4855 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
4856 mem.writeIntLittle(u32, code[3..7], displacement);
4857 }
4858 // push %r11
4859 code[7] = 0x41;
4860 code[8] = 0x53;
4861 // jmp [rip + disp]
4862 code[9] = 0xff;
4863 code[10] = 0x25;
4864 {
4865 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
4866 mem.writeIntLittle(u32, code[11..], displacement);
4867 }
4868 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
4869 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
4870 },
4871 .aarch64 => {
4872 var code: [6 * @sizeOf(u32)]u8 = undefined;
4873
4874 data_blk_outer: {
4875 const this_addr = stub_helper.addr;
4876 const target_addr = data.addr;
4877 data_blk: {
4878 const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk;
4879 // adr x17, disp
4880 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
4881 // nop
4882 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
4883 break :data_blk_outer;
4884 }
4885 data_blk: {
4886 const new_this_addr = this_addr + @sizeOf(u32);
4887 const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk;
4888 // nop
4889 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
4890 // adr x17, disp
4891 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
4892 break :data_blk_outer;
4893 }
4894 // Jump is too big, replace adr with adrp and add.
4895 const this_page = @intCast(i32, this_addr >> 12);
4896 const target_page = @intCast(i32, target_addr >> 12);
4897 const pages = @intCast(i21, target_page - this_page);
4898 // adrp x17, pages
4899 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
4900 const narrowed = @truncate(u12, target_addr);
4901 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
4902 }
4903
4904 // stp x16, x17, [sp, #-16]!
4905 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.stp(
4906 .x16,
4907 .x17,
4908 aarch64.Register.sp,
4909 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
4910 ).toU32());
4911
4912 binder_blk_outer: {
4913 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
4914 const target_addr = got.addr;
4915 binder_blk: {
4916 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk;
4917 const literal = math.cast(u18, displacement) catch break :binder_blk;
4918 // ldr x16, label
4919 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
4920 .literal = literal,
4921 }).toU32());
4922 // nop
4923 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
4924 break :binder_blk_outer;
4925 }
4926 binder_blk: {
4927 const new_this_addr = this_addr + @sizeOf(u32);
4928 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk;
4929 const literal = math.cast(u18, displacement) catch break :binder_blk;
4930 // nop
4931 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
4932 // ldr x16, label
4933 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
4934 .literal = literal,
4935 }).toU32());
4936 break :binder_blk_outer;
4937 }
4938 // Jump is too big, replace ldr with adrp and ldr(register).
4939 const this_page = @intCast(i32, this_addr >> 12);
4940 const target_page = @intCast(i32, target_addr >> 12);
4941 const pages = @intCast(i21, target_page - this_page);
4942 // adrp x16, pages
4943 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
4944 const narrowed = @truncate(u12, target_addr);
4945 const offset = try math.divExact(u12, narrowed, 8);
4946 // ldr x16, x16, offset
4947 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
4948 .register = .{
4949 .rn = .x16,
4950 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
4951 },
4952 }).toU32());
4953 }
4954
4955 // br x16
4956 mem.writeIntLittle(u32, code[20..24], aarch64.Instruction.br(.x16).toU32());
4957 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
4958 self.stub_helper_stubs_start_off = stub_helper.offset + code.len;
4959 },
4960 else => unreachable,
4961 }
4962}
4963
4964fn writeStub(self: *MachO, index: u32) !void {
4965 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4966 const stubs = text_segment.sections.items[self.stubs_section_index.?];
4967 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4968 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
4969
4970 const stub_off = stubs.offset + index * stubs.reserved2;
4971 const stub_addr = stubs.addr + index * stubs.reserved2;
4972 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
4973
4974 log.debug("writing stub at 0x{x}", .{stub_off});
4975
4976 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
4977 defer self.base.allocator.free(code);
4978
4979 switch (self.base.options.target.cpu.arch) {
4980 .x86_64 => {
4981 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
4982 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
4983 // jmp
4984 code[0] = 0xff;
4985 code[1] = 0x25;
4986 mem.writeIntLittle(u32, code[2..][0..4], displacement);
4987 },
4988 .aarch64 => {
4989 assert(la_ptr_addr >= stub_addr);
4990 outer: {
4991 const this_addr = stub_addr;
4992 const target_addr = la_ptr_addr;
4993 inner: {
4994 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :inner;
4995 const literal = math.cast(u18, displacement) catch break :inner;
4996 // ldr x16, literal
4997 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
4998 .literal = literal,
4999 }).toU32());
5000 // nop
5001 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
5002 break :outer;
5003 }
5004 inner: {
5005 const new_this_addr = this_addr + @sizeOf(u32);
5006 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :inner;
5007 const literal = math.cast(u18, displacement) catch break :inner;
5008 // nop
5009 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
5010 // ldr x16, literal
5011 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
5012 .literal = literal,
5013 }).toU32());
5014 break :outer;
5015 }
5016 // Use adrp followed by ldr(register).
5017 const this_page = @intCast(i32, this_addr >> 12);
5018 const target_page = @intCast(i32, target_addr >> 12);
5019 const pages = @intCast(i21, target_page - this_page);
5020 // adrp x16, pages
5021 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
5022 const narrowed = @truncate(u12, target_addr);
5023 const offset = try math.divExact(u12, narrowed, 8);
5024 // ldr x16, x16, offset
5025 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
5026 .register = .{
5027 .rn = .x16,
5028 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
5029 },
5030 }).toU32());
5031 }
5032 // br x16
5033 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
5034 },
5035 else => unreachable,
5036 }3749 }
5037 try self.base.file.?.pwriteAll(code, stub_off);
5038}
5039
5040fn writeStubInStubHelper(self: *MachO, index: u32) !void {
5041 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5042 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
5043
5044 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
5045 .x86_64 => 10,
5046 .aarch64 => 3 * @sizeOf(u32),
5047 else => unreachable,
5048 };
5049 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
5050
5051 var code = try self.base.allocator.alloc(u8, stub_size);
5052 defer self.base.allocator.free(code);
50533750
5054 switch (self.base.options.target.cpu.arch) {3751 if (self.dysymtab_cmd_index == null) {
5055 .x86_64 => {3752 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
5056 const displacement = try math.cast(3753 try self.load_commands.append(self.base.allocator, .{
5057 i32,3754 .Dysymtab = .{
5058 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,3755 .cmd = macho.LC_DYSYMTAB,
5059 );3756 .cmdsize = @sizeOf(macho.dysymtab_command),
5060 // pushq3757 .ilocalsym = 0,
5061 code[0] = 0x68;3758 .nlocalsym = 0,
5062 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.3759 .iextdefsym = 0,
5063 // jmpq3760 .nextdefsym = 0,
5064 code[5] = 0xe9;3761 .iundefsym = 0,
5065 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));3762 .nundefsym = 0,
5066 },3763 .tocoff = 0,
5067 .aarch64 => {3764 .ntoc = 0,
5068 const literal = blk: {3765 .modtaboff = 0,
5069 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);3766 .nmodtab = 0,
5070 break :blk try math.cast(u18, div_res);3767 .extrefsymoff = 0,
5071 };3768 .nextrefsyms = 0,
5072 // ldr w16, literal3769 .indirectsymoff = 0,
5073 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{3770 .nindirectsyms = 0,
5074 .literal = literal,3771 .extreloff = 0,
5075 }).toU32());3772 .nextrel = 0,
5076 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);3773 .locreloff = 0,
5077 // b disp3774 .nlocrel = 0,
5078 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());3775 },
5079 // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.3776 });
5080 mem.writeIntLittle(u32, code[8..12], 0x0);3777 self.load_commands_dirty = true;
5081 },
5082 else => unreachable,
5083 }3778 }
5084 try self.base.file.?.pwriteAll(code, stub_off);
5085}
5086
5087fn relocateSymbolTable(self: *MachO) !void {
5088 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5089 const nlocals = self.locals.items.len;
5090 const nglobals = self.globals.items.len;
5091 const nundefs = self.undefs.items.len;
5092 const nsyms = nlocals + nglobals + nundefs;
5093
5094 if (symtab.nsyms < nsyms) {
5095 const needed_size = nsyms * @sizeOf(macho.nlist_64);
5096 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
5097 // Move the entire symbol table to a new location
5098 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64), null);
5099 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
5100
5101 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
5102 symtab.symoff,
5103 symtab.symoff + existing_size,
5104 new_symoff,
5105 new_symoff + existing_size,
5106 });
51073779
5108 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);3780 if (self.dylinker_cmd_index == null) {
5109 if (amt != existing_size) return error.InputOutput;3781 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
5110 symtab.symoff = @intCast(u32, new_symoff);3782 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
5111 self.strtab_needs_relocation = true;3783 u64,
5112 }3784 @sizeOf(macho.dylinker_command) + mem.lenZ(default_dyld_path),
5113 symtab.nsyms = @intCast(u32, nsyms);3785 @sizeOf(u64),
3786 ));
3787 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
3788 .cmd = macho.LC_LOAD_DYLINKER,
3789 .cmdsize = cmdsize,
3790 .name = @sizeOf(macho.dylinker_command),
3791 });
3792 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
3793 mem.set(u8, dylinker_cmd.data, 0);
3794 mem.copy(u8, dylinker_cmd.data, mem.spanZ(default_dyld_path));
3795 try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
5114 self.load_commands_dirty = true;3796 self.load_commands_dirty = true;
5115 }3797 }
5116}
5117
5118fn writeLocalSymbol(self: *MachO, index: usize) !void {
5119 const tracy = trace(@src());
5120 defer tracy.end();
5121 try self.relocateSymbolTable();
5122 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5123 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
5124 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
5125 try self.base.file.?.pwriteAll(mem.asBytes(&self.locals.items[index]), off);
5126}
5127
5128fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
5129 const tracy = trace(@src());
5130 defer tracy.end();
51313798
5132 try self.relocateSymbolTable();3799 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
5133 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;3800 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
5134 const nlocals = self.locals.items.len;3801 try self.load_commands.append(self.base.allocator, .{
5135 const nglobals = self.globals.items.len;3802 .Main = .{
5136 const nundefs = self.undefs.items.len;3803 .cmd = macho.LC_MAIN,
51373804 .cmdsize = @sizeOf(macho.entry_point_command),
5138 const locals_off = symtab.symoff;3805 .entryoff = 0x0,
5139 const locals_size = nlocals * @sizeOf(macho.nlist_64);3806 .stacksize = 0,
51403807 },
5141 const globals_off = locals_off + locals_size;3808 });
5142 const globals_size = nglobals * @sizeOf(macho.nlist_64);3809 self.load_commands_dirty = true;
5143 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
5144 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off);
5145
5146 const undefs_off = globals_off + globals_size;
5147 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
5148 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
5149 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
5150
5151 // Update dynamic symbol table.
5152 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
5153 dysymtab.nlocalsym = @intCast(u32, nlocals);
5154 dysymtab.iextdefsym = @intCast(u32, nlocals);
5155 dysymtab.nextdefsym = @intCast(u32, nglobals);
5156 dysymtab.iundefsym = @intCast(u32, nlocals + nglobals);
5157 dysymtab.nundefsym = @intCast(u32, nundefs);
5158 self.load_commands_dirty = true;
5159}
5160
5161fn writeIndirectSymbolTable(self: *MachO) !void {
5162 // TODO figure out a way not to rewrite the table every time if
5163 // no new undefs are not added.
5164 const tracy = trace(@src());
5165 defer tracy.end();
5166
5167 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5168 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
5169 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
5170 const got = &data_const_seg.sections.items[self.got_section_index.?];
5171 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5172 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
5173 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
5174
5175 const nstubs = @intCast(u32, self.stubs.items.len);
5176 const ngot_entries = @intCast(u32, self.got_entries.items.len);
5177 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
5178 const nindirectsyms = nstubs * 2 + ngot_entries;
5179 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
5180
5181 if (needed_size > allocated_size) {
5182 dysymtab.nindirectsyms = 0;
5183 dysymtab.indirectsymoff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, @sizeOf(u32), null));
5184 }3810 }
5185 dysymtab.nindirectsyms = nindirectsyms;
5186 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
5187 dysymtab.indirectsymoff,
5188 dysymtab.indirectsymoff + needed_size,
5189 });
5190
5191 var buf = try self.base.allocator.alloc(u8, needed_size);
5192 defer self.base.allocator.free(buf);
5193 var stream = std.io.fixedBufferStream(buf);
5194 var writer = stream.writer();
51953811
5196 stubs.reserved1 = 0;3812 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {
5197 for (self.stubs.items) |id| {3813 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
5198 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);3814 const install_name = try std.fmt.allocPrint(self.base.allocator, "@rpath/{s}", .{
3815 self.base.options.emit.?.sub_path,
3816 });
3817 defer self.base.allocator.free(install_name);
3818 var dylib_cmd = try commands.createLoadDylibCommand(
3819 self.base.allocator,
3820 install_name,
3821 2,
3822 0x10000, // TODO forward user-provided versions
3823 0x10000,
3824 );
3825 errdefer dylib_cmd.deinit(self.base.allocator);
3826 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
3827 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
3828 self.load_commands_dirty = true;
5199 }3829 }
52003830
5201 got.reserved1 = nstubs;3831 if (self.source_version_cmd_index == null) {
5202 for (self.got_entries.items) |entry| {3832 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
5203 switch (entry.where) {3833 try self.load_commands.append(self.base.allocator, .{
5204 .undef => {3834 .SourceVersion = .{
5205 try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index);3835 .cmd = macho.LC_SOURCE_VERSION,
5206 },3836 .cmdsize = @sizeOf(macho.source_version_command),
5207 .local => {3837 .version = 0x0,
5208 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
5209 },3838 },
5210 }3839 });
3840 self.load_commands_dirty = true;
5211 }3841 }
52123842
5213 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;3843 if (self.build_version_cmd_index == null) {
5214 for (self.stubs.items) |id| {3844 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
5215 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);3845 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3846 u64,
3847 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
3848 @sizeOf(u64),
3849 ));
3850 const ver = self.base.options.target.os.version_range.semver.min;
3851 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
3852 const is_simulator_abi = self.base.options.target.abi == .simulator;
3853 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
3854 .cmd = macho.LC_BUILD_VERSION,
3855 .cmdsize = cmdsize,
3856 .platform = switch (self.base.options.target.os.tag) {
3857 .macos => macho.PLATFORM_MACOS,
3858 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,
3859 .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS,
3860 .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS,
3861 else => unreachable,
3862 },
3863 .minos = version,
3864 .sdk = version,
3865 .ntools = 1,
3866 });
3867 const ld_ver = macho.build_tool_version{
3868 .tool = macho.TOOL_LD,
3869 .version = 0x0,
3870 };
3871 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
3872 mem.set(u8, cmd.data, 0);
3873 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
3874 try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
3875 self.load_commands_dirty = true;
5216 }3876 }
52173877
5218 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);3878 if (self.uuid_cmd_index == null) {
5219 self.load_commands_dirty = true;3879 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
5220}3880 var uuid_cmd: macho.uuid_command = .{
52213881 .cmd = macho.LC_UUID,
5222fn writeDices(self: *MachO) !void {3882 .cmdsize = @sizeOf(macho.uuid_command),
5223 if (!self.has_dices) return;3883 .uuid = undefined,
52243884 };
5225 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;3885 std.crypto.random.bytes(&uuid_cmd.uuid);
5226 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;3886 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
5227 const fileoff = seg.inner.fileoff + seg.inner.filesize;3887 self.load_commands_dirty = true;
5228
5229 var buf = std.ArrayList(u8).init(self.base.allocator);
5230 defer buf.deinit();
5231
5232 var block: *TextBlock = self.blocks.get(.{
5233 .seg = self.text_segment_cmd_index orelse return,
5234 .sect = self.text_section_index orelse return,
5235 }) orelse return;
5236
5237 while (block.prev) |prev| {
5238 block = prev;
5239 }3888 }
52403889
5241 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;3890 if (self.data_in_code_cmd_index == null) {
5242 const text_sect = text_seg.sections.items[self.text_section_index.?];3891 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
52433892 try self.load_commands.append(self.base.allocator, .{
5244 while (true) {3893 .LinkeditData = .{
5245 if (block.dices.items.len > 0) {3894 .cmd = macho.LC_DATA_IN_CODE,
5246 const sym = self.locals.items[block.local_sym_index];3895 .cmdsize = @sizeOf(macho.linkedit_data_command),
5247 const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset);3896 .dataoff = 0,
52483897 .datasize = 0,
5249 try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry));3898 },
5250 for (block.dices.items) |dice| {3899 });
5251 const rebased_dice = macho.data_in_code_entry{3900 self.load_commands_dirty = true;
5252 .offset = base_off + dice.offset,
5253 .length = dice.length,
5254 .kind = dice.kind,
5255 };
5256 buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice));
5257 }
5258 }
5259
5260 if (block.next) |next| {
5261 block = next;
5262 } else break;
5263 }3901 }
52643902
5265 const datasize = @intCast(u32, buf.items.len);3903 self.cold_start = true;
5266
5267 dice_cmd.dataoff = @intCast(u32, fileoff);
5268 dice_cmd.datasize = datasize;
5269 seg.inner.filesize += datasize;
5270
5271 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
5272
5273 try self.base.file.?.pwriteAll(buf.items, fileoff);
5274}3904}
52753905
5276fn writeCodeSignaturePadding(self: *MachO) !void {3906const AllocateSectionOpts = struct {
5277 // TODO figure out how not to rewrite padding every single time.3907 flags: u32 = macho.S_REGULAR,
5278 const tracy = trace(@src());3908 reserved1: u32 = 0,
5279 defer tracy.end();3909 reserved2: u32 = 0,
52803910};
5281 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5282 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
5283 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
5284 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
5285 self.base.options.emit.?.sub_path,
5286 fileoff,
5287 self.page_size,
5288 );
5289 code_sig_cmd.dataoff = @intCast(u32, fileoff);
5290 code_sig_cmd.datasize = needed_size;
52913911
5292 // Advance size of __LINKEDIT segment3912fn allocateSection(
5293 linkedit_segment.inner.filesize += needed_size;3913 self: *MachO,
5294 if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) {3914 segment_id: u16,
5295 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size);3915 sectname: []const u8,
5296 }3916 size: u64,
5297 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });3917 alignment: u32,
5298 // Pad out the space. We need to do this to calculate valid hashes for everything in the file3918 opts: AllocateSectionOpts,
5299 // except for code signature data.3919) !u16 {
5300 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);3920 const seg = &self.load_commands.items[segment_id].Segment;
5301 self.load_commands_dirty = true;3921 var sect = macho.section_64{
5302}3922 .sectname = makeStaticString(sectname),
3923 .segname = seg.inner.segname,
3924 .size = @intCast(u32, size),
3925 .@"align" = alignment,
3926 .flags = opts.flags,
3927 .reserved1 = opts.reserved1,
3928 .reserved2 = opts.reserved2,
3929 };
53033930
5304fn writeCodeSignature(self: *MachO) !void {3931 const alignment_pow_2 = try math.powi(u32, 2, alignment);
5305 const tracy = trace(@src());3932 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
5306 defer tracy.end();3933 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
53073934
5308 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;3935 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
5309 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;3936 commands.segmentName(sect),
3937 commands.sectionName(sect),
3938 off,
3939 off + size,
3940 });
53103941
5311 var code_sig: CodeSignature = .{};3942 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
5312 defer code_sig.deinit(self.base.allocator);3943 sect.offset = @intCast(u32, off);
53133944
5314 try code_sig.calcAdhocSignature(3945 const index = @intCast(u16, seg.sections.items.len);
5315 self.base.allocator,3946 try seg.sections.append(self.base.allocator, sect);
5316 self.base.file.?,3947 seg.inner.cmdsize += @sizeOf(macho.section_64);
5317 self.base.options.emit.?.sub_path,3948 seg.inner.nsects += 1;
5318 text_segment.inner,
5319 code_sig_cmd,
5320 self.base.options.output_mode,
5321 self.page_size,
5322 );
53233949
5324 var buffer = try self.base.allocator.alloc(u8, code_sig.size());3950 const match = MatchingSection{
5325 defer self.base.allocator.free(buffer);3951 .seg = segment_id,
5326 var stream = std.io.fixedBufferStream(buffer);3952 .sect = index,
5327 try code_sig.write(stream.writer());3953 };
3954 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
3955 try self.atom_free_lists.putNoClobber(self.base.allocator, match, .{});
53283956
5329 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });3957 self.load_commands_dirty = true;
3958 self.sections_order_dirty = true;
53303959
5331 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);3960 return index;
5332}3961}
53333962
5334fn writeExportInfo(self: *MachO) !void {3963fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {
5335 if (!self.export_info_dirty) return;3964 const seg = self.load_commands.items[segment_id].Segment;
5336 if (self.globals.items.len == 0) return;3965 if (seg.sections.items.len == 0) {
3966 return if (start) |v| v else seg.inner.fileoff;
3967 }
3968 const last_sect = seg.sections.items[seg.sections.items.len - 1];
3969 const final_off = last_sect.offset + padToIdeal(last_sect.size);
3970 return mem.alignForwardGeneric(u64, final_off, alignment);
3971}
53373972
3973fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
5338 const tracy = trace(@src());3974 const tracy = trace(@src());
5339 defer tracy.end();3975 defer tracy.end();
53403976
5341 var trie: Trie = .{};3977 const seg = &self.load_commands.items[match.seg].Segment;
5342 defer trie.deinit(self.base.allocator);3978 const sect = &seg.sections.items[match.sect];
53433979
5344 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;3980 const alignment = try math.powi(u32, 2, sect.@"align");
5345 const base_address = text_segment.inner.vmaddr;3981 const max_size = self.allocatedSize(match.seg, sect.offset);
3982 const ideal_size = padToIdeal(new_size);
3983 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
53463984
5347 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.3985 if (needed_size > max_size) blk: {
5348 log.debug("writing export trie", .{});3986 log.debug(" (need to grow!)", .{});
3987 // Need to move all sections below in file and address spaces.
3988 const offset_amt = offset: {
3989 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);
3990 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
3991 };
53493992
5350 for (self.globals.items) |sym| {3993 // Before we commit to this, check if the segment needs to grow too.
5351 const sym_name = self.getString(sym.n_strx);3994 // We assume that each section header is growing linearly with the increasing
5352 log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });3995 // file offset / virtual memory address space.
3996 const last_sect = seg.sections.items[seg.sections.items.len - 1];
3997 const last_sect_off = last_sect.offset + last_sect.size;
3998 const seg_off = seg.inner.fileoff + seg.inner.filesize;
3999
4000 if (last_sect_off + offset_amt > seg_off) {
4001 // Need to grow segment first.
4002 log.debug(" (need to grow segment first)", .{});
4003 const spill_size = (last_sect_off + offset_amt) - seg_off;
4004 const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size);
4005 seg.inner.filesize += seg_offset_amt;
4006 seg.inner.vmsize += seg_offset_amt;
4007
4008 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4009 seg.inner.segname,
4010 seg.inner.fileoff,
4011 seg.inner.fileoff + seg.inner.filesize,
4012 seg.inner.vmaddr,
4013 seg.inner.vmaddr + seg.inner.vmsize,
4014 });
53534015
5354 try trie.put(self.base.allocator, .{4016 // TODO We should probably nop the expanded by distance, or put 0s.
5355 .name = sym_name,4017
5356 .vmaddr_offset = sym.n_value - base_address,4018 // TODO copyRangeAll doesn't automatically extend the file on macOS.
5357 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,4019 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5358 });4020 const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
5359 }4021 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
5360 try trie.finalize(self.base.allocator);4022
4023 var next: usize = match.seg + 1;
4024 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4025 const next_seg = &self.load_commands.items[next].Segment;
4026 _ = try self.base.file.?.copyRangeAll(
4027 next_seg.inner.fileoff,
4028 self.base.file.?,
4029 next_seg.inner.fileoff + seg_offset_amt,
4030 next_seg.inner.filesize,
4031 );
4032 next_seg.inner.fileoff += seg_offset_amt;
4033 next_seg.inner.vmaddr += seg_offset_amt;
4034
4035 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4036 next_seg.inner.segname,
4037 next_seg.inner.fileoff,
4038 next_seg.inner.fileoff + next_seg.inner.filesize,
4039 next_seg.inner.vmaddr,
4040 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4041 });
53614042
5362 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size));4043 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
5363 defer self.base.allocator.free(buffer);4044 moved_sect.offset += @intCast(u32, seg_offset_amt);
5364 var stream = std.io.fixedBufferStream(buffer);4045 moved_sect.addr += seg_offset_amt;
5365 const nwritten = try trie.write(stream.writer());4046
5366 assert(nwritten == trie.size);4047 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4048 commands.segmentName(moved_sect.*),
4049 commands.sectionName(moved_sect.*),
4050 moved_sect.offset,
4051 moved_sect.offset + moved_sect.size,
4052 moved_sect.addr,
4053 moved_sect.addr + moved_sect.size,
4054 });
53674055
5368 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;4056 try self.allocateLocalSymbols(.{
5369 const allocated_size = self.allocatedSizeLinkedit(dyld_info.export_off);4057 .seg = @intCast(u16, next),
5370 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));4058 .sect = @intCast(u16, moved_sect_id),
4059 }, @intCast(i64, seg_offset_amt));
4060 }
4061 }
4062 }
4063
4064 if (match.sect + 1 >= seg.sections.items.len) break :blk;
4065
4066 // We have enough space to expand within the segment, so move all sections by
4067 // the required amount and update their header offsets.
4068 const next_sect = seg.sections.items[match.sect + 1];
4069 const total_size = last_sect_off - next_sect.offset;
4070 _ = try self.base.file.?.copyRangeAll(
4071 next_sect.offset,
4072 self.base.file.?,
4073 next_sect.offset + offset_amt,
4074 total_size,
4075 );
4076
4077 var next = match.sect + 1;
4078 while (next < seg.sections.items.len) : (next += 1) {
4079 const moved_sect = &seg.sections.items[next];
4080 moved_sect.offset += @intCast(u32, offset_amt);
4081 moved_sect.addr += offset_amt;
4082
4083 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4084 commands.segmentName(moved_sect.*),
4085 commands.sectionName(moved_sect.*),
4086 moved_sect.offset,
4087 moved_sect.offset + moved_sect.size,
4088 moved_sect.addr,
4089 moved_sect.addr + moved_sect.size,
4090 });
53714091
5372 if (needed_size > allocated_size) {4092 try self.allocateLocalSymbols(.{
5373 dyld_info.export_off = 0;4093 .seg = match.seg,
5374 dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));4094 .sect = next,
5375 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.4095 }, @intCast(i64, offset_amt));
4096 }
5376 }4097 }
5377 dyld_info.export_size = @intCast(u32, needed_size);4098}
5378 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
53794099
5380 try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);4100fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
5381 self.load_commands_dirty = true;4101 const seg = self.load_commands.items[segment_id].Segment;
5382 self.export_info_dirty = false;4102 assert(start >= seg.inner.fileoff);
4103 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
4104 if (start > min_pos) return 0;
4105 for (seg.sections.items) |section| {
4106 if (section.offset <= start) continue;
4107 if (section.offset < min_pos) min_pos = section.offset;
4108 }
4109 return min_pos - start;
5383}4110}
53844111
5385fn writeRebaseInfoTable(self: *MachO) !void {4112fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
5386 if (!self.rebase_info_dirty) return;4113 const seg = self.load_commands.items[segment_id].Segment;
4114 var max_alignment: u32 = 1;
4115 var next = start_sect_id;
4116 while (next < seg.sections.items.len) : (next += 1) {
4117 const sect = seg.sections.items[next];
4118 const alignment = try math.powi(u32, 2, sect.@"align");
4119 max_alignment = math.max(max_alignment, alignment);
4120 }
4121 return max_alignment;
4122}
53874123
4124fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
5388 const tracy = trace(@src());4125 const tracy = trace(@src());
5389 defer tracy.end();4126 defer tracy.end();
53904127
5391 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);4128 const seg = &self.load_commands.items[match.seg].Segment;
5392 defer pointers.deinit();4129 const sect = &seg.sections.items[match.sect];
4130 var free_list = self.atom_free_lists.get(match).?;
4131 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
4132 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;
53934133
5394 {4134 // We use these to indicate our intention to update metadata, placing the new atom,
5395 var it = self.blocks.iterator();4135 // and possibly removing a free list node.
5396 while (it.next()) |entry| {4136 // It would be simpler to do it inside the for loop below, but that would cause a
5397 const match = entry.key_ptr.*;4137 // problem if an error was returned later in the function. So this action
5398 var block: *TextBlock = entry.value_ptr.*;4138 // is actually carried out at the end of the function, when errors are no longer possible.
53994139 var atom_placement: ?*Atom = null;
5400 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable4140 var free_list_removal: ?usize = null;
5401
5402 const seg = self.load_commands.items[match.seg].Segment;
5403
5404 while (true) {
5405 const sym = self.locals.items[block.local_sym_index];
5406 const base_offset = sym.n_value - seg.inner.vmaddr;
54074141
5408 for (block.rebases.items) |offset| {4142 // First we look for an appropriately sized free list node.
5409 try pointers.append(.{4143 // The list is unordered. We'll just take the first thing that works.
5410 .offset = base_offset + offset,4144 var vaddr = blk: {
5411 .segment_id = match.seg,4145 var i: usize = 0;
5412 });4146 while (i < free_list.items.len) {
4147 const big_atom = free_list.items[i];
4148 // We now have a pointer to a live atom that has too much capacity.
4149 // Is it enough that we could fit this new atom?
4150 const sym = self.locals.items[big_atom.local_sym_index];
4151 const capacity = big_atom.capacity(self.*);
4152 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
4153 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
4154 const capacity_end_vaddr = sym.n_value + capacity;
4155 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
4156 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
4157 if (new_start_vaddr < ideal_capacity_end_vaddr) {
4158 // Additional bookkeeping here to notice if this free list node
4159 // should be deleted because the atom that it points to has grown to take up
4160 // more of the extra capacity.
4161 if (!big_atom.freeListEligible(self.*)) {
4162 const bl = free_list.swapRemove(i);
4163 bl.deinit(self.base.allocator);
4164 } else {
4165 i += 1;
5413 }4166 }
4167 continue;
4168 }
4169 // At this point we know that we will place the new atom here. But the
4170 // remaining question is whether there is still yet enough capacity left
4171 // over for there to still be a free list node.
4172 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
4173 const keep_free_list_node = remaining_capacity >= min_text_capacity;
54144174
5415 if (block.prev) |prev| {4175 // Set up the metadata to be updated, after errors are no longer possible.
5416 block = prev;4176 atom_placement = big_atom;
5417 } else break;4177 if (!keep_free_list_node) {
4178 free_list_removal = i;
5418 }4179 }
4180 break :blk new_start_vaddr;
4181 } else if (self.atoms.get(match)) |last| {
4182 const last_symbol = self.locals.items[last.local_sym_index];
4183 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
4184 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
4185 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
4186 atom_placement = last;
4187 break :blk new_start_vaddr;
4188 } else {
4189 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);
5419 }4190 }
5420 }4191 };
5421
5422 if (self.got_section_index) |idx| {
5423 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
5424 const sect = seg.sections.items[idx];
5425 const base_offset = sect.addr - seg.inner.vmaddr;
5426 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
54274192
5428 for (self.got_entries.items) |entry, i| {4193 const expand_section = atom_placement == null or atom_placement.?.next == null;
5429 if (entry.where == .undef) continue;4194 if (expand_section) {
4195 const needed_size = @intCast(u32, (vaddr + new_atom_size) - sect.addr);
4196 try self.growSection(match, needed_size);
4197 _ = try self.atoms.put(self.base.allocator, match, atom);
4198 sect.size = needed_size;
4199 self.load_commands_dirty = true;
4200 }
4201 const align_pow = @intCast(u32, math.log2(alignment));
4202 if (sect.@"align" < align_pow) {
4203 sect.@"align" = align_pow;
4204 self.load_commands_dirty = true;
4205 }
4206 atom.size = new_atom_size;
4207 atom.alignment = align_pow;
54304208
5431 try pointers.append(.{4209 if (atom.prev) |prev| {
5432 .offset = base_offset + i * @sizeOf(u64),4210 prev.next = atom.next;
5433 .segment_id = segment_id,4211 }
5434 });4212 if (atom.next) |next| {
5435 }4213 next.prev = atom.prev;
5436 }4214 }
54374215
5438 if (self.la_symbol_ptr_section_index) |idx| {4216 if (atom_placement) |big_atom| {
5439 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;4217 atom.prev = big_atom;
5440 const sect = seg.sections.items[idx];4218 atom.next = big_atom.next;
5441 const base_offset = sect.addr - seg.inner.vmaddr;4219 big_atom.next = atom;
5442 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);4220 } else {
54434221 atom.prev = null;
5444 try pointers.ensureUnusedCapacity(self.stubs.items.len);4222 atom.next = null;
5445 for (self.stubs.items) |_, i| {4223 }
5446 pointers.appendAssumeCapacity(.{4224 if (free_list_removal) |i| {
5447 .offset = base_offset + i * @sizeOf(u64),4225 _ = free_list.swapRemove(i);
5448 .segment_id = segment_id,
5449 });
5450 }
5451 }4226 }
54524227
5453 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);4228 return vaddr;
4229}
54544230
5455 const size = try bind.rebaseInfoSize(pointers.items);4231pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
5456 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));4232 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
5457 defer self.base.allocator.free(buffer);4233 defer self.base.allocator.free(sym_name);
54584234
5459 var stream = std.io.fixedBufferStream(buffer);4235 if (self.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
5460 try bind.writeRebaseInfo(pointers.items, stream.writer());4236 .bytes = &self.strtab,
4237 })) |n_strx| {
4238 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
4239 return resolv.where_index;
4240 }
54614241
5462 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;4242 log.debug("adding new extern function '{s}'", .{sym_name});
5463 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);4243 const sym_index = @intCast(u32, self.undefs.items.len);
5464 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));4244 const n_strx = try self.makeString(sym_name);
4245 try self.undefs.append(self.base.allocator, .{
4246 .n_strx = n_strx,
4247 .n_type = macho.N_UNDF,
4248 .n_sect = 0,
4249 .n_desc = 0,
4250 .n_value = 0,
4251 });
4252 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4253 .where = .undef,
4254 .where_index = sym_index,
4255 });
4256 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
54654257
5466 if (needed_size > allocated_size) {4258 return sym_index;
5467 dyld_info.rebase_off = 0;4259}
5468 dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
5469 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
5470 }
54714260
5472 dyld_info.rebase_size = @intCast(u32, needed_size);4261const NextSegmentAddressAndOffset = struct {
5473 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });4262 address: u64,
4263 offset: u64,
4264};
54744265
5475 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);4266fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
5476 self.load_commands_dirty = true;4267 var prev_segment_idx: ?usize = null; // We use optional here for safety.
5477 self.rebase_info_dirty = false;4268 for (self.load_commands.items) |cmd, i| {
4269 if (cmd == .Segment) {
4270 prev_segment_idx = i;
4271 }
4272 }
4273 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
4274 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
4275 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
4276 return .{
4277 .address = address,
4278 .offset = offset,
4279 };
5478}4280}
54794281
5480fn writeBindInfoTable(self: *MachO) !void {4282fn updateSectionOrdinals(self: *MachO) !void {
5481 if (!self.binding_info_dirty) return;4283 if (!self.sections_order_dirty) return;
54824284
5483 const tracy = trace(@src());4285 const tracy = trace(@src());
5484 defer tracy.end();4286 defer tracy.end();
54854287
5486 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);4288 var ordinal_remap = std.AutoHashMap(u8, u8).init(self.base.allocator);
5487 defer pointers.deinit();4289 defer ordinal_remap.deinit();
4290 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
54884291
5489 if (self.got_section_index) |idx| {4292 var new_ordinal: u8 = 0;
5490 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;4293 for (self.load_commands.items) |lc, lc_id| {
5491 const sect = seg.sections.items[idx];4294 if (lc != .Segment) break;
5492 const base_offset = sect.addr - seg.inner.vmaddr;
5493 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
54944295
5495 for (self.got_entries.items) |entry, i| {4296 for (lc.Segment.sections.items) |_, sect_id| {
5496 if (entry.where == .local) continue;4297 const match = MatchingSection{
54974298 .seg = @intCast(u16, lc_id),
5498 const sym = self.undefs.items[entry.where_index];4299 .sect = @intCast(u16, sect_id),
5499 try pointers.append(.{4300 };
5500 .offset = base_offset + i * @sizeOf(u64),4301 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
5501 .segment_id = segment_id,4302 new_ordinal += 1;
5502 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),4303 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5503 .name = self.getString(sym.n_strx),4304 try ordinals.putNoClobber(self.base.allocator, match, {});
5504 });
5505 }4305 }
5506 }4306 }
55074307
4308 for (self.locals.items) |*sym| {
4309 if (sym.n_sect == 0) continue;
4310 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
4311 }
4312 for (self.globals.items) |*sym| {
4313 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
4314 }
4315
4316 self.section_ordinals.deinit(self.base.allocator);
4317 self.section_ordinals = ordinals;
4318}
4319
4320fn writeDyldInfoData(self: *MachO) !void {
4321 const tracy = trace(@src());
4322 defer tracy.end();
4323
4324 var rebase_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
4325 defer rebase_pointers.deinit();
4326 var bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
4327 defer bind_pointers.deinit();
4328 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
4329 defer lazy_bind_pointers.deinit();
4330
5508 {4331 {
5509 var it = self.blocks.iterator();4332 var it = self.atoms.iterator();
5510 while (it.next()) |entry| {4333 while (it.next()) |entry| {
5511 const match = entry.key_ptr.*;4334 const match = entry.key_ptr.*;
5512 var block: *TextBlock = entry.value_ptr.*;4335 var atom: *Atom = entry.value_ptr.*;
55134336
5514 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable4337 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
55154338
5516 const seg = self.load_commands.items[match.seg].Segment;4339 const seg = self.load_commands.items[match.seg].Segment;
55174340
5518 while (true) {4341 while (true) {
5519 const sym = self.locals.items[block.local_sym_index];4342 const sym = self.locals.items[atom.local_sym_index];
5520 const base_offset = sym.n_value - seg.inner.vmaddr;4343 const base_offset = sym.n_value - seg.inner.vmaddr;
55214344
5522 for (block.bindings.items) |binding| {4345 for (atom.rebases.items) |offset| {
4346 try rebase_pointers.append(.{
4347 .offset = base_offset + offset,
4348 .segment_id = match.seg,
4349 });
4350 }
4351
4352 for (atom.bindings.items) |binding| {
4353 const bind_sym = self.undefs.items[binding.local_sym_index];
4354 try bind_pointers.append(.{
4355 .offset = binding.offset + base_offset,
4356 .segment_id = match.seg,
4357 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
4358 .name = self.getString(bind_sym.n_strx),
4359 });
4360 }
4361
4362 for (atom.lazy_bindings.items) |binding| {
5523 const bind_sym = self.undefs.items[binding.local_sym_index];4363 const bind_sym = self.undefs.items[binding.local_sym_index];
5524 try pointers.append(.{4364 try lazy_bind_pointers.append(.{
5525 .offset = binding.offset + base_offset,4365 .offset = binding.offset + base_offset,
5526 .segment_id = match.seg,4366 .segment_id = match.seg,
5527 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),4367 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
...@@ -5529,94 +4369,105 @@ fn writeBindInfoTable(self: *MachO) !void {...@@ -5529,94 +4369,105 @@ fn writeBindInfoTable(self: *MachO) !void {
5529 });4369 });
5530 }4370 }
55314371
5532 if (block.prev) |prev| {4372 if (atom.prev) |prev| {
5533 block = prev;4373 atom = prev;
5534 } else break;4374 } else break;
5535 }4375 }
5536 }4376 }
5537 }4377 }
55384378
5539 const size = try bind.bindInfoSize(pointers.items);4379 var trie: Trie = .{};
5540 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));4380 defer trie.deinit(self.base.allocator);
5541 defer self.base.allocator.free(buffer);
5542
5543 var stream = std.io.fixedBufferStream(buffer);
5544 try bind.writeBindInfo(pointers.items, stream.writer());
55454381
5546 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;4382 {
5547 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);4383 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5548 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));4384 log.debug("generating export trie", .{});
4385 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4386 const base_address = text_segment.inner.vmaddr;
4387
4388 for (self.globals.items) |sym| {
4389 const sym_name = self.getString(sym.n_strx);
4390 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
4391
4392 try trie.put(self.base.allocator, .{
4393 .name = sym_name,
4394 .vmaddr_offset = sym.n_value - base_address,
4395 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
4396 });
4397 }
55494398
5550 if (needed_size > allocated_size) {4399 try trie.finalize(self.base.allocator);
5551 dyld_info.bind_off = 0;
5552 dyld_info.bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
5553 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
5554 }4400 }
55554401
5556 dyld_info.bind_size = @intCast(u32, needed_size);4402 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5557 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });4403 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
55584404 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5559 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);4405 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5560 self.load_commands_dirty = true;4406 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
5561 self.binding_info_dirty = false;4407 const export_size = trie.size;
5562}
5563
5564fn writeLazyBindInfoTable(self: *MachO) !void {
5565 if (!self.lazy_binding_info_dirty) return;
55664408
5567 const tracy = trace(@src());4409 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
5568 defer tracy.end();4410 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64)));
4411 seg.inner.filesize += dyld_info.rebase_size;
55694412
5570 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);4413 dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size;
5571 defer pointers.deinit();4414 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, bind_size, @alignOf(u64)));
4415 seg.inner.filesize += dyld_info.bind_size;
55724416
5573 if (self.la_symbol_ptr_section_index) |idx| {4417 dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size;
5574 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;4418 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64)));
5575 const sect = seg.sections.items[idx];4419 seg.inner.filesize += dyld_info.lazy_bind_size;
5576 const base_offset = sect.addr - seg.inner.vmaddr;
5577 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
5578
5579 try pointers.ensureUnusedCapacity(self.stubs.items.len);
5580
5581 for (self.stubs.items) |import_id, i| {
5582 const sym = self.undefs.items[import_id];
5583 pointers.appendAssumeCapacity(.{
5584 .offset = base_offset + i * @sizeOf(u64),
5585 .segment_id = segment_id,
5586 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
5587 .name = self.getString(sym.n_strx),
5588 });
5589 }
5590 }
55914420
5592 const size = try bind.lazyBindInfoSize(pointers.items);4421 dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size;
5593 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));4422 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, export_size, @alignOf(u64)));
4423 seg.inner.filesize += dyld_info.export_size;
4424
4425 const needed_size = dyld_info.rebase_size + dyld_info.bind_size + dyld_info.lazy_bind_size + dyld_info.export_size;
4426 var buffer = try self.base.allocator.alloc(u8, needed_size);
5594 defer self.base.allocator.free(buffer);4427 defer self.base.allocator.free(buffer);
4428 mem.set(u8, buffer, 0);
55954429
5596 var stream = std.io.fixedBufferStream(buffer);4430 var stream = std.io.fixedBufferStream(buffer);
5597 try bind.writeLazyBindInfo(pointers.items, stream.writer());4431 const writer = stream.writer();
55984432
5599 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;4433 try bind.writeRebaseInfo(rebase_pointers.items, writer);
5600 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);4434 try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size));
5601 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
56024435
5603 if (needed_size > allocated_size) {4436 try bind.writeBindInfo(bind_pointers.items, writer);
5604 dyld_info.lazy_bind_off = 0;4437 try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size));
5605 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
5606 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
5607 }
56084438
5609 dyld_info.lazy_bind_size = @intCast(u32, needed_size);4439 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
5610 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 });4440 try stream.seekBy(@intCast(i64, dyld_info.lazy_bind_size) - @intCast(i64, lazy_bind_size));
56114441
5612 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);4442 _ = try trie.write(writer);
5613 try self.populateLazyBindOffsetsInStubHelper(buffer);4443
4444 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
4445 dyld_info.rebase_off,
4446 dyld_info.rebase_off + needed_size,
4447 });
4448
4449 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
4450 try self.populateLazyBindOffsetsInStubHelper(
4451 buffer[dyld_info.rebase_size + dyld_info.bind_size ..][0..dyld_info.lazy_bind_size],
4452 );
5614 self.load_commands_dirty = true;4453 self.load_commands_dirty = true;
5615 self.lazy_binding_info_dirty = false;
5616}4454}
56174455
5618fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {4456fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5619 if (self.stubs.items.len == 0) return;4457 const last_atom = self.atoms.get(.{
4458 .seg = self.text_segment_cmd_index.?,
4459 .sect = self.stub_helper_section_index.?,
4460 }) orelse return;
4461 if (last_atom == self.stub_helper_preamble_atom.?) return;
4462
4463 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),
4464 // we need reverse the order of atom traversal here as well.
4465 // TODO figure out a less error prone mechanims for this!
4466 var atom = last_atom;
4467 while (atom.prev) |prev| {
4468 atom = prev;
4469 }
4470 atom = atom.next.?;
56204471
5621 var stream = std.io.fixedBufferStream(buffer);4472 var stream = std.io.fixedBufferStream(buffer);
5622 var reader = stream.reader();4473 var reader = stream.reader();
...@@ -5661,50 +4512,245 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5661,50 +4512,245 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5661 else => {},4512 else => {},
5662 }4513 }
5663 }4514 }
5664 assert(self.stubs.items.len <= offsets.items.len);
56654515
5666 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {4516 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5667 .x86_64 => 10,4517 const sect = seg.sections.items[self.stub_helper_section_index.?];
5668 .aarch64 => 3 * @sizeOf(u32),4518 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
5669 else => unreachable,
5670 };
5671 const off: u4 = switch (self.base.options.target.cpu.arch) {
5672 .x86_64 => 1,4519 .x86_64 => 1,
5673 .aarch64 => 2 * @sizeOf(u32),4520 .aarch64 => 2 * @sizeOf(u32),
5674 else => unreachable,4521 else => unreachable,
5675 };4522 };
5676 var buf: [@sizeOf(u32)]u8 = undefined;4523 var buf: [@sizeOf(u32)]u8 = undefined;
5677 for (self.stubs.items) |_, index| {4524 _ = offsets.pop();
5678 const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off;4525 while (offsets.popOrNull()) |bind_offset| {
5679 mem.writeIntLittle(u32, &buf, offsets.items[index]);4526 const sym = self.locals.items[atom.local_sym_index];
5680 try self.base.file.?.pwriteAll(&buf, placeholder_off);4527 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
4528 mem.writeIntLittle(u32, &buf, bind_offset);
4529 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
4530 bind_offset,
4531 self.getString(sym.n_strx),
4532 file_offset,
4533 });
4534 try self.base.file.?.pwriteAll(&buf, file_offset);
4535
4536 if (atom.next) |next| {
4537 atom = next;
4538 } else break;
5681 }4539 }
5682}4540}
56834541
5684fn writeStringTable(self: *MachO) !void {4542fn writeDices(self: *MachO) !void {
5685 if (!self.strtab_dirty) return;4543 if (!self.has_dices) return;
4544
4545 const tracy = trace(@src());
4546 defer tracy.end();
4547
4548 var buf = std.ArrayList(u8).init(self.base.allocator);
4549 defer buf.deinit();
4550
4551 var atom: *Atom = self.atoms.get(.{
4552 .seg = self.text_segment_cmd_index orelse return,
4553 .sect = self.text_section_index orelse return,
4554 }) orelse return;
4555
4556 while (atom.prev) |prev| {
4557 atom = prev;
4558 }
4559
4560 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4561 const text_sect = text_seg.sections.items[self.text_section_index.?];
4562
4563 while (true) {
4564 if (atom.dices.items.len > 0) {
4565 const sym = self.locals.items[atom.local_sym_index];
4566 const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset);
4567
4568 try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry));
4569 for (atom.dices.items) |dice| {
4570 const rebased_dice = macho.data_in_code_entry{
4571 .offset = base_off + dice.offset,
4572 .length = dice.length,
4573 .kind = dice.kind,
4574 };
4575 buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice));
4576 }
4577 }
4578
4579 if (atom.next) |next| {
4580 atom = next;
4581 } else break;
4582 }
4583
4584 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4585 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
4586 const needed_size = @intCast(u32, buf.items.len);
56864587
4588 dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
4589 dice_cmd.datasize = needed_size;
4590 seg.inner.filesize += needed_size;
4591
4592 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
4593 dice_cmd.dataoff,
4594 dice_cmd.dataoff + dice_cmd.datasize,
4595 });
4596
4597 try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff);
4598 self.load_commands_dirty = true;
4599}
4600
4601fn writeSymbolTable(self: *MachO) !void {
5687 const tracy = trace(@src());4602 const tracy = trace(@src());
5688 defer tracy.end();4603 defer tracy.end();
56894604
4605 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5690 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;4606 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5691 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);4607 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
5692 const needed_size = mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64));4608
4609 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
4610 defer locals.deinit();
4611 try locals.appendSlice(self.locals.items);
4612
4613 if (self.has_stabs) {
4614 for (self.objects.items) |object| {
4615 if (object.debug_info == null) continue;
4616
4617 // Open scope
4618 try locals.ensureUnusedCapacity(3);
4619 locals.appendAssumeCapacity(.{
4620 .n_strx = try self.makeString(object.tu_comp_dir.?),
4621 .n_type = macho.N_SO,
4622 .n_sect = 0,
4623 .n_desc = 0,
4624 .n_value = 0,
4625 });
4626 locals.appendAssumeCapacity(.{
4627 .n_strx = try self.makeString(object.tu_name.?),
4628 .n_type = macho.N_SO,
4629 .n_sect = 0,
4630 .n_desc = 0,
4631 .n_value = 0,
4632 });
4633 locals.appendAssumeCapacity(.{
4634 .n_strx = try self.makeString(object.name),
4635 .n_type = macho.N_OSO,
4636 .n_sect = 0,
4637 .n_desc = 1,
4638 .n_value = object.mtime orelse 0,
4639 });
4640
4641 for (object.atoms.items) |atom| {
4642 if (atom.stab) |stab| {
4643 const nlists = try stab.asNlists(atom.local_sym_index, self);
4644 defer self.base.allocator.free(nlists);
4645 try locals.appendSlice(nlists);
4646 } else {
4647 for (atom.contained.items) |sym_at_off| {
4648 const stab = sym_at_off.stab orelse continue;
4649 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
4650 defer self.base.allocator.free(nlists);
4651 try locals.appendSlice(nlists);
4652 }
4653 }
4654 }
56934655
5694 if (needed_size > allocated_size or self.strtab_needs_relocation) {4656 // Close scope
5695 symtab.strsize = 0;4657 try locals.append(.{
5696 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));4658 .n_strx = 0,
5697 self.strtab_needs_relocation = false;4659 .n_type = macho.N_SO,
4660 .n_sect = 0,
4661 .n_desc = 0,
4662 .n_value = 0,
4663 });
4664 }
5698 }4665 }
5699 symtab.strsize = @intCast(u32, needed_size);
5700 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
57014666
5702 try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);4667 const nlocals = locals.items.len;
4668 const nexports = self.globals.items.len;
4669 const nundefs = self.undefs.items.len;
4670
4671 const locals_off = symtab.symoff;
4672 const locals_size = nlocals * @sizeOf(macho.nlist_64);
4673 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
4674 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
4675
4676 const exports_off = locals_off + locals_size;
4677 const exports_size = nexports * @sizeOf(macho.nlist_64);
4678 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
4679 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
4680
4681 const undefs_off = exports_off + exports_size;
4682 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
4683 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
4684 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
4685
4686 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);
4687 seg.inner.filesize += locals_size + exports_size + undefs_size;
4688
4689 // Update dynamic symbol table.
4690 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
4691 dysymtab.nlocalsym = @intCast(u32, nlocals);
4692 dysymtab.iextdefsym = dysymtab.nlocalsym;
4693 dysymtab.nextdefsym = @intCast(u32, nexports);
4694 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
4695 dysymtab.nundefsym = @intCast(u32, nundefs);
4696
4697 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4698 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
4699 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
4700 const got = &data_const_segment.sections.items[self.got_section_index.?];
4701 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4702 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
4703
4704 const nstubs = @intCast(u32, self.stubs_map.keys().len);
4705 const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
4706
4707 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
4708 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
4709
4710 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
4711 seg.inner.filesize += needed_size;
4712
4713 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
4714 dysymtab.indirectsymoff,
4715 dysymtab.indirectsymoff + needed_size,
4716 });
4717
4718 var buf = try self.base.allocator.alloc(u8, needed_size);
4719 defer self.base.allocator.free(buf);
4720
4721 var stream = std.io.fixedBufferStream(buf);
4722 var writer = stream.writer();
4723
4724 stubs.reserved1 = 0;
4725 for (self.stubs_map.keys()) |key| {
4726 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
4727 }
4728
4729 got.reserved1 = nstubs;
4730 for (self.got_entries_map.keys()) |key| {
4731 switch (key.where) {
4732 .undef => {
4733 try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);
4734 },
4735 .local => {
4736 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
4737 },
4738 }
4739 }
4740
4741 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
4742 for (self.stubs_map.keys()) |key| {
4743 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
4744 }
4745
4746 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
5703 self.load_commands_dirty = true;4747 self.load_commands_dirty = true;
5704 self.strtab_dirty = false;
5705}4748}
57064749
5707fn writeStringTableZld(self: *MachO) !void {4750fn writeStringTable(self: *MachO) !void {
4751 const tracy = trace(@src());
4752 defer tracy.end();
4753
5708 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;4754 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5709 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;4755 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5710 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);4756 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
...@@ -5719,55 +4765,81 @@ fn writeStringTableZld(self: *MachO) !void {...@@ -5719,55 +4765,81 @@ fn writeStringTableZld(self: *MachO) !void {
5719 // This is potentially the last section, so we need to pad it out.4765 // This is potentially the last section, so we need to pad it out.
5720 try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);4766 try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
5721 }4767 }
4768 self.load_commands_dirty = true;
5722}4769}
57234770
5724fn updateLinkeditSegmentSizes(self: *MachO) !void {4771fn writeLinkeditSegment(self: *MachO) !void {
5725 if (!self.load_commands_dirty) return;4772 const tracy = trace(@src());
4773 defer tracy.end();
4774
4775 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4776 seg.inner.filesize = 0;
4777
4778 try self.writeDyldInfoData();
4779 try self.writeDices();
4780 try self.writeSymbolTable();
4781 try self.writeStringTable();
4782
4783 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
4784}
57264785
4786fn writeCodeSignaturePadding(self: *MachO) !void {
5727 const tracy = trace(@src());4787 const tracy = trace(@src());
5728 defer tracy.end();4788 defer tracy.end();
57294789
5730 // Now, we are in position to update __LINKEDIT segment sizes.
5731 // TODO Add checkpointing so that we don't have to do this every single time.
5732 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;4790 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5733 var final_offset = linkedit_segment.inner.fileoff;4791 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
57344792 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
5735 if (self.dyld_info_cmd_index) |idx| {4793 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
5736 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;4794 self.base.options.emit.?.sub_path,
5737 final_offset = std.math.max(final_offset, dyld_info.rebase_off + dyld_info.rebase_size);4795 fileoff,
5738 final_offset = std.math.max(final_offset, dyld_info.bind_off + dyld_info.bind_size);4796 self.page_size,
5739 final_offset = std.math.max(final_offset, dyld_info.weak_bind_off + dyld_info.weak_bind_size);4797 );
5740 final_offset = std.math.max(final_offset, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size);4798 code_sig_cmd.dataoff = @intCast(u32, fileoff);
5741 final_offset = std.math.max(final_offset, dyld_info.export_off + dyld_info.export_size);4799 code_sig_cmd.datasize = needed_size;
5742 }4800
5743 if (self.function_starts_cmd_index) |idx| {4801 // Advance size of __LINKEDIT segment
5744 const fstart = self.load_commands.items[idx].LinkeditData;4802 linkedit_segment.inner.filesize += needed_size;
5745 final_offset = std.math.max(final_offset, fstart.dataoff + fstart.datasize);4803 if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) {
5746 }4804 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size);
5747 if (self.data_in_code_cmd_index) |idx| {4805 }
5748 const dic = self.load_commands.items[idx].LinkeditData;4806 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
5749 final_offset = std.math.max(final_offset, dic.dataoff + dic.datasize);4807 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
5750 }4808 // except for code signature data.
5751 if (self.dysymtab_cmd_index) |idx| {4809 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
5752 const dysymtab = self.load_commands.items[idx].Dysymtab;
5753 const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32);
5754 final_offset = std.math.max(final_offset, dysymtab.indirectsymoff + nindirectsize);
5755 // TODO Handle more dynamic symbol table sections.
5756 }
5757 if (self.symtab_cmd_index) |idx| {
5758 const symtab = self.load_commands.items[idx].Symtab;
5759 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
5760 final_offset = std.math.max(final_offset, symtab.symoff + symsize);
5761 final_offset = std.math.max(final_offset, symtab.stroff + symtab.strsize);
5762 }
5763
5764 const filesize = final_offset - linkedit_segment.inner.fileoff;
5765 linkedit_segment.inner.filesize = filesize;
5766 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, filesize, self.page_size);
5767 try self.base.file.?.pwriteAll(&[_]u8{0}, final_offset);
5768 self.load_commands_dirty = true;4810 self.load_commands_dirty = true;
5769}4811}
57704812
4813fn writeCodeSignature(self: *MachO) !void {
4814 const tracy = trace(@src());
4815 defer tracy.end();
4816
4817 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4818 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
4819
4820 var code_sig: CodeSignature = .{};
4821 defer code_sig.deinit(self.base.allocator);
4822
4823 try code_sig.calcAdhocSignature(
4824 self.base.allocator,
4825 self.base.file.?,
4826 self.base.options.emit.?.sub_path,
4827 text_segment.inner,
4828 code_sig_cmd,
4829 self.base.options.output_mode,
4830 self.page_size,
4831 );
4832
4833 var buffer = try self.base.allocator.alloc(u8, code_sig.size());
4834 defer self.base.allocator.free(buffer);
4835 var stream = std.io.fixedBufferStream(buffer);
4836 try code_sig.write(stream.writer());
4837
4838 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
4839
4840 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
4841}
4842
5771/// Writes all load commands and section headers.4843/// Writes all load commands and section headers.
5772fn writeLoadCommands(self: *MachO) !void {4844fn writeLoadCommands(self: *MachO) !void {
5773 if (!self.load_commands_dirty) return;4845 if (!self.load_commands_dirty) return;
...@@ -5844,6 +4916,13 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -5844,6 +4916,13 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
5844 std.math.maxInt(@TypeOf(actual_size));4916 std.math.maxInt(@TypeOf(actual_size));
5845}4917}
58464918
4919pub fn makeStaticString(bytes: []const u8) [16]u8 {
4920 var buf = [_]u8{0} ** 16;
4921 assert(bytes.len <= buf.len);
4922 mem.copy(u8, &buf, bytes);
4923 return buf;
4924}
4925
5847pub fn makeString(self: *MachO, string: []const u8) !u32 {4926pub fn makeString(self: *MachO, string: []const u8) !u32 {
5848 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{4927 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{
5849 .bytes = &self.strtab,4928 .bytes = &self.strtab,
src/link/MachO/Atom.zig created+1324
...@@ -0,0 +1,1324 @@
1const Atom = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const aarch64 = @import("../../codegen/aarch64.zig");
6const assert = std.debug.assert;
7const commands = @import("commands.zig");
8const log = std.log.scoped(.text_block);
9const macho = std.macho;
10const math = std.math;
11const mem = std.mem;
12const meta = std.meta;
13const trace = @import("../../tracy.zig").trace;
14
15const Allocator = mem.Allocator;
16const Arch = std.Target.Cpu.Arch;
17const MachO = @import("../MachO.zig");
18const Object = @import("Object.zig");
19const StringIndexAdapter = std.hash_map.StringIndexAdapter;
20
21/// Each decl always gets a local symbol with the fully qualified name.
22/// The vaddr and size are found here directly.
23/// The file offset is found by computing the vaddr offset from the section vaddr
24/// the symbol references, and adding that to the file offset of the section.
25/// If this field is 0, it means the codegen size = 0 and there is no symbol or
26/// offset table entry.
27local_sym_index: u32,
28
29/// List of symbol aliases pointing to the same atom via different nlists
30aliases: std.ArrayListUnmanaged(u32) = .{},
31
32/// List of symbols contained within this atom
33contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
34
35/// Code (may be non-relocated) this atom represents
36code: std.ArrayListUnmanaged(u8) = .{},
37
38/// Size and alignment of this atom
39/// Unlike in Elf, we need to store the size of this symbol as part of
40/// the atom since macho.nlist_64 lacks this information.
41size: u64,
42
43/// Alignment of this atom as a power of 2.
44/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
45alignment: u32,
46
47/// List of relocations belonging to this atom.
48relocs: std.ArrayListUnmanaged(Relocation) = .{},
49
50/// List of offsets contained within this atom that need rebasing by the dynamic
51/// loader in presence of ASLR.
52rebases: std.ArrayListUnmanaged(u64) = .{},
53
54/// List of offsets contained within this atom that will be dynamically bound
55/// by the dynamic loader and contain pointers to resolved (at load time) extern
56/// symbols (aka proxies aka imports)
57bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
58
59/// List of lazy bindings
60lazy_bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
61
62/// List of data-in-code entries. This is currently specific to x86_64 only.
63dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
64
65/// Stab entry for this atom. This is currently specific to a binary created
66/// by linking object files in a traditional sense - in incremental sense, we
67/// bypass stabs altogether to produce dSYM bundle directly with fully relocated
68/// DWARF sections.
69stab: ?Stab = null,
70
71/// Points to the previous and next neighbours
72next: ?*Atom,
73prev: ?*Atom,
74
75/// Previous/next linked list pointers.
76/// This is the linked list node for this Decl's corresponding .debug_info tag.
77dbg_info_prev: ?*Atom,
78dbg_info_next: ?*Atom,
79/// Offset into .debug_info pointing to the tag for this Decl.
80dbg_info_off: u32,
81/// Size of the .debug_info tag for this Decl, not including padding.
82dbg_info_len: u32,
83
84dirty: bool = true,
85
86pub const SymbolAtOffset = struct {
87 local_sym_index: u32,
88 offset: u64,
89 stab: ?Stab = null,
90
91 pub fn format(
92 self: SymbolAtOffset,
93 comptime fmt: []const u8,
94 options: std.fmt.FormatOptions,
95 writer: anytype,
96 ) !void {
97 _ = fmt;
98 _ = options;
99 try std.fmt.format(writer, "{{ {d}: .offset = {d}", .{ self.local_sym_index, self.offset });
100 if (self.stab) |stab| {
101 try std.fmt.format(writer, ", .stab = {any}", .{stab});
102 }
103 try std.fmt.format(writer, " }}", .{});
104 }
105};
106
107pub const Stab = union(enum) {
108 function: u64,
109 static,
110 global,
111
112 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
113 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
114 defer nlists.deinit();
115
116 const sym = macho_file.locals.items[local_sym_index];
117 switch (stab) {
118 .function => |size| {
119 try nlists.ensureUnusedCapacity(4);
120 nlists.appendAssumeCapacity(.{
121 .n_strx = 0,
122 .n_type = macho.N_BNSYM,
123 .n_sect = sym.n_sect,
124 .n_desc = 0,
125 .n_value = sym.n_value,
126 });
127 nlists.appendAssumeCapacity(.{
128 .n_strx = sym.n_strx,
129 .n_type = macho.N_FUN,
130 .n_sect = sym.n_sect,
131 .n_desc = 0,
132 .n_value = sym.n_value,
133 });
134 nlists.appendAssumeCapacity(.{
135 .n_strx = 0,
136 .n_type = macho.N_FUN,
137 .n_sect = 0,
138 .n_desc = 0,
139 .n_value = size,
140 });
141 nlists.appendAssumeCapacity(.{
142 .n_strx = 0,
143 .n_type = macho.N_ENSYM,
144 .n_sect = sym.n_sect,
145 .n_desc = 0,
146 .n_value = size,
147 });
148 },
149 .global => {
150 try nlists.append(.{
151 .n_strx = sym.n_strx,
152 .n_type = macho.N_GSYM,
153 .n_sect = 0,
154 .n_desc = 0,
155 .n_value = 0,
156 });
157 },
158 .static => {
159 try nlists.append(.{
160 .n_strx = sym.n_strx,
161 .n_type = macho.N_STSYM,
162 .n_sect = sym.n_sect,
163 .n_desc = 0,
164 .n_value = sym.n_value,
165 });
166 },
167 }
168
169 return nlists.toOwnedSlice();
170 }
171};
172
173pub const Relocation = struct {
174 /// Offset within the atom's code buffer.
175 /// Note relocation size can be inferred by relocation's kind.
176 offset: u32,
177
178 where: enum {
179 local,
180 undef,
181 },
182
183 where_index: u32,
184
185 payload: union(enum) {
186 unsigned: Unsigned,
187 branch: Branch,
188 page: Page,
189 page_off: PageOff,
190 pointer_to_got: PointerToGot,
191 signed: Signed,
192 load: Load,
193 },
194
195 const ResolveArgs = struct {
196 block: *Atom,
197 offset: u32,
198 source_addr: u64,
199 target_addr: u64,
200 macho_file: *MachO,
201 };
202
203 pub const Unsigned = struct {
204 subtractor: ?u32,
205
206 /// Addend embedded directly in the relocation slot
207 addend: i64,
208
209 /// Extracted from r_length:
210 /// => 3 implies true
211 /// => 2 implies false
212 /// => * is unreachable
213 is_64bit: bool,
214
215 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
216 const result = blk: {
217 if (self.subtractor) |subtractor| {
218 const sym = args.macho_file.locals.items[subtractor];
219 break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;
220 } else {
221 break :blk @intCast(i64, args.target_addr) + self.addend;
222 }
223 };
224
225 if (self.is_64bit) {
226 mem.writeIntLittle(u64, args.block.code.items[args.offset..][0..8], @bitCast(u64, result));
227 } else {
228 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
229 }
230 }
231
232 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
233 _ = fmt;
234 _ = options;
235 try std.fmt.format(writer, "Unsigned {{ ", .{});
236 if (self.subtractor) |sub| {
237 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
238 }
239 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
240 const length: usize = if (self.is_64bit) 8 else 4;
241 try std.fmt.format(writer, ".length = {}, ", .{length});
242 try std.fmt.format(writer, "}}", .{});
243 }
244 };
245
246 pub const Branch = struct {
247 arch: Arch,
248
249 pub fn resolve(self: Branch, args: ResolveArgs) !void {
250 switch (self.arch) {
251 .aarch64 => {
252 const displacement = math.cast(
253 i28,
254 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
255 ) catch |err| switch (err) {
256 error.Overflow => {
257 log.err("jump too big to encode as i28 displacement value", .{});
258 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
259 args.target_addr,
260 args.source_addr,
261 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
262 });
263 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
264 return error.TODOImplementBranchIslands;
265 },
266 };
267 const code = args.block.code.items[args.offset..][0..4];
268 var inst = aarch64.Instruction{
269 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
270 aarch64.Instruction,
271 aarch64.Instruction.unconditional_branch_immediate,
272 ), code),
273 };
274 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
275 mem.writeIntLittle(u32, code, inst.toU32());
276 },
277 .x86_64 => {
278 const displacement = try math.cast(
279 i32,
280 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
281 );
282 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
283 },
284 else => return error.UnsupportedCpuArchitecture,
285 }
286 }
287
288 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
289 _ = self;
290 _ = fmt;
291 _ = options;
292 try std.fmt.format(writer, "Branch {{}}", .{});
293 }
294 };
295
296 pub const Page = struct {
297 kind: enum {
298 page,
299 got,
300 tlvp,
301 },
302 addend: u32 = 0,
303
304 pub fn resolve(self: Page, args: ResolveArgs) !void {
305 const target_addr = args.target_addr + self.addend;
306 const source_page = @intCast(i32, args.source_addr >> 12);
307 const target_page = @intCast(i32, target_addr >> 12);
308 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
309
310 const code = args.block.code.items[args.offset..][0..4];
311 var inst = aarch64.Instruction{
312 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
313 aarch64.Instruction,
314 aarch64.Instruction.pc_relative_address,
315 ), code),
316 };
317 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
318 inst.pc_relative_address.immlo = @truncate(u2, pages);
319
320 mem.writeIntLittle(u32, code, inst.toU32());
321 }
322
323 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
324 _ = fmt;
325 _ = options;
326 try std.fmt.format(writer, "Page {{ ", .{});
327 switch (self.kind) {
328 .page => {},
329 .got => {
330 try std.fmt.format(writer, ".got, ", .{});
331 },
332 .tlvp => {
333 try std.fmt.format(writer, ".tlvp", .{});
334 },
335 }
336 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
337 try std.fmt.format(writer, "}}", .{});
338 }
339 };
340
341 pub const PageOff = struct {
342 kind: enum {
343 page,
344 got,
345 tlvp,
346 },
347 addend: u32 = 0,
348 op_kind: ?OpKind = null,
349
350 pub const OpKind = enum {
351 arithmetic,
352 load,
353 };
354
355 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
356 const code = args.block.code.items[args.offset..][0..4];
357
358 switch (self.kind) {
359 .page => {
360 const target_addr = args.target_addr + self.addend;
361 const narrowed = @truncate(u12, target_addr);
362
363 const op_kind = self.op_kind orelse unreachable;
364 var inst: aarch64.Instruction = blk: {
365 switch (op_kind) {
366 .arithmetic => {
367 break :blk .{
368 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
369 aarch64.Instruction,
370 aarch64.Instruction.add_subtract_immediate,
371 ), code),
372 };
373 },
374 .load => {
375 break :blk .{
376 .load_store_register = mem.bytesToValue(meta.TagPayload(
377 aarch64.Instruction,
378 aarch64.Instruction.load_store_register,
379 ), code),
380 };
381 },
382 }
383 };
384
385 if (op_kind == .arithmetic) {
386 inst.add_subtract_immediate.imm12 = narrowed;
387 } else {
388 const offset: u12 = blk: {
389 if (inst.load_store_register.size == 0) {
390 if (inst.load_store_register.v == 1) {
391 // 128-bit SIMD is scaled by 16.
392 break :blk try math.divExact(u12, narrowed, 16);
393 }
394 // Otherwise, 8-bit SIMD or ldrb.
395 break :blk narrowed;
396 } else {
397 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
398 break :blk try math.divExact(u12, narrowed, denom);
399 }
400 };
401 inst.load_store_register.offset = offset;
402 }
403
404 mem.writeIntLittle(u32, code, inst.toU32());
405 },
406 .got => {
407 const narrowed = @truncate(u12, args.target_addr);
408 var inst: aarch64.Instruction = .{
409 .load_store_register = mem.bytesToValue(meta.TagPayload(
410 aarch64.Instruction,
411 aarch64.Instruction.load_store_register,
412 ), code),
413 };
414 const offset = try math.divExact(u12, narrowed, 8);
415 inst.load_store_register.offset = offset;
416 mem.writeIntLittle(u32, code, inst.toU32());
417 },
418 .tlvp => {
419 const RegInfo = struct {
420 rd: u5,
421 rn: u5,
422 size: u1,
423 };
424 const reg_info: RegInfo = blk: {
425 if (isArithmeticOp(code)) {
426 const inst = mem.bytesToValue(meta.TagPayload(
427 aarch64.Instruction,
428 aarch64.Instruction.add_subtract_immediate,
429 ), code);
430 break :blk .{
431 .rd = inst.rd,
432 .rn = inst.rn,
433 .size = inst.sf,
434 };
435 } else {
436 const inst = mem.bytesToValue(meta.TagPayload(
437 aarch64.Instruction,
438 aarch64.Instruction.load_store_register,
439 ), code);
440 break :blk .{
441 .rd = inst.rt,
442 .rn = inst.rn,
443 .size = @truncate(u1, inst.size),
444 };
445 }
446 };
447 const narrowed = @truncate(u12, args.target_addr);
448 var inst = aarch64.Instruction{
449 .add_subtract_immediate = .{
450 .rd = reg_info.rd,
451 .rn = reg_info.rn,
452 .imm12 = narrowed,
453 .sh = 0,
454 .s = 0,
455 .op = 0,
456 .sf = reg_info.size,
457 },
458 };
459 mem.writeIntLittle(u32, code, inst.toU32());
460 },
461 }
462 }
463
464 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
465 _ = fmt;
466 _ = options;
467 try std.fmt.format(writer, "PageOff {{ ", .{});
468 switch (self.kind) {
469 .page => {},
470 .got => {
471 try std.fmt.format(writer, ".got, ", .{});
472 },
473 .tlvp => {
474 try std.fmt.format(writer, ".tlvp, ", .{});
475 },
476 }
477 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
478 try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
479 try std.fmt.format(writer, "}}", .{});
480 }
481 };
482
483 pub const PointerToGot = struct {
484 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
485 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
486 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, result));
487 }
488
489 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
490 _ = self;
491 _ = fmt;
492 _ = options;
493 try std.fmt.format(writer, "PointerToGot {{}}", .{});
494 }
495 };
496
497 pub const Signed = struct {
498 addend: i64,
499 correction: u3,
500
501 pub fn resolve(self: Signed, args: ResolveArgs) !void {
502 const target_addr = @intCast(i64, args.target_addr) + self.addend;
503 const displacement = try math.cast(
504 i32,
505 target_addr - @intCast(i64, args.source_addr + self.correction + 4),
506 );
507 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
508 }
509
510 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
511 _ = fmt;
512 _ = options;
513 try std.fmt.format(writer, "Signed {{ ", .{});
514 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
515 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
516 try std.fmt.format(writer, "}}", .{});
517 }
518 };
519
520 pub const Load = struct {
521 kind: enum {
522 got,
523 tlvp,
524 },
525 addend: i32 = 0,
526
527 pub fn resolve(self: Load, args: ResolveArgs) !void {
528 if (self.kind == .tlvp) {
529 // We need to rewrite the opcode from movq to leaq.
530 args.block.code.items[args.offset - 2] = 0x8d;
531 }
532 const displacement = try math.cast(
533 i32,
534 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,
535 );
536 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
537 }
538
539 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
540 _ = fmt;
541 _ = options;
542 try std.fmt.format(writer, "Load {{ ", .{});
543 try std.fmt.format(writer, "{s}, ", .{self.kind});
544 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
545 try std.fmt.format(writer, "}}", .{});
546 }
547 };
548
549 pub fn resolve(self: Relocation, args: ResolveArgs) !void {
550 switch (self.payload) {
551 .unsigned => |unsigned| try unsigned.resolve(args),
552 .branch => |branch| try branch.resolve(args),
553 .page => |page| try page.resolve(args),
554 .page_off => |page_off| try page_off.resolve(args),
555 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
556 .signed => |signed| try signed.resolve(args),
557 .load => |load| try load.resolve(args),
558 }
559 }
560
561 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
562 try std.fmt.format(writer, "Relocation {{ ", .{});
563 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
564 try std.fmt.format(writer, ".where = {}, ", .{self.where});
565 try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index});
566
567 switch (self.payload) {
568 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
569 .branch => |branch| try branch.format(fmt, options, writer),
570 .page => |page| try page.format(fmt, options, writer),
571 .page_off => |page_off| try page_off.format(fmt, options, writer),
572 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
573 .signed => |signed| try signed.format(fmt, options, writer),
574 .load => |load| try load.format(fmt, options, writer),
575 }
576
577 try std.fmt.format(writer, "}}", .{});
578 }
579};
580
581pub const empty = Atom{
582 .local_sym_index = 0,
583 .size = 0,
584 .alignment = 0,
585 .prev = null,
586 .next = null,
587 .dbg_info_prev = null,
588 .dbg_info_next = null,
589 .dbg_info_off = undefined,
590 .dbg_info_len = undefined,
591};
592
593pub fn deinit(self: *Atom, allocator: *Allocator) void {
594 self.dices.deinit(allocator);
595 self.lazy_bindings.deinit(allocator);
596 self.bindings.deinit(allocator);
597 self.rebases.deinit(allocator);
598 self.relocs.deinit(allocator);
599 self.contained.deinit(allocator);
600 self.aliases.deinit(allocator);
601 self.code.deinit(allocator);
602}
603
604pub fn clearRetainingCapacity(self: *Atom) void {
605 self.dices.clearRetainingCapacity();
606 self.lazy_bindings.clearRetainingCapacity();
607 self.bindings.clearRetainingCapacity();
608 self.rebases.clearRetainingCapacity();
609 self.relocs.clearRetainingCapacity();
610 self.contained.clearRetainingCapacity();
611 self.aliases.clearRetainingCapacity();
612 self.code.clearRetainingCapacity();
613}
614
615/// Returns how much room there is to grow in virtual address space.
616/// File offset relocation happens transparently, so it is not included in
617/// this calculation.
618pub fn capacity(self: Atom, macho_file: MachO) u64 {
619 const self_sym = macho_file.locals.items[self.local_sym_index];
620 if (self.next) |next| {
621 const next_sym = macho_file.locals.items[next.local_sym_index];
622 return next_sym.n_value - self_sym.n_value;
623 } else {
624 // We are the last atom.
625 // The capacity is limited only by virtual address space.
626 return std.math.maxInt(u64) - self_sym.n_value;
627 }
628}
629
630pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
631 // No need to keep a free list node for the last atom.
632 const next = self.next orelse return false;
633 const self_sym = macho_file.locals.items[self.local_sym_index];
634 const next_sym = macho_file.locals.items[next.local_sym_index];
635 const cap = next_sym.n_value - self_sym.n_value;
636 const ideal_cap = MachO.padToIdeal(self.size);
637 if (cap <= ideal_cap) return false;
638 const surplus = cap - ideal_cap;
639 return surplus >= MachO.min_text_capacity;
640}
641
642const RelocContext = struct {
643 base_addr: u64 = 0,
644 base_offset: u64 = 0,
645 allocator: *Allocator,
646 object: *Object,
647 macho_file: *MachO,
648 parsed_atoms: *Object.ParsedAtoms,
649};
650
651fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
652 var parsed_rel = Relocation{
653 .offset = @intCast(u32, @intCast(u64, rel.r_address) - context.base_offset),
654 .where = undefined,
655 .where_index = undefined,
656 .payload = undefined,
657 };
658
659 if (rel.r_extern == 0) {
660 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
661
662 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
663 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
664 const sect = seg.sections.items[sect_id];
665 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
666 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
667 const sym_name = try std.fmt.allocPrint(context.allocator, "l_{s}_{s}_{s}", .{
668 context.object.name,
669 commands.segmentName(sect),
670 commands.sectionName(sect),
671 });
672 defer context.allocator.free(sym_name);
673
674 try context.macho_file.locals.append(context.allocator, .{
675 .n_strx = try context.macho_file.makeString(sym_name),
676 .n_type = macho.N_SECT,
677 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
678 .n_desc = 0,
679 .n_value = 0,
680 });
681 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
682 break :blk local_sym_index;
683 };
684
685 parsed_rel.where = .local;
686 parsed_rel.where_index = local_sym_index;
687 } else {
688 const sym = context.object.symtab.items[rel.r_symbolnum];
689 const sym_name = context.object.getString(sym.n_strx);
690
691 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
692 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
693 parsed_rel.where = .local;
694 parsed_rel.where_index = where_index;
695 } else {
696 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
697 .bytes = &context.macho_file.strtab,
698 }) orelse unreachable;
699 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
700 switch (resolv.where) {
701 .global => {
702 parsed_rel.where = .local;
703 parsed_rel.where_index = resolv.local_sym_index;
704 },
705 .undef => {
706 parsed_rel.where = .undef;
707 parsed_rel.where_index = resolv.where_index;
708 },
709 }
710 }
711 }
712
713 return parsed_rel;
714}
715
716pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocContext) !void {
717 const tracy = trace(@src());
718 defer tracy.end();
719
720 const filtered_relocs = filterRelocs(relocs, context.base_offset, context.base_offset + self.size);
721 var it = RelocIterator{
722 .buffer = filtered_relocs,
723 };
724
725 var addend: u32 = 0;
726 var subtractor: ?u32 = null;
727 const arch = context.macho_file.base.options.target.cpu.arch;
728
729 while (it.next()) |rel| {
730 if (isAddend(rel, arch)) {
731 // Addend is not a relocation with effect on the TextBlock, so
732 // parse it and carry on.
733 assert(addend == 0); // Oh no, addend was not reset!
734 addend = rel.r_symbolnum;
735
736 // Verify ADDEND is followed by a PAGE21 or PAGEOFF12.
737 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
738 switch (next) {
739 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
740 else => {
741 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
742 return error.UnexpectedRelocationType;
743 },
744 }
745 continue;
746 }
747
748 if (isSubtractor(rel, arch)) {
749 // Subtractor is not a relocation with effect on the TextBlock, so
750 // parse it and carry on.
751 assert(subtractor == null); // Oh no, subtractor was not reset!
752 assert(rel.r_extern == 1);
753 const sym = context.object.symtab.items[rel.r_symbolnum];
754 const sym_name = context.object.getString(sym.n_strx);
755
756 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
757 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
758 subtractor = where_index;
759 } else {
760 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
761 .bytes = &context.macho_file.strtab,
762 }) orelse unreachable;
763 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
764 assert(resolv.where == .global);
765 subtractor = resolv.local_sym_index;
766 }
767
768 // Verify SUBTRACTOR is followed by UNSIGNED.
769 switch (arch) {
770 .aarch64 => {
771 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
772 if (next != .ARM64_RELOC_UNSIGNED) {
773 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
774 return error.UnexpectedRelocationType;
775 }
776 },
777 .x86_64 => {
778 const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type);
779 if (next != .X86_64_RELOC_UNSIGNED) {
780 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
781 return error.UnexpectedRelocationType;
782 }
783 },
784 else => unreachable,
785 }
786 continue;
787 }
788
789 var parsed_rel = try initRelocFromObject(rel, context);
790
791 switch (arch) {
792 .aarch64 => {
793 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
794 switch (rel_type) {
795 .ARM64_RELOC_ADDEND => unreachable,
796 .ARM64_RELOC_SUBTRACTOR => unreachable,
797 .ARM64_RELOC_BRANCH26 => {
798 self.parseBranch(rel, &parsed_rel, context);
799 },
800 .ARM64_RELOC_UNSIGNED => {
801 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
802 subtractor = null;
803 },
804 .ARM64_RELOC_PAGE21,
805 .ARM64_RELOC_GOT_LOAD_PAGE21,
806 .ARM64_RELOC_TLVP_LOAD_PAGE21,
807 => {
808 self.parsePage(rel, &parsed_rel, addend);
809 if (rel_type == .ARM64_RELOC_PAGE21)
810 addend = 0;
811 },
812 .ARM64_RELOC_PAGEOFF12,
813 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
814 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
815 => {
816 self.parsePageOff(rel, &parsed_rel, addend);
817 if (rel_type == .ARM64_RELOC_PAGEOFF12)
818 addend = 0;
819 },
820 .ARM64_RELOC_POINTER_TO_GOT => {
821 self.parsePointerToGot(rel, &parsed_rel);
822 },
823 }
824 },
825 .x86_64 => {
826 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
827 .X86_64_RELOC_SUBTRACTOR => unreachable,
828 .X86_64_RELOC_BRANCH => {
829 self.parseBranch(rel, &parsed_rel, context);
830 },
831 .X86_64_RELOC_UNSIGNED => {
832 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
833 subtractor = null;
834 },
835 .X86_64_RELOC_SIGNED,
836 .X86_64_RELOC_SIGNED_1,
837 .X86_64_RELOC_SIGNED_2,
838 .X86_64_RELOC_SIGNED_4,
839 => {
840 self.parseSigned(rel, &parsed_rel, context);
841 },
842 .X86_64_RELOC_GOT_LOAD,
843 .X86_64_RELOC_GOT,
844 .X86_64_RELOC_TLV,
845 => {
846 self.parseLoad(rel, &parsed_rel);
847 },
848 }
849 },
850 else => unreachable,
851 }
852
853 try self.relocs.append(context.allocator, parsed_rel);
854
855 const is_via_got = switch (parsed_rel.payload) {
856 .pointer_to_got => true,
857 .load => |load| load.kind == .got,
858 .page => |page| page.kind == .got,
859 .page_off => |page_off| page_off.kind == .got,
860 else => false,
861 };
862
863 if (is_via_got) blk: {
864 const key = MachO.GotIndirectionKey{
865 .where = switch (parsed_rel.where) {
866 .local => .local,
867 .undef => .undef,
868 },
869 .where_index = parsed_rel.where_index,
870 };
871 if (context.macho_file.got_entries_map.contains(key)) break :blk;
872
873 const atom = try context.macho_file.createGotAtom(key);
874 try context.macho_file.got_entries_map.putNoClobber(context.macho_file.base.allocator, key, atom);
875 const match = MachO.MatchingSection{
876 .seg = context.macho_file.data_const_segment_cmd_index.?,
877 .sect = context.macho_file.got_section_index.?,
878 };
879
880 if (context.parsed_atoms.getPtr(match)) |last| {
881 last.*.next = atom;
882 atom.prev = last.*;
883 last.* = atom;
884 } else {
885 try context.parsed_atoms.putNoClobber(match, atom);
886 }
887 } else if (parsed_rel.payload == .unsigned) {
888 switch (parsed_rel.where) {
889 .undef => {
890 try self.bindings.append(context.allocator, .{
891 .local_sym_index = parsed_rel.where_index,
892 .offset = parsed_rel.offset,
893 });
894 },
895 .local => {
896 const source_sym = context.macho_file.locals.items[self.local_sym_index];
897 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
898 const seg = context.macho_file.load_commands.items[match.seg].Segment;
899 const sect = seg.sections.items[match.sect];
900 const sect_type = commands.sectionType(sect);
901
902 const should_rebase = rebase: {
903 if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false;
904
905 // TODO actually, a check similar to what dyld is doing, that is, verifying
906 // that the segment is writable should be enough here.
907 const is_right_segment = blk: {
908 if (context.macho_file.data_segment_cmd_index) |idx| {
909 if (match.seg == idx) {
910 break :blk true;
911 }
912 }
913 if (context.macho_file.data_const_segment_cmd_index) |idx| {
914 if (match.seg == idx) {
915 break :blk true;
916 }
917 }
918 break :blk false;
919 };
920
921 if (!is_right_segment) break :rebase false;
922 if (sect_type != macho.S_LITERAL_POINTERS and
923 sect_type != macho.S_REGULAR and
924 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
925 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
926 {
927 break :rebase false;
928 }
929
930 break :rebase true;
931 };
932
933 if (should_rebase) {
934 try self.rebases.append(context.allocator, parsed_rel.offset);
935 }
936 },
937 }
938 } else if (parsed_rel.payload == .branch) blk: {
939 if (parsed_rel.where != .undef) break :blk;
940 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
941
942 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
943 const laptr_atom = try context.macho_file.createLazyPointerAtom(
944 stub_helper_atom.local_sym_index,
945 parsed_rel.where_index,
946 );
947 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
948 try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stub_atom);
949 // TODO clean this up!
950 if (context.parsed_atoms.getPtr(.{
951 .seg = context.macho_file.text_segment_cmd_index.?,
952 .sect = context.macho_file.stub_helper_section_index.?,
953 })) |last| {
954 last.*.next = stub_helper_atom;
955 stub_helper_atom.prev = last.*;
956 last.* = stub_helper_atom;
957 } else {
958 try context.parsed_atoms.putNoClobber(.{
959 .seg = context.macho_file.text_segment_cmd_index.?,
960 .sect = context.macho_file.stub_helper_section_index.?,
961 }, stub_helper_atom);
962 }
963 if (context.parsed_atoms.getPtr(.{
964 .seg = context.macho_file.text_segment_cmd_index.?,
965 .sect = context.macho_file.stubs_section_index.?,
966 })) |last| {
967 last.*.next = stub_atom;
968 stub_atom.prev = last.*;
969 last.* = stub_atom;
970 } else {
971 try context.parsed_atoms.putNoClobber(.{
972 .seg = context.macho_file.text_segment_cmd_index.?,
973 .sect = context.macho_file.stubs_section_index.?,
974 }, stub_atom);
975 }
976 if (context.parsed_atoms.getPtr(.{
977 .seg = context.macho_file.data_segment_cmd_index.?,
978 .sect = context.macho_file.la_symbol_ptr_section_index.?,
979 })) |last| {
980 last.*.next = laptr_atom;
981 laptr_atom.prev = last.*;
982 last.* = laptr_atom;
983 } else {
984 try context.parsed_atoms.putNoClobber(.{
985 .seg = context.macho_file.data_segment_cmd_index.?,
986 .sect = context.macho_file.la_symbol_ptr_section_index.?,
987 }, laptr_atom);
988 }
989 }
990 }
991}
992
993fn isAddend(rel: macho.relocation_info, arch: Arch) bool {
994 if (arch != .aarch64) return false;
995 return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND;
996}
997
998fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool {
999 return switch (arch) {
1000 .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR,
1001 .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR,
1002 else => unreachable,
1003 };
1004}
1005
1006fn parseUnsigned(
1007 self: Atom,
1008 rel: macho.relocation_info,
1009 out: *Relocation,
1010 subtractor: ?u32,
1011 context: RelocContext,
1012) void {
1013 assert(rel.r_pcrel == 0);
1014
1015 const is_64bit: bool = switch (rel.r_length) {
1016 3 => true,
1017 2 => false,
1018 else => unreachable,
1019 };
1020
1021 var addend: i64 = if (is_64bit)
1022 mem.readIntLittle(i64, self.code.items[out.offset..][0..8])
1023 else
1024 mem.readIntLittle(i32, self.code.items[out.offset..][0..4]);
1025
1026 if (rel.r_extern == 0) {
1027 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
1028 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
1029 addend -= @intCast(i64, target_sect_base_addr);
1030 }
1031
1032 out.payload = .{
1033 .unsigned = .{
1034 .subtractor = subtractor,
1035 .is_64bit = is_64bit,
1036 .addend = addend,
1037 },
1038 };
1039}
1040
1041fn parseBranch(self: Atom, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
1042 _ = self;
1043 assert(rel.r_pcrel == 1);
1044 assert(rel.r_length == 2);
1045
1046 out.payload = .{
1047 .branch = .{
1048 .arch = context.macho_file.base.options.target.cpu.arch,
1049 },
1050 };
1051}
1052
1053fn parsePage(self: Atom, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
1054 _ = self;
1055 assert(rel.r_pcrel == 1);
1056 assert(rel.r_length == 2);
1057
1058 out.payload = .{
1059 .page = .{
1060 .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
1061 .ARM64_RELOC_PAGE21 => .page,
1062 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
1063 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
1064 else => unreachable,
1065 },
1066 .addend = addend,
1067 },
1068 };
1069}
1070
1071fn parsePageOff(self: Atom, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
1072 assert(rel.r_pcrel == 0);
1073 assert(rel.r_length == 2);
1074
1075 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1076 const op_kind: ?Relocation.PageOff.OpKind = blk: {
1077 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
1078 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code.items[out.offset..][0..4]))
1079 .arithmetic
1080 else
1081 .load;
1082 break :blk op_kind;
1083 };
1084
1085 out.payload = .{
1086 .page_off = .{
1087 .kind = switch (rel_type) {
1088 .ARM64_RELOC_PAGEOFF12 => .page,
1089 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
1090 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
1091 else => unreachable,
1092 },
1093 .addend = addend,
1094 .op_kind = op_kind,
1095 },
1096 };
1097}
1098
1099fn parsePointerToGot(self: Atom, rel: macho.relocation_info, out: *Relocation) void {
1100 _ = self;
1101 assert(rel.r_pcrel == 1);
1102 assert(rel.r_length == 2);
1103
1104 out.payload = .{
1105 .pointer_to_got = .{},
1106 };
1107}
1108
1109fn parseSigned(self: Atom, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
1110 assert(rel.r_pcrel == 1);
1111 assert(rel.r_length == 2);
1112
1113 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1114 const correction: u3 = switch (rel_type) {
1115 .X86_64_RELOC_SIGNED => 0,
1116 .X86_64_RELOC_SIGNED_1 => 1,
1117 .X86_64_RELOC_SIGNED_2 => 2,
1118 .X86_64_RELOC_SIGNED_4 => 4,
1119 else => unreachable,
1120 };
1121 var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;
1122
1123 if (rel.r_extern == 0) {
1124 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
1125 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
1126 addend += @intCast(i64, context.base_addr + out.offset + correction + 4) - @intCast(i64, target_sect_base_addr);
1127 }
1128
1129 out.payload = .{
1130 .signed = .{
1131 .correction = correction,
1132 .addend = addend,
1133 },
1134 };
1135}
1136
1137fn parseLoad(self: Atom, rel: macho.relocation_info, out: *Relocation) void {
1138 assert(rel.r_pcrel == 1);
1139 assert(rel.r_length == 2);
1140
1141 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1142 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)
1143 mem.readIntLittle(i32, self.code.items[out.offset..][0..4])
1144 else
1145 0;
1146
1147 out.payload = .{
1148 .load = .{
1149 .kind = switch (rel_type) {
1150 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
1151 .X86_64_RELOC_TLV => .tlvp,
1152 else => unreachable,
1153 },
1154 .addend = addend,
1155 },
1156 };
1157}
1158
1159pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1160 const tracy = trace(@src());
1161 defer tracy.end();
1162
1163 for (self.relocs.items) |rel| {
1164 log.debug("relocating {}", .{rel});
1165
1166 const source_addr = blk: {
1167 const sym = macho_file.locals.items[self.local_sym_index];
1168 break :blk sym.n_value + rel.offset;
1169 };
1170 const target_addr = blk: {
1171 const is_via_got = switch (rel.payload) {
1172 .pointer_to_got => true,
1173 .page => |page| page.kind == .got,
1174 .page_off => |page_off| page_off.kind == .got,
1175 .load => |load| load.kind == .got,
1176 else => false,
1177 };
1178
1179 if (is_via_got) {
1180 const atom = macho_file.got_entries_map.get(.{
1181 .where = switch (rel.where) {
1182 .local => .local,
1183 .undef => .undef,
1184 },
1185 .where_index = rel.where_index,
1186 }) orelse {
1187 const sym = switch (rel.where) {
1188 .local => macho_file.locals.items[rel.where_index],
1189 .undef => macho_file.undefs.items[rel.where_index],
1190 };
1191 log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)});
1192 log.err(" this is an internal linker error", .{});
1193 return error.FailedToResolveRelocationTarget;
1194 };
1195 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
1196 }
1197
1198 switch (rel.where) {
1199 .local => {
1200 const sym = macho_file.locals.items[rel.where_index];
1201 const is_tlv = is_tlv: {
1202 const source_sym = macho_file.locals.items[self.local_sym_index];
1203 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
1204 const seg = macho_file.load_commands.items[match.seg].Segment;
1205 const sect = seg.sections.items[match.sect];
1206 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
1207 };
1208 if (is_tlv) {
1209 // For TLV relocations, the value specified as a relocation is the displacement from the
1210 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
1211 // defined TLV template init section in the following order:
1212 // * wrt to __thread_data if defined, then
1213 // * wrt to __thread_bss
1214 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment;
1215 const base_address = inner: {
1216 if (macho_file.tlv_data_section_index) |i| {
1217 break :inner seg.sections.items[i].addr;
1218 } else if (macho_file.tlv_bss_section_index) |i| {
1219 break :inner seg.sections.items[i].addr;
1220 } else {
1221 log.err("threadlocal variables present but no initializer sections found", .{});
1222 log.err(" __thread_data not found", .{});
1223 log.err(" __thread_bss not found", .{});
1224 return error.FailedToResolveRelocationTarget;
1225 }
1226 };
1227 break :blk sym.n_value - base_address;
1228 }
1229
1230 break :blk sym.n_value;
1231 },
1232 .undef => {
1233 const atom = macho_file.stubs_map.get(rel.where_index) orelse {
1234 // TODO this is required for incremental when we don't have every symbol
1235 // resolved when creating relocations. In this case, we will insert a branch
1236 // reloc to an undef symbol which may happen to be defined within the binary.
1237 // Then, the undef we point at will be a null symbol (free symbol) which we
1238 // should remove/repurpose. To circumvent this (for now), we check if the symbol
1239 // we point to is garbage, and if so we fall back to symbol resolver to find by name.
1240 const n_strx = macho_file.undefs.items[rel.where_index].n_strx;
1241 if (macho_file.symbol_resolver.get(n_strx)) |resolv| inner: {
1242 if (resolv.where != .global) break :inner;
1243 break :blk macho_file.globals.items[resolv.where_index].n_value;
1244 }
1245
1246 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
1247 break :blk 0; // Dynamically bound by dyld.
1248 };
1249
1250 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
1251 },
1252 }
1253 };
1254
1255 log.debug(" | source_addr = 0x{x}", .{source_addr});
1256 log.debug(" | target_addr = 0x{x}", .{target_addr});
1257
1258 try rel.resolve(.{
1259 .block = self,
1260 .offset = rel.offset,
1261 .source_addr = source_addr,
1262 .target_addr = target_addr,
1263 .macho_file = macho_file,
1264 });
1265 }
1266}
1267
1268pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
1269 _ = fmt;
1270 _ = options;
1271 try std.fmt.format(writer, "TextBlock {{ ", .{});
1272 try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
1273 try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items});
1274 try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items});
1275 try std.fmt.format(writer, ".code = {*}, ", .{self.code.items});
1276 try std.fmt.format(writer, ".size = {d}, ", .{self.size});
1277 try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment});
1278 try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items});
1279 try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items});
1280 try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items});
1281 try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items});
1282 if (self.stab) |stab| {
1283 try std.fmt.format(writer, ".stab = {any}, ", .{stab});
1284 }
1285 try std.fmt.format(writer, "}}", .{});
1286}
1287
1288const RelocIterator = struct {
1289 buffer: []const macho.relocation_info,
1290 index: i32 = -1,
1291
1292 pub fn next(self: *RelocIterator) ?macho.relocation_info {
1293 self.index += 1;
1294 if (self.index < self.buffer.len) {
1295 return self.buffer[@intCast(u32, self.index)];
1296 }
1297 return null;
1298 }
1299
1300 pub fn peek(self: RelocIterator) macho.relocation_info {
1301 assert(self.index + 1 < self.buffer.len);
1302 return self.buffer[@intCast(u32, self.index + 1)];
1303 }
1304};
1305
1306fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info {
1307 const Predicate = struct {
1308 addr: u64,
1309
1310 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1311 return rel.r_address < self.addr;
1312 }
1313 };
1314
1315 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
1316 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
1317
1318 return relocs[start..end];
1319}
1320
1321inline fn isArithmeticOp(inst: *const [4]u8) bool {
1322 const group_decode = @truncate(u5, inst[3]);
1323 return ((group_decode >> 2) == 4);
1324}
src/link/MachO/DebugSymbols.zig+77-95
...@@ -5,25 +5,25 @@ const assert = std.debug.assert;...@@ -5,25 +5,25 @@ const assert = std.debug.assert;
5const fs = std.fs;5const fs = std.fs;
6const log = std.log.scoped(.dsym);6const log = std.log.scoped(.dsym);
7const macho = std.macho;7const macho = std.macho;
8const math = std.math;
8const mem = std.mem;9const mem = std.mem;
9const DW = std.dwarf;10const DW = std.dwarf;
10const leb = std.leb;11const leb = std.leb;
11const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
1213
13const build_options = @import("build_options");14const build_options = @import("build_options");
15const commands = @import("commands.zig");
14const trace = @import("../../tracy.zig").trace;16const trace = @import("../../tracy.zig").trace;
17const LoadCommand = commands.LoadCommand;
15const Module = @import("../../Module.zig");18const Module = @import("../../Module.zig");
16const Type = @import("../../type.zig").Type;19const Type = @import("../../type.zig").Type;
17const link = @import("../../link.zig");20const link = @import("../../link.zig");
18const MachO = @import("../MachO.zig");21const MachO = @import("../MachO.zig");
19const SrcFn = MachO.SrcFn;
20const TextBlock = MachO.TextBlock;22const TextBlock = MachO.TextBlock;
21const padToIdeal = MachO.padToIdeal;
22
23const commands = @import("commands.zig");
24const emptyHeader = commands.emptyHeader;
25const LoadCommand = commands.LoadCommand;
26const SegmentCommand = commands.SegmentCommand;23const SegmentCommand = commands.SegmentCommand;
24const SrcFn = MachO.SrcFn;
25const makeStaticString = MachO.makeStaticString;
26const padToIdeal = MachO.padToIdeal;
2727
28const page_size: u16 = 0x1000;28const page_size: u16 = 0x1000;
2929
...@@ -188,105 +188,84 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void...@@ -188,105 +188,84 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
188 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });188 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
189189
190 try self.load_commands.append(allocator, .{190 try self.load_commands.append(allocator, .{
191 .Segment = SegmentCommand.empty("__DWARF", .{191 .Segment = .{
192 .vmaddr = vmaddr,192 .inner = .{
193 .vmsize = needed_size,193 .segname = makeStaticString("__DWARF"),
194 .fileoff = off,194 .vmaddr = vmaddr,
195 .filesize = needed_size,195 .vmsize = needed_size,
196 }),196 .fileoff = off,
197 .filesize = needed_size,
198 },
199 },
197 });200 });
198 self.load_commands_dirty = true;201 self.load_commands_dirty = true;
199 }202 }
200 if (self.debug_str_section_index == null) {203 if (self.debug_str_section_index == null) {
201 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
202 self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
203 assert(self.debug_string_table.items.len == 0);204 assert(self.debug_string_table.items.len == 0);
204205 self.debug_str_section_index = try self.allocateSection(
205 try dwarf_segment.addSection(allocator, "__debug_str", .{206 "__debug_str",
206 .addr = dwarf_segment.inner.vmaddr,207 @intCast(u32, self.debug_string_table.items.len),
207 .size = @intCast(u32, self.debug_string_table.items.len),208 0,
208 .offset = @intCast(u32, dwarf_segment.inner.fileoff),209 );
209 .@"align" = 1,
210 });
211 self.load_commands_dirty = true;
212 self.debug_string_table_dirty = true;210 self.debug_string_table_dirty = true;
213 }211 }
214 if (self.debug_info_section_index == null) {212 if (self.debug_info_section_index == null) {
215 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;213 self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0);
216 self.debug_info_section_index = @intCast(u16, dwarf_segment.sections.items.len);
217
218 const file_size_hint = 200;
219 const p_align = 1;
220 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
221
222 log.debug("found __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
223
224 try dwarf_segment.addSection(allocator, "__debug_info", .{
225 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
226 .size = file_size_hint,
227 .offset = @intCast(u32, off),
228 .@"align" = p_align,
229 });
230 self.load_commands_dirty = true;
231 self.debug_info_header_dirty = true;214 self.debug_info_header_dirty = true;
232 }215 }
233 if (self.debug_abbrev_section_index == null) {216 if (self.debug_abbrev_section_index == null) {
234 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;217 self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0);
235 self.debug_abbrev_section_index = @intCast(u16, dwarf_segment.sections.items.len);
236
237 const file_size_hint = 128;
238 const p_align = 1;
239 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
240
241 log.debug("found __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
242
243 try dwarf_segment.addSection(allocator, "__debug_abbrev", .{
244 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
245 .size = file_size_hint,
246 .offset = @intCast(u32, off),
247 .@"align" = p_align,
248 });
249 self.load_commands_dirty = true;
250 self.debug_abbrev_section_dirty = true;218 self.debug_abbrev_section_dirty = true;
251 }219 }
252 if (self.debug_aranges_section_index == null) {220 if (self.debug_aranges_section_index == null) {
253 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;221 self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4);
254 self.debug_aranges_section_index = @intCast(u16, dwarf_segment.sections.items.len);
255
256 const file_size_hint = 160;
257 const p_align = 16;
258 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
259
260 log.debug("found __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
261
262 try dwarf_segment.addSection(allocator, "__debug_aranges", .{
263 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
264 .size = file_size_hint,
265 .offset = @intCast(u32, off),
266 .@"align" = p_align,
267 });
268 self.load_commands_dirty = true;
269 self.debug_aranges_section_dirty = true;222 self.debug_aranges_section_dirty = true;
270 }223 }
271 if (self.debug_line_section_index == null) {224 if (self.debug_line_section_index == null) {
272 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;225 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
273 self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len);226 self.debug_line_header_dirty = true;
227 }
228}
274229
275 const file_size_hint = 250;230fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
276 const p_align = 1;231 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
277 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);232 var sect = macho.section_64{
233 .sectname = makeStaticString(sectname),
234 .segname = seg.inner.segname,
235 .size = @intCast(u32, size),
236 .@"align" = alignment,
237 };
238 const alignment_pow_2 = try math.powi(u32, 2, alignment);
239 const off = seg.findFreeSpace(size, alignment_pow_2, null);
278240
279 log.debug("found __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });241 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
280242
281 try dwarf_segment.addSection(allocator, "__debug_line", .{243 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
282 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,244 commands.segmentName(sect),
283 .size = file_size_hint,245 commands.sectionName(sect),
284 .offset = @intCast(u32, off),246 off,
285 .@"align" = p_align,247 off + size,
286 });248 });
287 self.load_commands_dirty = true;249
288 self.debug_line_header_dirty = true;250 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
289 }251 sect.offset = @intCast(u32, off);
252
253 const index = @intCast(u16, seg.sections.items.len);
254 try seg.sections.append(self.base.base.allocator, sect);
255 seg.inner.cmdsize += @sizeOf(macho.section_64);
256 seg.inner.nsects += 1;
257
258 // TODO
259 // const match = MatchingSection{
260 // .seg = segment_id,
261 // .sect = index,
262 // };
263 // _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
264 // try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
265
266 self.load_commands_dirty = true;
267
268 return index;
290}269}
291270
292pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {271pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
...@@ -614,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {...@@ -614,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
614}593}
615594
616fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {595fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
617 var cmd = SegmentCommand.empty("", .{596 var cmd = SegmentCommand{
618 .cmdsize = base_cmd.inner.cmdsize,597 .inner = .{
619 .vmaddr = base_cmd.inner.vmaddr,598 .segname = undefined,
620 .vmsize = base_cmd.inner.vmsize,599 .cmdsize = base_cmd.inner.cmdsize,
621 .maxprot = base_cmd.inner.maxprot,600 .vmaddr = base_cmd.inner.vmaddr,
622 .initprot = base_cmd.inner.initprot,601 .vmsize = base_cmd.inner.vmsize,
623 .nsects = base_cmd.inner.nsects,602 .maxprot = base_cmd.inner.maxprot,
624 .flags = base_cmd.inner.flags,603 .initprot = base_cmd.inner.initprot,
625 });604 .nsects = base_cmd.inner.nsects,
605 .flags = base_cmd.inner.flags,
606 },
607 };
626 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);608 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
627609
628 try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);610 try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);
...@@ -692,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {...@@ -692,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
692}674}
693675
694fn writeHeader(self: *DebugSymbols) !void {676fn writeHeader(self: *DebugSymbols) !void {
695 var header = emptyHeader(.{677 var header = commands.emptyHeader(.{
696 .filetype = macho.MH_DSYM,678 .filetype = macho.MH_DSYM,
697 });679 });
698680
src/link/MachO/Object.zig+130-140
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Object = @This();1const Object = @This();
22
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");
4const assert = std.debug.assert;5const assert = std.debug.assert;
5const dwarf = std.dwarf;6const dwarf = std.dwarf;
6const fs = std.fs;7const fs = std.fs;
...@@ -13,11 +14,12 @@ const sort = std.sort;...@@ -13,11 +14,12 @@ const sort = std.sort;
13const commands = @import("commands.zig");14const commands = @import("commands.zig");
14const segmentName = commands.segmentName;15const segmentName = commands.segmentName;
15const sectionName = commands.sectionName;16const sectionName = commands.sectionName;
17const trace = @import("../../tracy.zig").trace;
1618
17const Allocator = mem.Allocator;19const Allocator = mem.Allocator;
20const Atom = @import("Atom.zig");
18const LoadCommand = commands.LoadCommand;21const LoadCommand = commands.LoadCommand;
19const MachO = @import("../MachO.zig");22const MachO = @import("../MachO.zig");
20const TextBlock = @import("TextBlock.zig");
2123
22file: fs.File,24file: fs.File,
23name: []const u8,25name: []const u8,
...@@ -54,7 +56,7 @@ tu_name: ?[]const u8 = null,...@@ -54,7 +56,7 @@ tu_name: ?[]const u8 = null,
54tu_comp_dir: ?[]const u8 = null,56tu_comp_dir: ?[]const u8 = null,
55mtime: ?u64 = null,57mtime: ?u64 = null,
5658
57text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},59atoms: std.ArrayListUnmanaged(*Atom) = .{},
58sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},60sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5961
60// TODO symbol mapping and its inverse can probably be simple arrays62// TODO symbol mapping and its inverse can probably be simple arrays
...@@ -62,6 +64,8 @@ sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},...@@ -62,6 +64,8 @@ sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
62symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},64symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
63reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},65reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
6466
67analyzed: bool = false,
68
65const DebugInfo = struct {69const DebugInfo = struct {
66 inner: dwarf.DwarfInfo,70 inner: dwarf.DwarfInfo,
67 debug_info: []u8,71 debug_info: []u8,
...@@ -134,7 +138,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {...@@ -134,7 +138,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
134 self.data_in_code_entries.deinit(allocator);138 self.data_in_code_entries.deinit(allocator);
135 self.symtab.deinit(allocator);139 self.symtab.deinit(allocator);
136 self.strtab.deinit(allocator);140 self.strtab.deinit(allocator);
137 self.text_blocks.deinit(allocator);141 self.atoms.deinit(allocator);
138 self.sections_as_symbols.deinit(allocator);142 self.sections_as_symbols.deinit(allocator);
139 self.symbol_mapping.deinit(allocator);143 self.symbol_mapping.deinit(allocator);
140 self.reverse_symbol_mapping.deinit(allocator);144 self.reverse_symbol_mapping.deinit(allocator);
...@@ -316,16 +320,17 @@ const Context = struct {...@@ -316,16 +320,17 @@ const Context = struct {
316 object: *Object,320 object: *Object,
317 macho_file: *MachO,321 macho_file: *MachO,
318 match: MachO.MatchingSection,322 match: MachO.MatchingSection,
323 parsed_atoms: *ParsedAtoms,
319};324};
320325
321const TextBlockParser = struct {326const AtomParser = struct {
322 section: macho.section_64,327 section: macho.section_64,
323 code: []u8,328 code: []u8,
324 relocs: []macho.relocation_info,329 relocs: []macho.relocation_info,
325 nlists: []NlistWithIndex,330 nlists: []NlistWithIndex,
326 index: u32 = 0,331 index: u32 = 0,
327332
328 fn peek(self: TextBlockParser) ?NlistWithIndex {333 fn peek(self: AtomParser) ?NlistWithIndex {
329 return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;334 return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
330 }335 }
331336
...@@ -339,9 +344,12 @@ const TextBlockParser = struct {...@@ -339,9 +344,12 @@ const TextBlockParser = struct {
339 }344 }
340 }345 }
341346
342 pub fn next(self: *TextBlockParser, context: Context) !?*TextBlock {347 pub fn next(self: *AtomParser, context: Context) !?*Atom {
343 if (self.index == self.nlists.len) return null;348 if (self.index == self.nlists.len) return null;
344349
350 const tracy = trace(@src());
351 defer tracy.end();
352
345 var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);353 var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);
346 defer aliases.deinit();354 defer aliases.deinit();
347355
...@@ -364,12 +372,12 @@ const TextBlockParser = struct {...@@ -364,12 +372,12 @@ const TextBlockParser = struct {
364 }372 }
365373
366 if (aliases.items.len > 1) {374 if (aliases.items.len > 1) {
367 // Bubble-up senior symbol as the main link to the text block.375 // Bubble-up senior symbol as the main link to the atom.
368 sort.sort(376 sort.sort(
369 NlistWithIndex,377 NlistWithIndex,
370 aliases.items,378 aliases.items,
371 context,379 context,
372 TextBlockParser.lessThanBySeniority,380 AtomParser.lessThanBySeniority,
373 );381 );
374 }382 }
375383
...@@ -389,12 +397,12 @@ const TextBlockParser = struct {...@@ -389,12 +397,12 @@ const TextBlockParser = struct {
389 else397 else
390 max_align;398 max_align;
391399
392 const stab: ?TextBlock.Stab = if (context.object.debug_info) |di| blk: {400 const stab: ?Atom.Stab = if (context.object.debug_info) |di| blk: {
393 // TODO there has to be a better to handle this.401 // TODO there has to be a better to handle this.
394 for (di.inner.func_list.items) |func| {402 for (di.inner.func_list.items) |func| {
395 if (func.pc_range) |range| {403 if (func.pc_range) |range| {
396 if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) {404 if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) {
397 break :blk TextBlock.Stab{405 break :blk Atom.Stab{
398 .function = range.end - range.start,406 .function = range.end - range.start,
399 };407 };
400 }408 }
...@@ -405,28 +413,31 @@ const TextBlockParser = struct {...@@ -405,28 +413,31 @@ const TextBlockParser = struct {
405 break :blk .static;413 break :blk .static;
406 } else null;414 } else null;
407415
408 const block = try context.allocator.create(TextBlock);416 const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align);
409 block.* = TextBlock.empty;417 atom.stab = stab;
410 block.local_sym_index = senior_nlist.index;
411 block.stab = stab;
412 block.size = size;
413 block.alignment = actual_align;
414 try context.macho_file.managed_blocks.append(context.allocator, block);
415418
416 try block.code.appendSlice(context.allocator, code);419 const is_zerofill = blk: {
420 const section_type = commands.sectionType(self.section);
421 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
422 };
423 if (!is_zerofill) {
424 mem.copy(u8, atom.code.items, code);
425 }
417426
418 try block.aliases.ensureTotalCapacity(context.allocator, aliases.items.len);427 try atom.aliases.ensureTotalCapacity(context.allocator, aliases.items.len);
419 for (aliases.items) |alias| {428 for (aliases.items) |alias| {
420 block.aliases.appendAssumeCapacity(alias.index);429 atom.aliases.appendAssumeCapacity(alias.index);
421 const sym = &context.macho_file.locals.items[alias.index];430 const sym = &context.macho_file.locals.items[alias.index];
422 sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);431 sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
423 }432 }
424433
425 try block.parseRelocs(self.relocs, .{434 try atom.parseRelocs(self.relocs, .{
426 .base_addr = start_addr,435 .base_addr = self.section.addr,
436 .base_offset = start_addr,
427 .allocator = context.allocator,437 .allocator = context.allocator,
428 .object = context.object,438 .object = context.object,
429 .macho_file = context.macho_file,439 .macho_file = context.macho_file,
440 .parsed_atoms = context.parsed_atoms,
430 });441 });
431442
432 if (context.macho_file.has_dices) {443 if (context.macho_file.has_dices) {
...@@ -435,10 +446,10 @@ const TextBlockParser = struct {...@@ -435,10 +446,10 @@ const TextBlockParser = struct {
435 senior_nlist.nlist.n_value,446 senior_nlist.nlist.n_value,
436 senior_nlist.nlist.n_value + size,447 senior_nlist.nlist.n_value + size,
437 );448 );
438 try block.dices.ensureTotalCapacity(context.allocator, dices.len);449 try atom.dices.ensureTotalCapacity(context.allocator, dices.len);
439450
440 for (dices) |dice| {451 for (dices) |dice| {
441 block.dices.appendAssumeCapacity(.{452 atom.dices.appendAssumeCapacity(.{
442 .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),453 .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),
443 .length = dice.length,454 .length = dice.length,
444 .kind = dice.kind,455 .kind = dice.kind,
...@@ -448,16 +459,22 @@ const TextBlockParser = struct {...@@ -448,16 +459,22 @@ const TextBlockParser = struct {
448459
449 self.index += 1;460 self.index += 1;
450461
451 return block;462 return atom;
452 }463 }
453};464};
454465
455pub fn parseTextBlocks(466pub const ParsedAtoms = std.AutoHashMap(MachO.MatchingSection, *Atom);
467
468pub fn parseIntoAtoms(
456 self: *Object,469 self: *Object,
457 allocator: *Allocator,470 allocator: *Allocator,
458 object_id: u16,471 object_id: u16,
459 macho_file: *MachO,472 macho_file: *MachO,
460) !void {473) !ParsedAtoms {
474 const tracy = trace(@src());
475 defer tracy.end();
476
477 var parsed_atoms = ParsedAtoms.init(allocator);
461 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;478 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
462479
463 log.debug("analysing {s}", .{self.name});480 log.debug("analysing {s}", .{self.name});
...@@ -498,7 +515,7 @@ pub fn parseTextBlocks(...@@ -498,7 +515,7 @@ pub fn parseTextBlocks(
498515
499 for (seg.sections.items) |sect, id| {516 for (seg.sections.items) |sect, id| {
500 const sect_id = @intCast(u8, id);517 const sect_id = @intCast(u8, id);
501 log.debug("putting section '{s},{s}' as a TextBlock", .{518 log.debug("putting section '{s},{s}' as an Atom", .{
502 segmentName(sect),519 segmentName(sect),
503 sectionName(sect),520 sectionName(sect),
504 });521 });
...@@ -523,14 +540,17 @@ pub fn parseTextBlocks(...@@ -523,14 +540,17 @@ pub fn parseTextBlocks(
523 // Symbols within this section only.540 // Symbols within this section only.
524 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);541 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);
525542
543 // TODO rewrite and re-enable dead-code stripping optimisation. I think it might make sense
544 // to do this in a standalone pass after we parse the sections as atoms.
526 // In release mode, if the object file was generated with dead code stripping optimisations,545 // In release mode, if the object file was generated with dead code stripping optimisations,
527 // note it now and parse sections as atoms.546 // note it now and parse sections as atoms.
528 const is_splittable = blk: {547 // const is_splittable = blk: {
529 if (macho_file.base.options.optimize_mode == .Debug) break :blk false;548 // if (macho_file.base.options.optimize_mode == .Debug) break :blk false;
530 break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;549 // break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
531 };550 // };
551 const is_splittable = false;
532552
533 macho_file.has_dices = blk: {553 macho_file.has_dices = macho_file.has_dices or blk: {
534 if (self.text_section_index) |index| {554 if (self.text_section_index) |index| {
535 if (index != id) break :blk false;555 if (index != id) break :blk false;
536 if (self.data_in_code_entries.items.len == 0) break :blk false;556 if (self.data_in_code_entries.items.len == 0) break :blk false;
...@@ -541,12 +561,12 @@ pub fn parseTextBlocks(...@@ -541,12 +561,12 @@ pub fn parseTextBlocks(
541 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;561 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
542562
543 next: {563 next: {
544 if (is_splittable) blocks: {564 if (is_splittable) atoms: {
545 if (filtered_nlists.len == 0) break :blocks;565 if (filtered_nlists.len == 0) break :atoms;
546566
547 // If the first nlist does not match the start of the section,567 // If the first nlist does not match the start of the section,
548 // then we need to encapsulate the memory range [section start, first symbol)568 // then we need to encapsulate the memory range [section start, first symbol)
549 // as a temporary symbol and insert the matching TextBlock.569 // as a temporary symbol and insert the matching Atom.
550 const first_nlist = filtered_nlists[0].nlist;570 const first_nlist = filtered_nlists[0].nlist;
551 if (first_nlist.n_value > sect.addr) {571 if (first_nlist.n_value > sect.addr) {
552 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{572 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
...@@ -556,44 +576,45 @@ pub fn parseTextBlocks(...@@ -556,44 +576,45 @@ pub fn parseTextBlocks(
556 });576 });
557 defer allocator.free(sym_name);577 defer allocator.free(sym_name);
558578
559 const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {579 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
560 const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);580 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
561 try macho_file.locals.append(allocator, .{581 try macho_file.locals.append(allocator, .{
562 .n_strx = try macho_file.makeString(sym_name),582 .n_strx = try macho_file.makeString(sym_name),
563 .n_type = macho.N_SECT,583 .n_type = macho.N_SECT,
564 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),584 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
565 .n_desc = 0,585 .n_desc = 0,
566 .n_value = sect.addr,586 .n_value = 0,
567 });587 });
568 try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);588 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
569 break :blk block_local_sym_index;589 break :blk atom_local_sym_index;
570 };590 };
591 const atom_code = code[0 .. first_nlist.n_value - sect.addr];
592 const atom_size = atom_code.len;
593 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, atom_size, sect.@"align");
571594
572 const block_code = code[0 .. first_nlist.n_value - sect.addr];595 const is_zerofill = blk: {
573 const block_size = block_code.len;596 const section_type = commands.sectionType(sect);
574597 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
575 const block = try allocator.create(TextBlock);598 };
576 block.* = TextBlock.empty;599 if (!is_zerofill) {
577 block.local_sym_index = block_local_sym_index;600 mem.copy(u8, atom.code.items, atom_code);
578 block.size = block_size;601 }
579 block.alignment = sect.@"align";
580 try macho_file.managed_blocks.append(allocator, block);
581
582 try block.code.appendSlice(allocator, block_code);
583602
584 try block.parseRelocs(relocs, .{603 try atom.parseRelocs(relocs, .{
585 .base_addr = 0,604 .base_addr = sect.addr,
605 .base_offset = 0,
586 .allocator = allocator,606 .allocator = allocator,
587 .object = self,607 .object = self,
588 .macho_file = macho_file,608 .macho_file = macho_file,
609 .parsed_atoms = &parsed_atoms,
589 });610 });
590611
591 if (macho_file.has_dices) {612 if (macho_file.has_dices) {
592 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);613 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + atom_size);
593 try block.dices.ensureTotalCapacity(allocator, dices.len);614 try atom.dices.ensureTotalCapacity(allocator, dices.len);
594615
595 for (dices) |dice| {616 for (dices) |dice| {
596 block.dices.appendAssumeCapacity(.{617 atom.dices.appendAssumeCapacity(.{
597 .offset = dice.offset - try math.cast(u32, sect.addr),618 .offset = dice.offset - try math.cast(u32, sect.addr),
598 .length = dice.length,619 .length = dice.length,
599 .kind = dice.kind,620 .kind = dice.kind,
...@@ -601,29 +622,17 @@ pub fn parseTextBlocks(...@@ -601,29 +622,17 @@ pub fn parseTextBlocks(
601 }622 }
602 }623 }
603624
604 // Update target section's metadata625 if (parsed_atoms.getPtr(match)) |last| {
605 // TODO should we update segment's size here too?626 last.*.next = atom;
606 // How does it tie with incremental space allocs?627 atom.prev = last.*;
607 const tseg = &macho_file.load_commands.items[match.seg].Segment;628 last.* = atom;
608 const tsect = &tseg.sections.items[match.sect];
609 const new_alignment = math.max(tsect.@"align", block.alignment);
610 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
611 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
612 tsect.size = new_size;
613 tsect.@"align" = new_alignment;
614
615 if (macho_file.blocks.getPtr(match)) |last| {
616 last.*.next = block;
617 block.prev = last.*;
618 last.* = block;
619 } else {629 } else {
620 try macho_file.blocks.putNoClobber(allocator, match, block);630 try parsed_atoms.putNoClobber(match, atom);
621 }631 }
622632 try self.atoms.append(allocator, atom);
623 try self.text_blocks.append(allocator, block);
624 }633 }
625634
626 var parser = TextBlockParser{635 var parser = AtomParser{
627 .section = sect,636 .section = sect,
628 .code = code,637 .code = code,
629 .relocs = relocs,638 .relocs = relocs,
...@@ -635,10 +644,11 @@ pub fn parseTextBlocks(...@@ -635,10 +644,11 @@ pub fn parseTextBlocks(
635 .object = self,644 .object = self,
636 .macho_file = macho_file,645 .macho_file = macho_file,
637 .match = match,646 .match = match,
638 })) |block| {647 .parsed_atoms = &parsed_atoms,
639 const sym = macho_file.locals.items[block.local_sym_index];648 })) |atom| {
649 const sym = macho_file.locals.items[atom.local_sym_index];
640 const is_ext = blk: {650 const is_ext = blk: {
641 const orig_sym_id = self.reverse_symbol_mapping.get(block.local_sym_index) orelse unreachable;651 const orig_sym_id = self.reverse_symbol_mapping.get(atom.local_sym_index) orelse unreachable;
642 break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]);652 break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]);
643 };653 };
644 if (is_ext) {654 if (is_ext) {
...@@ -662,38 +672,26 @@ pub fn parseTextBlocks(...@@ -662,38 +672,26 @@ pub fn parseTextBlocks(
662 // In x86_64 relocs, it can so happen that the compiler refers to the same672 // In x86_64 relocs, it can so happen that the compiler refers to the same
663 // atom by both the actual assigned symbol and the start of the section. In this673 // atom by both the actual assigned symbol and the start of the section. In this
664 // case, we need to link the two together so add an alias.674 // case, we need to link the two together so add an alias.
665 try block.aliases.append(allocator, alias);675 try atom.aliases.append(allocator, alias);
666 }676 }
667 }677 }
668678
669 // Update target section's metadata679 if (parsed_atoms.getPtr(match)) |last| {
670 // TODO should we update segment's size here too?680 last.*.next = atom;
671 // How does it tie with incremental space allocs?681 atom.prev = last.*;
672 const tseg = &macho_file.load_commands.items[match.seg].Segment;682 last.* = atom;
673 const tsect = &tseg.sections.items[match.sect];
674 const new_alignment = math.max(tsect.@"align", block.alignment);
675 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
676 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
677 tsect.size = new_size;
678 tsect.@"align" = new_alignment;
679
680 if (macho_file.blocks.getPtr(match)) |last| {
681 last.*.next = block;
682 block.prev = last.*;
683 last.* = block;
684 } else {683 } else {
685 try macho_file.blocks.putNoClobber(allocator, match, block);684 try parsed_atoms.putNoClobber(match, atom);
686 }685 }
687686 try self.atoms.append(allocator, atom);
688 try self.text_blocks.append(allocator, block);
689 }687 }
690688
691 break :next;689 break :next;
692 }690 }
693691
694 // Since there is no symbol to refer to this block, we create692 // Since there is no symbol to refer to this atom, we create
695 // a temp one, unless we already did that when working out the relocations693 // a temp one, unless we already did that when working out the relocations
696 // of other text blocks.694 // of other atoms.
697 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{695 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
698 self.name,696 self.name,
699 segmentName(sect),697 segmentName(sect),
...@@ -701,41 +699,43 @@ pub fn parseTextBlocks(...@@ -701,41 +699,43 @@ pub fn parseTextBlocks(
701 });699 });
702 defer allocator.free(sym_name);700 defer allocator.free(sym_name);
703701
704 const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {702 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
705 const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);703 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
706 try macho_file.locals.append(allocator, .{704 try macho_file.locals.append(allocator, .{
707 .n_strx = try macho_file.makeString(sym_name),705 .n_strx = try macho_file.makeString(sym_name),
708 .n_type = macho.N_SECT,706 .n_type = macho.N_SECT,
709 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),707 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
710 .n_desc = 0,708 .n_desc = 0,
711 .n_value = sect.addr,709 .n_value = 0,
712 });710 });
713 try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);711 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
714 break :blk block_local_sym_index;712 break :blk atom_local_sym_index;
715 };713 };
714 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align");
716715
717 const block = try allocator.create(TextBlock);716 const is_zerofill = blk: {
718 block.* = TextBlock.empty;717 const section_type = commands.sectionType(sect);
719 block.local_sym_index = block_local_sym_index;718 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
720 block.size = sect.size;719 };
721 block.alignment = sect.@"align";720 if (!is_zerofill) {
722 try macho_file.managed_blocks.append(allocator, block);721 mem.copy(u8, atom.code.items, code);
723722 }
724 try block.code.appendSlice(allocator, code);
725723
726 try block.parseRelocs(relocs, .{724 try atom.parseRelocs(relocs, .{
727 .base_addr = 0,725 .base_addr = sect.addr,
726 .base_offset = 0,
728 .allocator = allocator,727 .allocator = allocator,
729 .object = self,728 .object = self,
730 .macho_file = macho_file,729 .macho_file = macho_file,
730 .parsed_atoms = &parsed_atoms,
731 });731 });
732732
733 if (macho_file.has_dices) {733 if (macho_file.has_dices) {
734 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);734 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
735 try block.dices.ensureTotalCapacity(allocator, dices.len);735 try atom.dices.ensureTotalCapacity(allocator, dices.len);
736736
737 for (dices) |dice| {737 for (dices) |dice| {
738 block.dices.appendAssumeCapacity(.{738 atom.dices.appendAssumeCapacity(.{
739 .offset = dice.offset - try math.cast(u32, sect.addr),739 .offset = dice.offset - try math.cast(u32, sect.addr),
740 .length = dice.length,740 .length = dice.length,
741 .kind = dice.kind,741 .kind = dice.kind,
...@@ -743,12 +743,12 @@ pub fn parseTextBlocks(...@@ -743,12 +743,12 @@ pub fn parseTextBlocks(
743 }743 }
744 }744 }
745745
746 // Since this is block gets a helper local temporary symbol that didn't exist746 // Since this is atom gets a helper local temporary symbol that didn't exist
747 // in the object file which encompasses the entire section, we need traverse747 // in the object file which encompasses the entire section, we need traverse
748 // the filtered symbols and note which symbol is contained within so that748 // the filtered symbols and note which symbol is contained within so that
749 // we can properly allocate addresses down the line.749 // we can properly allocate addresses down the line.
750 // While we're at it, we need to update segment,section mapping of each symbol too.750 // While we're at it, we need to update segment,section mapping of each symbol too.
751 try block.contained.ensureTotalCapacity(allocator, filtered_nlists.len);751 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
752752
753 for (filtered_nlists) |nlist_with_index| {753 for (filtered_nlists) |nlist_with_index| {
754 const nlist = nlist_with_index.nlist;754 const nlist = nlist_with_index.nlist;
...@@ -756,12 +756,12 @@ pub fn parseTextBlocks(...@@ -756,12 +756,12 @@ pub fn parseTextBlocks(
756 const local = &macho_file.locals.items[local_sym_index];756 const local = &macho_file.locals.items[local_sym_index];
757 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);757 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
758758
759 const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: {759 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
760 // TODO there has to be a better to handle this.760 // TODO there has to be a better to handle this.
761 for (di.inner.func_list.items) |func| {761 for (di.inner.func_list.items) |func| {
762 if (func.pc_range) |range| {762 if (func.pc_range) |range| {
763 if (nlist.n_value >= range.start and nlist.n_value < range.end) {763 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
764 break :blk TextBlock.Stab{764 break :blk Atom.Stab{
765 .function = range.end - range.start,765 .function = range.end - range.start,
766 };766 };
767 }767 }
...@@ -772,35 +772,25 @@ pub fn parseTextBlocks(...@@ -772,35 +772,25 @@ pub fn parseTextBlocks(
772 break :blk .static;772 break :blk .static;
773 } else null;773 } else null;
774774
775 block.contained.appendAssumeCapacity(.{775 atom.contained.appendAssumeCapacity(.{
776 .local_sym_index = local_sym_index,776 .local_sym_index = local_sym_index,
777 .offset = nlist.n_value - sect.addr,777 .offset = nlist.n_value - sect.addr,
778 .stab = stab,778 .stab = stab,
779 });779 });
780 }780 }
781781
782 // Update target section's metadata782 if (parsed_atoms.getPtr(match)) |last| {
783 // TODO should we update segment's size here too?783 last.*.next = atom;
784 // How does it tie with incremental space allocs?784 atom.prev = last.*;
785 const tseg = &macho_file.load_commands.items[match.seg].Segment;785 last.* = atom;
786 const tsect = &tseg.sections.items[match.sect];
787 const new_alignment = math.max(tsect.@"align", block.alignment);
788 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
789 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
790 tsect.size = new_size;
791 tsect.@"align" = new_alignment;
792
793 if (macho_file.blocks.getPtr(match)) |last| {
794 last.*.next = block;
795 block.prev = last.*;
796 last.* = block;
797 } else {786 } else {
798 try macho_file.blocks.putNoClobber(allocator, match, block);787 try parsed_atoms.putNoClobber(match, atom);
799 }788 }
800789 try self.atoms.append(allocator, atom);
801 try self.text_blocks.append(allocator, block);
802 }790 }
803 }791 }
792
793 return parsed_atoms;
804}794}
805795
806fn parseSymtab(self: *Object, allocator: *Allocator) !void {796fn parseSymtab(self: *Object, allocator: *Allocator) !void {
src/link/MachO/TextBlock.zig deleted-1221
...@@ -1,1221 +0,0 @@
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");
17const StringIndexAdapter = std.hash_map.StringIndexAdapter;
18
19/// Each decl always gets a local symbol with the fully qualified name.
20/// The vaddr and size are found here directly.
21/// The file offset is found by computing the vaddr offset from the section vaddr
22/// the symbol references, and adding that to the file offset of the section.
23/// If this field is 0, it means the codegen size = 0 and there is no symbol or
24/// offset table entry.
25local_sym_index: u32,
26
27/// List of symbol aliases pointing to the same block via different nlists
28aliases: std.ArrayListUnmanaged(u32) = .{},
29
30/// List of symbols contained within this block
31contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
32
33/// Code (may be non-relocated) this block represents
34code: std.ArrayListUnmanaged(u8) = .{},
35
36/// Size and alignment of this text block
37/// Unlike in Elf, we need to store the size of this symbol as part of
38/// the TextBlock since macho.nlist_64 lacks this information.
39size: u64,
40alignment: u32,
41
42relocs: std.ArrayListUnmanaged(Relocation) = .{},
43
44/// List of offsets contained within this block that need rebasing by the dynamic
45/// loader in presence of ASLR
46rebases: std.ArrayListUnmanaged(u64) = .{},
47
48/// List of offsets contained within this block that will be dynamically bound
49/// by the dynamic loader and contain pointers to resolved (at load time) extern
50/// symbols (aka proxies aka imports)
51bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
52
53/// List of data-in-code entries. This is currently specific to x86_64 only.
54dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
55
56/// Stab entry for this block. This is currently specific to a binary created
57/// by linking object files in a traditional sense - in incremental sense, we
58/// bypass stabs altogether to produce dSYM bundle directly with fully relocated
59/// DWARF sections.
60stab: ?Stab = null,
61
62/// Points to the previous and next neighbours
63next: ?*TextBlock,
64prev: ?*TextBlock,
65
66/// Previous/next linked list pointers.
67/// This is the linked list node for this Decl's corresponding .debug_info tag.
68dbg_info_prev: ?*TextBlock,
69dbg_info_next: ?*TextBlock,
70/// Offset into .debug_info pointing to the tag for this Decl.
71dbg_info_off: u32,
72/// Size of the .debug_info tag for this Decl, not including padding.
73dbg_info_len: u32,
74
75pub const SymbolAtOffset = struct {
76 local_sym_index: u32,
77 offset: u64,
78 stab: ?Stab = null,
79
80 pub fn format(
81 self: SymbolAtOffset,
82 comptime fmt: []const u8,
83 options: std.fmt.FormatOptions,
84 writer: anytype,
85 ) !void {
86 _ = fmt;
87 _ = options;
88 try std.fmt.format(writer, "{{ {d}: .offset = {d}", .{ self.local_sym_index, self.offset });
89 if (self.stab) |stab| {
90 try std.fmt.format(writer, ", .stab = {any}", .{stab});
91 }
92 try std.fmt.format(writer, " }}", .{});
93 }
94};
95
96pub const Stab = union(enum) {
97 function: u64,
98 static,
99 global,
100
101 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
102 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
103 defer nlists.deinit();
104
105 const sym = macho_file.locals.items[local_sym_index];
106 switch (stab) {
107 .function => |size| {
108 try nlists.ensureUnusedCapacity(4);
109 nlists.appendAssumeCapacity(.{
110 .n_strx = 0,
111 .n_type = macho.N_BNSYM,
112 .n_sect = sym.n_sect,
113 .n_desc = 0,
114 .n_value = sym.n_value,
115 });
116 nlists.appendAssumeCapacity(.{
117 .n_strx = sym.n_strx,
118 .n_type = macho.N_FUN,
119 .n_sect = sym.n_sect,
120 .n_desc = 0,
121 .n_value = sym.n_value,
122 });
123 nlists.appendAssumeCapacity(.{
124 .n_strx = 0,
125 .n_type = macho.N_FUN,
126 .n_sect = 0,
127 .n_desc = 0,
128 .n_value = size,
129 });
130 nlists.appendAssumeCapacity(.{
131 .n_strx = 0,
132 .n_type = macho.N_ENSYM,
133 .n_sect = sym.n_sect,
134 .n_desc = 0,
135 .n_value = size,
136 });
137 },
138 .global => {
139 try nlists.append(.{
140 .n_strx = sym.n_strx,
141 .n_type = macho.N_GSYM,
142 .n_sect = 0,
143 .n_desc = 0,
144 .n_value = 0,
145 });
146 },
147 .static => {
148 try nlists.append(.{
149 .n_strx = sym.n_strx,
150 .n_type = macho.N_STSYM,
151 .n_sect = sym.n_sect,
152 .n_desc = 0,
153 .n_value = sym.n_value,
154 });
155 },
156 }
157
158 return nlists.toOwnedSlice();
159 }
160};
161
162pub const Relocation = struct {
163 /// Offset within the `block`s code buffer.
164 /// Note relocation size can be inferred by relocation's kind.
165 offset: u32,
166
167 where: enum {
168 local,
169 undef,
170 },
171
172 where_index: u32,
173
174 payload: union(enum) {
175 unsigned: Unsigned,
176 branch: Branch,
177 page: Page,
178 page_off: PageOff,
179 pointer_to_got: PointerToGot,
180 signed: Signed,
181 load: Load,
182 },
183
184 const ResolveArgs = struct {
185 block: *TextBlock,
186 offset: u32,
187 source_addr: u64,
188 target_addr: u64,
189 macho_file: *MachO,
190 };
191
192 pub const Unsigned = struct {
193 subtractor: ?u32,
194
195 /// Addend embedded directly in the relocation slot
196 addend: i64,
197
198 /// Extracted from r_length:
199 /// => 3 implies true
200 /// => 2 implies false
201 /// => * is unreachable
202 is_64bit: bool,
203
204 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
205 const result = blk: {
206 if (self.subtractor) |subtractor| {
207 const sym = args.macho_file.locals.items[subtractor];
208 break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;
209 } else {
210 break :blk @intCast(i64, args.target_addr) + self.addend;
211 }
212 };
213
214 if (self.is_64bit) {
215 mem.writeIntLittle(u64, args.block.code.items[args.offset..][0..8], @bitCast(u64, result));
216 } else {
217 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
218 }
219 }
220
221 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
222 _ = fmt;
223 _ = options;
224 try std.fmt.format(writer, "Unsigned {{ ", .{});
225 if (self.subtractor) |sub| {
226 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
227 }
228 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
229 const length: usize = if (self.is_64bit) 8 else 4;
230 try std.fmt.format(writer, ".length = {}, ", .{length});
231 try std.fmt.format(writer, "}}", .{});
232 }
233 };
234
235 pub const Branch = struct {
236 arch: Arch,
237
238 pub fn resolve(self: Branch, args: ResolveArgs) !void {
239 switch (self.arch) {
240 .aarch64 => {
241 const displacement = try math.cast(
242 i28,
243 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
244 );
245 const code = args.block.code.items[args.offset..][0..4];
246 var inst = aarch64.Instruction{
247 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
248 aarch64.Instruction,
249 aarch64.Instruction.unconditional_branch_immediate,
250 ), code),
251 };
252 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
253 mem.writeIntLittle(u32, code, inst.toU32());
254 },
255 .x86_64 => {
256 const displacement = try math.cast(
257 i32,
258 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
259 );
260 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
261 },
262 else => return error.UnsupportedCpuArchitecture,
263 }
264 }
265
266 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
267 _ = self;
268 _ = fmt;
269 _ = options;
270 try std.fmt.format(writer, "Branch {{}}", .{});
271 }
272 };
273
274 pub const Page = struct {
275 kind: enum {
276 page,
277 got,
278 tlvp,
279 },
280 addend: u32 = 0,
281
282 pub fn resolve(self: Page, args: ResolveArgs) !void {
283 const target_addr = args.target_addr + self.addend;
284 const source_page = @intCast(i32, args.source_addr >> 12);
285 const target_page = @intCast(i32, target_addr >> 12);
286 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
287
288 const code = args.block.code.items[args.offset..][0..4];
289 var inst = aarch64.Instruction{
290 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
291 aarch64.Instruction,
292 aarch64.Instruction.pc_relative_address,
293 ), code),
294 };
295 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
296 inst.pc_relative_address.immlo = @truncate(u2, pages);
297
298 mem.writeIntLittle(u32, code, inst.toU32());
299 }
300
301 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
302 _ = fmt;
303 _ = options;
304 try std.fmt.format(writer, "Page {{ ", .{});
305 switch (self.kind) {
306 .page => {},
307 .got => {
308 try std.fmt.format(writer, ".got, ", .{});
309 },
310 .tlvp => {
311 try std.fmt.format(writer, ".tlvp", .{});
312 },
313 }
314 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
315 try std.fmt.format(writer, "}}", .{});
316 }
317 };
318
319 pub const PageOff = struct {
320 kind: enum {
321 page,
322 got,
323 tlvp,
324 },
325 addend: u32 = 0,
326 op_kind: ?OpKind = null,
327
328 pub const OpKind = enum {
329 arithmetic,
330 load,
331 };
332
333 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
334 const code = args.block.code.items[args.offset..][0..4];
335
336 switch (self.kind) {
337 .page => {
338 const target_addr = args.target_addr + self.addend;
339 const narrowed = @truncate(u12, target_addr);
340
341 const op_kind = self.op_kind orelse unreachable;
342 var inst: aarch64.Instruction = blk: {
343 switch (op_kind) {
344 .arithmetic => {
345 break :blk .{
346 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
347 aarch64.Instruction,
348 aarch64.Instruction.add_subtract_immediate,
349 ), code),
350 };
351 },
352 .load => {
353 break :blk .{
354 .load_store_register = mem.bytesToValue(meta.TagPayload(
355 aarch64.Instruction,
356 aarch64.Instruction.load_store_register,
357 ), code),
358 };
359 },
360 }
361 };
362
363 if (op_kind == .arithmetic) {
364 inst.add_subtract_immediate.imm12 = narrowed;
365 } else {
366 const offset: u12 = blk: {
367 if (inst.load_store_register.size == 0) {
368 if (inst.load_store_register.v == 1) {
369 // 128-bit SIMD is scaled by 16.
370 break :blk try math.divExact(u12, narrowed, 16);
371 }
372 // Otherwise, 8-bit SIMD or ldrb.
373 break :blk narrowed;
374 } else {
375 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
376 break :blk try math.divExact(u12, narrowed, denom);
377 }
378 };
379 inst.load_store_register.offset = offset;
380 }
381
382 mem.writeIntLittle(u32, code, inst.toU32());
383 },
384 .got => {
385 const narrowed = @truncate(u12, args.target_addr);
386 var inst: aarch64.Instruction = .{
387 .load_store_register = mem.bytesToValue(meta.TagPayload(
388 aarch64.Instruction,
389 aarch64.Instruction.load_store_register,
390 ), code),
391 };
392 const offset = try math.divExact(u12, narrowed, 8);
393 inst.load_store_register.offset = offset;
394 mem.writeIntLittle(u32, code, inst.toU32());
395 },
396 .tlvp => {
397 const RegInfo = struct {
398 rd: u5,
399 rn: u5,
400 size: u1,
401 };
402 const reg_info: RegInfo = blk: {
403 if (isArithmeticOp(code)) {
404 const inst = mem.bytesToValue(meta.TagPayload(
405 aarch64.Instruction,
406 aarch64.Instruction.add_subtract_immediate,
407 ), code);
408 break :blk .{
409 .rd = inst.rd,
410 .rn = inst.rn,
411 .size = inst.sf,
412 };
413 } else {
414 const inst = mem.bytesToValue(meta.TagPayload(
415 aarch64.Instruction,
416 aarch64.Instruction.load_store_register,
417 ), code);
418 break :blk .{
419 .rd = inst.rt,
420 .rn = inst.rn,
421 .size = @truncate(u1, inst.size),
422 };
423 }
424 };
425 const narrowed = @truncate(u12, args.target_addr);
426 var inst = aarch64.Instruction{
427 .add_subtract_immediate = .{
428 .rd = reg_info.rd,
429 .rn = reg_info.rn,
430 .imm12 = narrowed,
431 .sh = 0,
432 .s = 0,
433 .op = 0,
434 .sf = reg_info.size,
435 },
436 };
437 mem.writeIntLittle(u32, code, inst.toU32());
438 },
439 }
440 }
441
442 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
443 _ = fmt;
444 _ = options;
445 try std.fmt.format(writer, "PageOff {{ ", .{});
446 switch (self.kind) {
447 .page => {},
448 .got => {
449 try std.fmt.format(writer, ".got, ", .{});
450 },
451 .tlvp => {
452 try std.fmt.format(writer, ".tlvp, ", .{});
453 },
454 }
455 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
456 try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
457 try std.fmt.format(writer, "}}", .{});
458 }
459 };
460
461 pub const PointerToGot = struct {
462 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
463 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
464 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, result));
465 }
466
467 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
468 _ = self;
469 _ = fmt;
470 _ = options;
471 try std.fmt.format(writer, "PointerToGot {{}}", .{});
472 }
473 };
474
475 pub const Signed = struct {
476 addend: i64,
477 correction: i4,
478
479 pub fn resolve(self: Signed, args: ResolveArgs) !void {
480 const target_addr = @intCast(i64, args.target_addr) + self.addend;
481 const displacement = try math.cast(
482 i32,
483 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
484 );
485 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
486 }
487
488 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
489 _ = fmt;
490 _ = options;
491 try std.fmt.format(writer, "Signed {{ ", .{});
492 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
493 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
494 try std.fmt.format(writer, "}}", .{});
495 }
496 };
497
498 pub const Load = struct {
499 kind: enum {
500 got,
501 tlvp,
502 },
503 addend: i32 = 0,
504
505 pub fn resolve(self: Load, args: ResolveArgs) !void {
506 if (self.kind == .tlvp) {
507 // We need to rewrite the opcode from movq to leaq.
508 args.block.code.items[args.offset - 2] = 0x8d;
509 }
510 const displacement = try math.cast(
511 i32,
512 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,
513 );
514 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
515 }
516
517 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
518 _ = fmt;
519 _ = options;
520 try std.fmt.format(writer, "Load {{ ", .{});
521 try std.fmt.format(writer, "{s}, ", .{self.kind});
522 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
523 try std.fmt.format(writer, "}}", .{});
524 }
525 };
526
527 pub fn resolve(self: Relocation, args: ResolveArgs) !void {
528 switch (self.payload) {
529 .unsigned => |unsigned| try unsigned.resolve(args),
530 .branch => |branch| try branch.resolve(args),
531 .page => |page| try page.resolve(args),
532 .page_off => |page_off| try page_off.resolve(args),
533 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
534 .signed => |signed| try signed.resolve(args),
535 .load => |load| try load.resolve(args),
536 }
537 }
538
539 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
540 try std.fmt.format(writer, "Relocation {{ ", .{});
541 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
542 try std.fmt.format(writer, ".where = {}, ", .{self.where});
543 try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index});
544
545 switch (self.payload) {
546 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
547 .branch => |branch| try branch.format(fmt, options, writer),
548 .page => |page| try page.format(fmt, options, writer),
549 .page_off => |page_off| try page_off.format(fmt, options, writer),
550 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
551 .signed => |signed| try signed.format(fmt, options, writer),
552 .load => |load| try load.format(fmt, options, writer),
553 }
554
555 try std.fmt.format(writer, "}}", .{});
556 }
557};
558
559pub const empty = TextBlock{
560 .local_sym_index = 0,
561 .size = 0,
562 .alignment = 0,
563 .prev = null,
564 .next = null,
565 .dbg_info_prev = null,
566 .dbg_info_next = null,
567 .dbg_info_off = undefined,
568 .dbg_info_len = undefined,
569};
570
571pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
572 self.dices.deinit(allocator);
573 self.bindings.deinit(allocator);
574 self.rebases.deinit(allocator);
575 self.relocs.deinit(allocator);
576 self.contained.deinit(allocator);
577 self.aliases.deinit(allocator);
578 self.code.deinit(allocator);
579}
580
581/// Returns how much room there is to grow in virtual address space.
582/// File offset relocation happens transparently, so it is not included in
583/// this calculation.
584pub fn capacity(self: TextBlock, macho_file: MachO) u64 {
585 const self_sym = macho_file.locals.items[self.local_sym_index];
586 if (self.next) |next| {
587 const next_sym = macho_file.locals.items[next.local_sym_index];
588 return next_sym.n_value - self_sym.n_value;
589 } else {
590 // We are the last block.
591 // The capacity is limited only by virtual address space.
592 return std.math.maxInt(u64) - self_sym.n_value;
593 }
594}
595
596pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
597 // No need to keep a free list node for the last block.
598 const next = self.next orelse return false;
599 const self_sym = macho_file.locals.items[self.local_sym_index];
600 const next_sym = macho_file.locals.items[next.local_sym_index];
601 const cap = next_sym.n_value - self_sym.n_value;
602 const ideal_cap = MachO.padToIdeal(self.size);
603 if (cap <= ideal_cap) return false;
604 const surplus = cap - ideal_cap;
605 return surplus >= MachO.min_text_capacity;
606}
607
608const RelocContext = struct {
609 base_addr: u64 = 0,
610 allocator: *Allocator,
611 object: *Object,
612 macho_file: *MachO,
613};
614
615fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
616 var parsed_rel = Relocation{
617 .offset = @intCast(u32, @intCast(u64, rel.r_address) - context.base_addr),
618 .where = undefined,
619 .where_index = undefined,
620 .payload = undefined,
621 };
622
623 if (rel.r_extern == 0) {
624 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
625
626 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
627 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
628 const sect = seg.sections.items[sect_id];
629 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
630 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
631 const sym_name = try std.fmt.allocPrint(context.allocator, "l_{s}_{s}_{s}", .{
632 context.object.name,
633 commands.segmentName(sect),
634 commands.sectionName(sect),
635 });
636 defer context.allocator.free(sym_name);
637
638 try context.macho_file.locals.append(context.allocator, .{
639 .n_strx = try context.macho_file.makeString(sym_name),
640 .n_type = macho.N_SECT,
641 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
642 .n_desc = 0,
643 .n_value = sect.addr,
644 });
645 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
646 break :blk local_sym_index;
647 };
648
649 parsed_rel.where = .local;
650 parsed_rel.where_index = local_sym_index;
651 } else {
652 const sym = context.object.symtab.items[rel.r_symbolnum];
653 const sym_name = context.object.getString(sym.n_strx);
654
655 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
656 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
657 parsed_rel.where = .local;
658 parsed_rel.where_index = where_index;
659 } else {
660 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
661 .bytes = &context.macho_file.strtab,
662 }) orelse unreachable;
663 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
664 switch (resolv.where) {
665 .global => {
666 parsed_rel.where = .local;
667 parsed_rel.where_index = resolv.local_sym_index;
668 },
669 .undef => {
670 parsed_rel.where = .undef;
671 parsed_rel.where_index = resolv.where_index;
672 },
673 }
674 }
675 }
676
677 return parsed_rel;
678}
679
680pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: RelocContext) !void {
681 const filtered_relocs = filterRelocs(relocs, context.base_addr, context.base_addr + self.size);
682 var it = RelocIterator{
683 .buffer = filtered_relocs,
684 };
685
686 var addend: u32 = 0;
687 var subtractor: ?u32 = null;
688 const arch = context.macho_file.base.options.target.cpu.arch;
689
690 while (it.next()) |rel| {
691 if (isAddend(rel, arch)) {
692 // Addend is not a relocation with effect on the TextBlock, so
693 // parse it and carry on.
694 assert(addend == 0); // Oh no, addend was not reset!
695 addend = rel.r_symbolnum;
696
697 // Verify ADDEND is followed by a PAGE21 or PAGEOFF12.
698 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
699 switch (next) {
700 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
701 else => {
702 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
703 return error.UnexpectedRelocationType;
704 },
705 }
706 continue;
707 }
708
709 if (isSubtractor(rel, arch)) {
710 // Subtractor is not a relocation with effect on the TextBlock, so
711 // parse it and carry on.
712 assert(subtractor == null); // Oh no, subtractor was not reset!
713 assert(rel.r_extern == 1);
714 const sym = context.object.symtab.items[rel.r_symbolnum];
715 const sym_name = context.object.getString(sym.n_strx);
716
717 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
718 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
719 subtractor = where_index;
720 } else {
721 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
722 .bytes = &context.macho_file.strtab,
723 }) orelse unreachable;
724 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
725 assert(resolv.where == .global);
726 subtractor = resolv.local_sym_index;
727 }
728
729 // Verify SUBTRACTOR is followed by UNSIGNED.
730 switch (arch) {
731 .aarch64 => {
732 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
733 if (next != .ARM64_RELOC_UNSIGNED) {
734 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
735 return error.UnexpectedRelocationType;
736 }
737 },
738 .x86_64 => {
739 const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type);
740 if (next != .X86_64_RELOC_UNSIGNED) {
741 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
742 return error.UnexpectedRelocationType;
743 }
744 },
745 else => unreachable,
746 }
747 continue;
748 }
749
750 var parsed_rel = try initRelocFromObject(rel, context);
751
752 switch (arch) {
753 .aarch64 => {
754 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
755 switch (rel_type) {
756 .ARM64_RELOC_ADDEND => unreachable,
757 .ARM64_RELOC_SUBTRACTOR => unreachable,
758 .ARM64_RELOC_BRANCH26 => {
759 self.parseBranch(rel, &parsed_rel, context);
760 },
761 .ARM64_RELOC_UNSIGNED => {
762 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
763 subtractor = null;
764 },
765 .ARM64_RELOC_PAGE21,
766 .ARM64_RELOC_GOT_LOAD_PAGE21,
767 .ARM64_RELOC_TLVP_LOAD_PAGE21,
768 => {
769 self.parsePage(rel, &parsed_rel, addend);
770 if (rel_type == .ARM64_RELOC_PAGE21)
771 addend = 0;
772 },
773 .ARM64_RELOC_PAGEOFF12,
774 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
775 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
776 => {
777 self.parsePageOff(rel, &parsed_rel, addend);
778 if (rel_type == .ARM64_RELOC_PAGEOFF12)
779 addend = 0;
780 },
781 .ARM64_RELOC_POINTER_TO_GOT => {
782 self.parsePointerToGot(rel, &parsed_rel);
783 },
784 }
785 },
786 .x86_64 => {
787 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
788 .X86_64_RELOC_SUBTRACTOR => unreachable,
789 .X86_64_RELOC_BRANCH => {
790 self.parseBranch(rel, &parsed_rel, context);
791 },
792 .X86_64_RELOC_UNSIGNED => {
793 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
794 subtractor = null;
795 },
796 .X86_64_RELOC_SIGNED,
797 .X86_64_RELOC_SIGNED_1,
798 .X86_64_RELOC_SIGNED_2,
799 .X86_64_RELOC_SIGNED_4,
800 => {
801 self.parseSigned(rel, &parsed_rel, context);
802 },
803 .X86_64_RELOC_GOT_LOAD,
804 .X86_64_RELOC_GOT,
805 .X86_64_RELOC_TLV,
806 => {
807 self.parseLoad(rel, &parsed_rel);
808 },
809 }
810 },
811 else => unreachable,
812 }
813
814 try self.relocs.append(context.allocator, parsed_rel);
815
816 const is_via_got = switch (parsed_rel.payload) {
817 .pointer_to_got => true,
818 .load => |load| load.kind == .got,
819 .page => |page| page.kind == .got,
820 .page_off => |page_off| page_off.kind == .got,
821 else => false,
822 };
823
824 if (is_via_got) blk: {
825 const key = MachO.GotIndirectionKey{
826 .where = switch (parsed_rel.where) {
827 .local => .local,
828 .undef => .undef,
829 },
830 .where_index = parsed_rel.where_index,
831 };
832 if (context.macho_file.got_entries_map.contains(key)) break :blk;
833
834 const got_index = @intCast(u32, context.macho_file.got_entries.items.len);
835 try context.macho_file.got_entries.append(context.allocator, key);
836 try context.macho_file.got_entries_map.putNoClobber(context.allocator, key, got_index);
837 } else if (parsed_rel.payload == .unsigned) {
838 switch (parsed_rel.where) {
839 .undef => {
840 try self.bindings.append(context.allocator, .{
841 .local_sym_index = parsed_rel.where_index,
842 .offset = parsed_rel.offset,
843 });
844 },
845 .local => {
846 const source_sym = context.macho_file.locals.items[self.local_sym_index];
847 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
848 const seg = context.macho_file.load_commands.items[match.seg].Segment;
849 const sect = seg.sections.items[match.sect];
850 const sect_type = commands.sectionType(sect);
851
852 const should_rebase = rebase: {
853 if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false;
854
855 // TODO actually, a check similar to what dyld is doing, that is, verifying
856 // that the segment is writable should be enough here.
857 const is_right_segment = blk: {
858 if (context.macho_file.data_segment_cmd_index) |idx| {
859 if (match.seg == idx) {
860 break :blk true;
861 }
862 }
863 if (context.macho_file.data_const_segment_cmd_index) |idx| {
864 if (match.seg == idx) {
865 break :blk true;
866 }
867 }
868 break :blk false;
869 };
870
871 if (!is_right_segment) break :rebase false;
872 if (sect_type != macho.S_LITERAL_POINTERS and
873 sect_type != macho.S_REGULAR and
874 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
875 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
876 {
877 break :rebase false;
878 }
879
880 break :rebase true;
881 };
882
883 if (should_rebase) {
884 try self.rebases.append(context.allocator, parsed_rel.offset);
885 }
886 },
887 }
888 } else if (parsed_rel.payload == .branch) blk: {
889 if (parsed_rel.where != .undef) break :blk;
890 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
891
892 const stubs_index = @intCast(u32, context.macho_file.stubs.items.len);
893 try context.macho_file.stubs.append(context.allocator, parsed_rel.where_index);
894 try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stubs_index);
895 }
896 }
897}
898
899fn isAddend(rel: macho.relocation_info, arch: Arch) bool {
900 if (arch != .aarch64) return false;
901 return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND;
902}
903
904fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool {
905 return switch (arch) {
906 .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR,
907 .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR,
908 else => unreachable,
909 };
910}
911
912fn parseUnsigned(
913 self: TextBlock,
914 rel: macho.relocation_info,
915 out: *Relocation,
916 subtractor: ?u32,
917 context: RelocContext,
918) void {
919 assert(rel.r_pcrel == 0);
920
921 const is_64bit: bool = switch (rel.r_length) {
922 3 => true,
923 2 => false,
924 else => unreachable,
925 };
926
927 var addend: i64 = if (is_64bit)
928 mem.readIntLittle(i64, self.code.items[out.offset..][0..8])
929 else
930 mem.readIntLittle(i32, self.code.items[out.offset..][0..4]);
931
932 if (rel.r_extern == 0) {
933 assert(out.where == .local);
934 const target_sym = context.macho_file.locals.items[out.where_index];
935 addend -= @intCast(i64, target_sym.n_value);
936 }
937
938 out.payload = .{
939 .unsigned = .{
940 .subtractor = subtractor,
941 .is_64bit = is_64bit,
942 .addend = addend,
943 },
944 };
945}
946
947fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
948 _ = self;
949 assert(rel.r_pcrel == 1);
950 assert(rel.r_length == 2);
951
952 out.payload = .{
953 .branch = .{
954 .arch = context.macho_file.base.options.target.cpu.arch,
955 },
956 };
957}
958
959fn parsePage(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
960 _ = self;
961 assert(rel.r_pcrel == 1);
962 assert(rel.r_length == 2);
963
964 out.payload = .{
965 .page = .{
966 .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
967 .ARM64_RELOC_PAGE21 => .page,
968 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
969 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
970 else => unreachable,
971 },
972 .addend = addend,
973 },
974 };
975}
976
977fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
978 assert(rel.r_pcrel == 0);
979 assert(rel.r_length == 2);
980
981 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
982 const op_kind: ?Relocation.PageOff.OpKind = blk: {
983 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
984 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code.items[out.offset..][0..4]))
985 .arithmetic
986 else
987 .load;
988 break :blk op_kind;
989 };
990
991 out.payload = .{
992 .page_off = .{
993 .kind = switch (rel_type) {
994 .ARM64_RELOC_PAGEOFF12 => .page,
995 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
996 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
997 else => unreachable,
998 },
999 .addend = addend,
1000 .op_kind = op_kind,
1001 },
1002 };
1003}
1004
1005fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void {
1006 _ = self;
1007 assert(rel.r_pcrel == 1);
1008 assert(rel.r_length == 2);
1009
1010 out.payload = .{
1011 .pointer_to_got = .{},
1012 };
1013}
1014
1015fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
1016 assert(rel.r_pcrel == 1);
1017 assert(rel.r_length == 2);
1018
1019 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1020 const correction: i4 = switch (rel_type) {
1021 .X86_64_RELOC_SIGNED => 0,
1022 .X86_64_RELOC_SIGNED_1 => 1,
1023 .X86_64_RELOC_SIGNED_2 => 2,
1024 .X86_64_RELOC_SIGNED_4 => 4,
1025 else => unreachable,
1026 };
1027 var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;
1028
1029 if (rel.r_extern == 0) {
1030 const source_sym = context.macho_file.locals.items[self.local_sym_index];
1031 const target_sym = switch (out.where) {
1032 .local => context.macho_file.locals.items[out.where_index],
1033 .undef => context.macho_file.undefs.items[out.where_index],
1034 };
1035 addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value);
1036 }
1037
1038 out.payload = .{
1039 .signed = .{
1040 .correction = correction,
1041 .addend = addend,
1042 },
1043 };
1044}
1045
1046fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void {
1047 assert(rel.r_pcrel == 1);
1048 assert(rel.r_length == 2);
1049
1050 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1051 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)
1052 mem.readIntLittle(i32, self.code.items[out.offset..][0..4])
1053 else
1054 0;
1055
1056 out.payload = .{
1057 .load = .{
1058 .kind = switch (rel_type) {
1059 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
1060 .X86_64_RELOC_TLV => .tlvp,
1061 else => unreachable,
1062 },
1063 .addend = addend,
1064 },
1065 };
1066}
1067
1068pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
1069 for (self.relocs.items) |rel| {
1070 log.debug("relocating {}", .{rel});
1071
1072 const source_addr = blk: {
1073 const sym = macho_file.locals.items[self.local_sym_index];
1074 break :blk sym.n_value + rel.offset;
1075 };
1076 const target_addr = blk: {
1077 const is_via_got = switch (rel.payload) {
1078 .pointer_to_got => true,
1079 .page => |page| page.kind == .got,
1080 .page_off => |page_off| page_off.kind == .got,
1081 .load => |load| load.kind == .got,
1082 else => false,
1083 };
1084
1085 if (is_via_got) {
1086 const dc_seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
1087 const got = dc_seg.sections.items[macho_file.got_section_index.?];
1088 const got_index = macho_file.got_entries_map.get(.{
1089 .where = switch (rel.where) {
1090 .local => .local,
1091 .undef => .undef,
1092 },
1093 .where_index = rel.where_index,
1094 }) orelse {
1095 const sym = switch (rel.where) {
1096 .local => macho_file.locals.items[rel.where_index],
1097 .undef => macho_file.undefs.items[rel.where_index],
1098 };
1099 log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)});
1100 log.err(" this is an internal linker error", .{});
1101 return error.FailedToResolveRelocationTarget;
1102 };
1103 break :blk got.addr + got_index * @sizeOf(u64);
1104 }
1105
1106 switch (rel.where) {
1107 .local => {
1108 const sym = macho_file.locals.items[rel.where_index];
1109 const is_tlv = is_tlv: {
1110 const source_sym = macho_file.locals.items[self.local_sym_index];
1111 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
1112 const seg = macho_file.load_commands.items[match.seg].Segment;
1113 const sect = seg.sections.items[match.sect];
1114 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
1115 };
1116 if (is_tlv) {
1117 // For TLV relocations, the value specified as a relocation is the displacement from the
1118 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
1119 // defined TLV template init section in the following order:
1120 // * wrt to __thread_data if defined, then
1121 // * wrt to __thread_bss
1122 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment;
1123 const base_address = inner: {
1124 if (macho_file.tlv_data_section_index) |i| {
1125 break :inner seg.sections.items[i].addr;
1126 } else if (macho_file.tlv_bss_section_index) |i| {
1127 break :inner seg.sections.items[i].addr;
1128 } else {
1129 log.err("threadlocal variables present but no initializer sections found", .{});
1130 log.err(" __thread_data not found", .{});
1131 log.err(" __thread_bss not found", .{});
1132 return error.FailedToResolveRelocationTarget;
1133 }
1134 };
1135 break :blk sym.n_value - base_address;
1136 }
1137
1138 break :blk sym.n_value;
1139 },
1140 .undef => {
1141 const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse {
1142 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
1143 break :blk 0; // Dynamically bound by dyld.
1144 };
1145 const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
1146 const stubs = segment.sections.items[macho_file.stubs_section_index.?];
1147 break :blk stubs.addr + stubs_index * stubs.reserved2;
1148 },
1149 }
1150 };
1151
1152 log.debug(" | source_addr = 0x{x}", .{source_addr});
1153 log.debug(" | target_addr = 0x{x}", .{target_addr});
1154
1155 try rel.resolve(.{
1156 .block = self,
1157 .offset = rel.offset,
1158 .source_addr = source_addr,
1159 .target_addr = target_addr,
1160 .macho_file = macho_file,
1161 });
1162 }
1163}
1164
1165pub fn format(self: TextBlock, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
1166 _ = fmt;
1167 _ = options;
1168 try std.fmt.format(writer, "TextBlock {{ ", .{});
1169 try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
1170 try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items});
1171 try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items});
1172 try std.fmt.format(writer, ".code = {*}, ", .{self.code.items});
1173 try std.fmt.format(writer, ".size = {d}, ", .{self.size});
1174 try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment});
1175 try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items});
1176 try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items});
1177 try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items});
1178 try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items});
1179 if (self.stab) |stab| {
1180 try std.fmt.format(writer, ".stab = {any}, ", .{stab});
1181 }
1182 try std.fmt.format(writer, "}}", .{});
1183}
1184
1185const RelocIterator = struct {
1186 buffer: []const macho.relocation_info,
1187 index: i32 = -1,
1188
1189 pub fn next(self: *RelocIterator) ?macho.relocation_info {
1190 self.index += 1;
1191 if (self.index < self.buffer.len) {
1192 return self.buffer[@intCast(u32, self.index)];
1193 }
1194 return null;
1195 }
1196
1197 pub fn peek(self: RelocIterator) macho.relocation_info {
1198 assert(self.index + 1 < self.buffer.len);
1199 return self.buffer[@intCast(u32, self.index + 1)];
1200 }
1201};
1202
1203fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info {
1204 const Predicate = struct {
1205 addr: u64,
1206
1207 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1208 return rel.r_address < self.addr;
1209 }
1210 };
1211
1212 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
1213 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
1214
1215 return relocs[start..end];
1216}
1217
1218inline fn isArithmeticOp(inst: *const [4]u8) bool {
1219 const group_decode = @truncate(u5, inst[3]);
1220 return ((group_decode >> 2) == 4);
1221}
src/link/MachO/bind.zig-9
...@@ -9,15 +9,6 @@ pub const Pointer = struct {...@@ -9,15 +9,6 @@ pub const Pointer = struct {
9 name: ?[]const u8 = null,9 name: ?[]const u8 = null,
10};10};
1111
12pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
13 _ = context;
14 if (a.segment_id < b.segment_id) return true;
15 if (a.segment_id == b.segment_id) {
16 return a.offset < b.offset;
17 }
18 return false;
19}
20
21pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {12pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
22 var stream = std.io.countingWriter(std.io.null_writer);13 var stream = std.io.countingWriter(std.io.null_writer);
23 var writer = stream.writer();14 var writer = stream.writer();
src/link/MachO/commands.zig+8-93
...@@ -9,6 +9,7 @@ const assert = std.debug.assert;...@@ -9,6 +9,7 @@ const assert = std.debug.assert;
99
10const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
11const MachO = @import("../MachO.zig");11const MachO = @import("../MachO.zig");
12const makeStaticString = MachO.makeStaticString;
12const padToIdeal = MachO.padToIdeal;13const padToIdeal = MachO.padToIdeal;
1314
14pub const HeaderArgs = struct {15pub const HeaderArgs = struct {
...@@ -217,75 +218,6 @@ pub const SegmentCommand = struct {...@@ -217,75 +218,6 @@ pub const SegmentCommand = struct {
217 inner: macho.segment_command_64,218 inner: macho.segment_command_64,
218 sections: std.ArrayListUnmanaged(macho.section_64) = .{},219 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
219220
220 const SegmentOptions = struct {
221 cmdsize: u32 = @sizeOf(macho.segment_command_64),
222 vmaddr: u64 = 0,
223 vmsize: u64 = 0,
224 fileoff: u64 = 0,
225 filesize: u64 = 0,
226 maxprot: macho.vm_prot_t = macho.VM_PROT_NONE,
227 initprot: macho.vm_prot_t = macho.VM_PROT_NONE,
228 nsects: u32 = 0,
229 flags: u32 = 0,
230 };
231
232 pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand {
233 return .{
234 .inner = .{
235 .cmd = macho.LC_SEGMENT_64,
236 .cmdsize = opts.cmdsize,
237 .segname = makeStaticString(segname),
238 .vmaddr = opts.vmaddr,
239 .vmsize = opts.vmsize,
240 .fileoff = opts.fileoff,
241 .filesize = opts.filesize,
242 .maxprot = opts.maxprot,
243 .initprot = opts.initprot,
244 .nsects = opts.nsects,
245 .flags = opts.flags,
246 },
247 };
248 }
249
250 const SectionOptions = struct {
251 addr: u64 = 0,
252 size: u64 = 0,
253 offset: u32 = 0,
254 @"align": u32 = 0,
255 reloff: u32 = 0,
256 nreloc: u32 = 0,
257 flags: u32 = macho.S_REGULAR,
258 reserved1: u32 = 0,
259 reserved2: u32 = 0,
260 reserved3: u32 = 0,
261 };
262
263 pub fn addSection(
264 self: *SegmentCommand,
265 alloc: *Allocator,
266 comptime sectname: []const u8,
267 opts: SectionOptions,
268 ) !void {
269 var section = macho.section_64{
270 .sectname = makeStaticString(sectname),
271 .segname = undefined,
272 .addr = opts.addr,
273 .size = opts.size,
274 .offset = opts.offset,
275 .@"align" = opts.@"align",
276 .reloff = opts.reloff,
277 .nreloc = opts.nreloc,
278 .flags = opts.flags,
279 .reserved1 = opts.reserved1,
280 .reserved2 = opts.reserved2,
281 .reserved3 = opts.reserved3,
282 };
283 mem.copy(u8, &section.segname, &self.inner.segname);
284 try self.sections.append(alloc, section);
285 self.inner.cmdsize += @sizeOf(macho.section_64);
286 self.inner.nsects += 1;
287 }
288
289 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {221 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
290 const inner = try reader.readStruct(macho.segment_command_64);222 const inner = try reader.readStruct(macho.segment_command_64);
291 var segment = SegmentCommand{223 var segment = SegmentCommand{
...@@ -314,10 +246,8 @@ pub const SegmentCommand = struct {...@@ -314,10 +246,8 @@ pub const SegmentCommand = struct {
314 }246 }
315247
316 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {248 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
317 assert(start > 0);249 assert(start >= self.inner.fileoff);
318 if (start == self.inner.fileoff)250 var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
319 return 0;
320 var min_pos: u64 = std.math.maxInt(u64);
321 for (self.sections.items) |section| {251 for (self.sections.items) |section| {
322 if (section.offset <= start) continue;252 if (section.offset <= start) continue;
323 if (section.offset < min_pos) min_pos = section.offset;253 if (section.offset < min_pos) min_pos = section.offset;
...@@ -337,12 +267,12 @@ pub const SegmentCommand = struct {...@@ -337,12 +267,12 @@ pub const SegmentCommand = struct {
337 return null;267 return null;
338 }268 }
339269
340 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16, start: ?u64) u64 {270 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
341 var st: u64 = if (start) |v| v else self.inner.fileoff;271 var offset: u64 = if (start) |v| v else self.inner.fileoff;
342 while (self.detectAllocCollision(st, object_size)) |item_end| {272 while (self.detectAllocCollision(offset, object_size)) |item_end| {
343 st = mem.alignForwardGeneric(u64, item_end, min_alignment);273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
344 }274 }
345 return st;275 return offset;
346 }276 }
347277
348 fn eql(self: SegmentCommand, other: SegmentCommand) bool {278 fn eql(self: SegmentCommand, other: SegmentCommand) bool {
...@@ -427,13 +357,6 @@ pub fn createLoadDylibCommand(...@@ -427,13 +357,6 @@ pub fn createLoadDylibCommand(
427 return dylib_cmd;357 return dylib_cmd;
428}358}
429359
430fn makeStaticString(bytes: []const u8) [16]u8 {
431 var buf = [_]u8{0} ** 16;
432 assert(bytes.len <= buf.len);
433 mem.copy(u8, &buf, bytes);
434 return buf;
435}
436
437fn parseName(name: *const [16]u8) []const u8 {360fn parseName(name: *const [16]u8) []const u8 {
438 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;361 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
439 return name[0..len];362 return name[0..len];
...@@ -514,17 +437,14 @@ test "read-write segment command" {...@@ -514,17 +437,14 @@ test "read-write segment command" {
514 };437 };
515 var cmd = SegmentCommand{438 var cmd = SegmentCommand{
516 .inner = .{439 .inner = .{
517 .cmd = macho.LC_SEGMENT_64,
518 .cmdsize = 152,440 .cmdsize = 152,
519 .segname = makeStaticString("__TEXT"),441 .segname = makeStaticString("__TEXT"),
520 .vmaddr = 4294967296,442 .vmaddr = 4294967296,
521 .vmsize = 294912,443 .vmsize = 294912,
522 .fileoff = 0,
523 .filesize = 294912,444 .filesize = 294912,
524 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,445 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
525 .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,446 .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
526 .nsects = 1,447 .nsects = 1,
527 .flags = 0,
528 },448 },
529 };449 };
530 try cmd.sections.append(gpa, .{450 try cmd.sections.append(gpa, .{
...@@ -534,12 +454,7 @@ test "read-write segment command" {...@@ -534,12 +454,7 @@ test "read-write segment command" {
534 .size = 448,454 .size = 448,
535 .offset = 16384,455 .offset = 16384,
536 .@"align" = 2,456 .@"align" = 2,
537 .reloff = 0,
538 .nreloc = 0,
539 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,457 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
540 .reserved1 = 0,
541 .reserved2 = 0,
542 .reserved3 = 0,
543 });458 });
544 defer cmd.deinit(gpa);459 defer cmd.deinit(gpa);
545 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });460 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
test/stage2/darwin.zig+11-11
...@@ -27,8 +27,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -27,8 +27,8 @@ pub fn addCases(ctx: *TestContext) !void {
2727
28 // Regular old hello world28 // Regular old hello world
29 case.addCompareOutput(29 case.addCompareOutput(
30 \\extern "c" fn write(usize, usize, usize) usize;30 \\extern fn write(usize, usize, usize) usize;
31 \\extern "c" fn exit(usize) noreturn;31 \\extern fn exit(usize) noreturn;
32 \\32 \\
33 \\pub export fn main() noreturn {33 \\pub export fn main() noreturn {
34 \\ print();34 \\ print();
...@@ -47,8 +47,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -47,8 +47,8 @@ pub fn addCases(ctx: *TestContext) !void {
4747
48 // Print it 4 times and force growth and realloc.48 // Print it 4 times and force growth and realloc.
49 case.addCompareOutput(49 case.addCompareOutput(
50 \\extern "c" fn write(usize, usize, usize) usize;50 \\extern fn write(usize, usize, usize) usize;
51 \\extern "c" fn exit(usize) noreturn;51 \\extern fn exit(usize) noreturn;
52 \\52 \\
53 \\pub export fn main() noreturn {53 \\pub export fn main() noreturn {
54 \\ print();54 \\ print();
...@@ -74,8 +74,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -74,8 +74,8 @@ pub fn addCases(ctx: *TestContext) !void {
7474
75 // Print it once, and change the message.75 // Print it once, and change the message.
76 case.addCompareOutput(76 case.addCompareOutput(
77 \\extern "c" fn write(usize, usize, usize) usize;77 \\extern fn write(usize, usize, usize) usize;
78 \\extern "c" fn exit(usize) noreturn;78 \\extern fn exit(usize) noreturn;
79 \\79 \\
80 \\pub export fn main() noreturn {80 \\pub export fn main() noreturn {
81 \\ print();81 \\ print();
...@@ -94,8 +94,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -94,8 +94,8 @@ pub fn addCases(ctx: *TestContext) !void {
9494
95 // Now we print it twice.95 // Now we print it twice.
96 case.addCompareOutput(96 case.addCompareOutput(
97 \\extern "c" fn write(usize, usize, usize) usize;97 \\extern fn write(usize, usize, usize) usize;
98 \\extern "c" fn exit(usize) noreturn;98 \\extern fn exit(usize) noreturn;
99 \\99 \\
100 \\pub export fn main() noreturn {100 \\pub export fn main() noreturn {
101 \\ print();101 \\ print();
...@@ -121,7 +121,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -121,7 +121,7 @@ pub fn addCases(ctx: *TestContext) !void {
121 // This test case also covers an infrequent scenarion where the string table *may* be relocated121 // This test case also covers an infrequent scenarion where the string table *may* be relocated
122 // into the position preceeding the symbol table which results in a dyld error.122 // into the position preceeding the symbol table which results in a dyld error.
123 case.addCompareOutput(123 case.addCompareOutput(
124 \\extern "c" fn exit(usize) noreturn;124 \\extern fn exit(usize) noreturn;
125 \\125 \\
126 \\pub export fn main() noreturn {126 \\pub export fn main() noreturn {
127 \\ exit(0);127 \\ exit(0);
...@@ -131,8 +131,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -131,8 +131,8 @@ pub fn addCases(ctx: *TestContext) !void {
131 );131 );
132132
133 case.addCompareOutput(133 case.addCompareOutput(
134 \\extern "c" fn exit(usize) noreturn;134 \\extern fn exit(usize) noreturn;
135 \\extern "c" fn write(usize, usize, usize) usize;135 \\extern fn write(usize, usize, usize) usize;
136 \\136 \\
137 \\pub export fn main() noreturn {137 \\pub export fn main() noreturn {
138 \\ _ = write(1, @ptrToInt("Hey!\n"), 5);138 \\ _ = write(1, @ptrToInt("Hey!\n"), 5);