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
574574 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
575575 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
576576 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
577578 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
578579 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
579580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
580581 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
581 "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
582582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
583583 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
584584 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
lib/std/macho.zig+20-20
......@@ -601,35 +601,35 @@ pub const segment_command = extern struct {
601601/// command and their size is reflected in cmdsize.
602602pub const segment_command_64 = extern struct {
603603 /// LC_SEGMENT_64
604 cmd: u32,
604 cmd: u32 = LC_SEGMENT_64,
605605
606606 /// includes sizeof section_64 structs
607 cmdsize: u32,
607 cmdsize: u32 = @sizeOf(segment_command_64),
608608
609609 /// segment name
610610 segname: [16]u8,
611611
612612 /// memory address of this segment
613 vmaddr: u64,
613 vmaddr: u64 = 0,
614614
615615 /// memory size of this segment
616 vmsize: u64,
616 vmsize: u64 = 0,
617617
618618 /// file offset of this segment
619 fileoff: u64,
619 fileoff: u64 = 0,
620620
621621 /// amount to map from the file
622 filesize: u64,
622 filesize: u64 = 0,
623623
624624 /// maximum VM protection
625 maxprot: vm_prot_t,
625 maxprot: vm_prot_t = VM_PROT_NONE,
626626
627627 /// initial VM protection
628 initprot: vm_prot_t,
628 initprot: vm_prot_t = VM_PROT_NONE,
629629
630630 /// number of sections in segment
631 nsects: u32,
632 flags: u32,
631 nsects: u32 = 0,
632 flags: u32 = 0,
633633};
634634
635635/// 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 {
700700 segname: [16]u8,
701701
702702 /// memory address of this section
703 addr: u64,
703 addr: u64 = 0,
704704
705705 /// size in bytes of this section
706 size: u64,
706 size: u64 = 0,
707707
708708 /// file offset of this section
709 offset: u32,
709 offset: u32 = 0,
710710
711711 /// section alignment (power of 2)
712 @"align": u32,
712 @"align": u32 = 0,
713713
714714 /// file offset of relocation entries
715 reloff: u32,
715 reloff: u32 = 0,
716716
717717 /// number of relocation entries
718 nreloc: u32,
718 nreloc: u32 = 0,
719719
720720 /// flags (section type and attributes
721 flags: u32,
721 flags: u32 = S_REGULAR,
722722
723723 /// reserved (for offset or index)
724 reserved1: u32,
724 reserved1: u32 = 0,
725725
726726 /// reserved (for count or sizeof)
727 reserved2: u32,
727 reserved2: u32 = 0,
728728
729729 /// reserved
730 reserved3: u32,
730 reserved3: u32 = 0,
731731};
732732
733733pub const nlist = extern struct {
src/codegen.zig+17-45
......@@ -2816,24 +2816,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28162816 if (self.air.value(callee)) |func_value| {
28172817 if (func_value.castTag(.function)) |func_payload| {
28182818 const func = func_payload.data;
2819 const got_addr = blk: {
2820 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
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 };
2819 // TODO I'm hacking my way through here by repurposing .memory for storing
2820 // index to the GOT target symbol index.
28282821 switch (arch) {
28292822 .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 });
28312826 // callq *%rax
28322827 try self.code.ensureCapacity(self.code.items.len + 2);
28332828 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
28342829 },
28352830 .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 });
28372834 // blr x30
28382835 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
28392836 },
......@@ -4345,29 +4342,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43454342 }).toU32());
43464343
43474344 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.
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;
4345 // TODO I think the reloc might be in the wrong place.
43584346 const decl = macho_file.active_decl.?;
43594347 // Page reloc for adrp instruction.
43604348 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
43614349 .offset = offset,
43624350 .where = .local,
4363 .where_index = where_index,
4351 .where_index = @intCast(u32, addr),
43644352 .payload = .{ .page = .{ .kind = .got } },
43654353 });
43664354 // Pageoff reloc for adrp instruction.
43674355 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
43684356 .offset = offset + 4,
43694357 .where = .local,
4370 .where_index = where_index,
4358 .where_index = @intCast(u32, addr),
43714359 .payload = .{ .page_off = .{ .kind = .got } },
43724360 });
43734361 } else {
......@@ -4628,22 +4616,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46284616 const offset = @intCast(u32, self.code.items.len);
46294617
46304618 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.
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;
4619 // TODO I think the reloc might be in the wrong place.
46414620 const decl = macho_file.active_decl.?;
46424621 // Load reloc for LEA instruction.
46434622 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
46444623 .offset = offset - 4,
46454624 .where = .local,
4646 .where_index = where_index,
4625 .where_index = @intCast(u32, x),
46474626 .payload = .{ .load = .{ .kind = .got } },
46484627 });
46494628 } else {
......@@ -4869,17 +4848,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
48694848 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
48704849 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
48714850 return MCValue{ .memory = got_addr };
4872 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4873 const got_addr = blk: {
4874 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4875 const got = seg.sections.items[macho_file.got_section_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 };
4851 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4852 // TODO I'm hacking my way through here by repurposing .memory for storing
4853 // index to the GOT target symbol index.
4854 return MCValue{ .memory = decl.link.macho.local_sym_index };
48834855 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
48844856 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
48854857 return MCValue{ .memory = got_addr };
src/link/MachO.zig+3231-4152
......@@ -24,6 +24,7 @@ const trace = @import("../tracy.zig").trace;
2424const Air = @import("../Air.zig");
2525const Allocator = mem.Allocator;
2626const Archive = @import("MachO/Archive.zig");
27const Atom = @import("MachO/Atom.zig");
2728const Cache = @import("../Cache.zig");
2829const CodeSignature = @import("MachO/CodeSignature.zig");
2930const Compilation = @import("../Compilation.zig");
......@@ -39,9 +40,10 @@ const Module = @import("../Module.zig");
3940const SegmentCommand = commands.SegmentCommand;
4041const StringIndexAdapter = std.hash_map.StringIndexAdapter;
4142const StringIndexContext = std.hash_map.StringIndexContext;
42pub const TextBlock = @import("MachO/TextBlock.zig");
4343const Trie = @import("MachO/Trie.zig");
4444
45pub const TextBlock = Atom;
46
4547pub const base_tag: File.Tag = File.Tag.macho;
4648
4749base: File,
......@@ -95,9 +97,6 @@ source_version_cmd_index: ?u16 = null,
9597build_version_cmd_index: ?u16 = null,
9698uuid_cmd_index: ?u16 = null,
9799code_signature_cmd_index: ?u16 = null,
98/// Path to libSystem
99/// TODO this is obsolete, remove it.
100libsystem_cmd_index: ?u16 = null,
101100
102101// __TEXT segment sections
103102text_section_index: ?u16 = null,
......@@ -132,65 +131,59 @@ tlv_bss_section_index: ?u16 = null,
132131la_symbol_ptr_section_index: ?u16 = null,
133132data_section_index: ?u16 = null,
134133bss_section_index: ?u16 = null,
135common_section_index: ?u16 = null,
136134
137135objc_const_section_index: ?u16 = null,
138136objc_selrefs_section_index: ?u16 = null,
139137objc_classrefs_section_index: ?u16 = null,
140138objc_data_section_index: ?u16 = null,
141139
140bss_file_offset: u32 = 0,
141tlv_bss_file_offset: u32 = 0,
142
142143locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
143144globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
144145undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
145146symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
147unresolved: std.AutoArrayHashMapUnmanaged(u32, enum {
148 none,
149 stub,
150 got,
151}) = .{},
152tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
146153
147154locals_free_list: std.ArrayListUnmanaged(u32) = .{},
148155globals_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
152161strtab: std.ArrayListUnmanaged(u8) = .{},
153162strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
154163
155got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{},
156got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{},
157
158got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
159
160stubs: std.ArrayListUnmanaged(u32) = .{},
161stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
164got_entries_map: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, *Atom) = .{},
165stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{},
162166
163167error_flags: File.ErrorFlags = File.ErrorFlags{},
164168
165got_entries_count_dirty: bool = false,
166169load_commands_dirty: bool = false,
167rebase_info_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
170sections_order_dirty: bool = false,
175171has_dices: bool = false,
176172has_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
178179section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
179180
180pending_updates: std.ArrayListUnmanaged(struct {
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
181/// A list of atoms that have surplus capacity. This list can have false
189182/// positives, as functions grow and shrink over time, only sometimes being added
190183/// or removed from the freelist.
191184///
192/// A text block has surplus capacity when its overcapacity value is greater than
193/// padToIdeal(minimum_text_block_size). That is, when it has so
185/// An atom has surplus capacity when its overcapacity value is greater than
186/// padToIdeal(minimum_atom_size). That is, when it has so
194187/// much extra capacity, that we could fit a small new symbol in it, itself with
195188/// ideal_capacity or more.
196189///
......@@ -198,25 +191,23 @@ pending_updates: std.ArrayListUnmanaged(struct {
198191///
199192/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
200193/// 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 it
194/// allocate a fresh atom, which will have ideal capacity, and then grow it
202195/// 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 block
206last_text_block: ?*TextBlock = null,
198/// Pointer to the last allocated atom
199atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
207200
208/// List of TextBlocks that are owned directly by the linker.
209/// Currently these are only TextBlocks that are the result of linking
210/// object files. TextBlock which take part in incremental linking are
201/// List of atoms that are owned directly by the linker.
202/// Currently these are only atoms that are the result of linking
203/// object files. Atoms which take part in incremental linking are
211204/// at present owned by Module.Decl.
212205/// TODO consolidate this.
213managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
214
215blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
216207
217208/// Table of Decls that are currently alive.
218209/// 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.
220211/// TODO consolidate this.
221212decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
222213
......@@ -226,6 +217,12 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
226217/// somewhere else in the codegen.
227218active_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
229226const SymbolWithLoc = struct {
230227 // Table where the symbol can be found.
231228 where: enum {
......@@ -247,21 +244,10 @@ pub const GotIndirectionKey = struct {
247244
248245/// When allocating, the ideal_capacity is calculated by
249246/// actual_capacity + (actual_capacity / ideal_factor)
250const ideal_factor = 2;
247const ideal_factor = 4;
251248
252249/// Default path to dyld
253/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
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";
250const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
265251
266252/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
267253/// 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.
269255const minimum_text_block_size = 64;
270256pub 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
272262pub const Export = struct {
273263 sym_index: ?u32 = null,
274264};
......@@ -323,31 +313,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
323313 return self;
324314 }
325315
326 if (!options.strip and options.module != null) {
327 // Create dSYM bundle.
328 const dir = options.module.?.zig_cache_artifact_directory;
329 log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path });
316 // TODO Migrate DebugSymbols to the merged linker codepaths
317 // if (!options.strip and options.module != null) {
318 // // Create dSYM bundle.
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(
332 allocator,
333 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
334 .{sub_path},
335 );
336 defer allocator.free(d_sym_path);
322 // const d_sym_path = try fmt.allocPrint(
323 // allocator,
324 // "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
325 // .{sub_path},
326 // );
327 // defer allocator.free(d_sym_path);
337328
338 var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});
339 defer d_sym_bundle.close();
329 // var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});
330 // defer d_sym_bundle.close();
340331
341 const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
342 .truncate = false,
343 .read = true,
344 });
332 // const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
333 // .truncate = false,
334 // .read = true,
335 // });
345336
346 self.d_sym = .{
347 .base = self,
348 .file = d_sym_file,
349 };
350 }
337 // self.d_sym = .{
338 // .base = self,
339 // .file = d_sym_file,
340 // };
341 // }
351342
352343 // Index 0 is always a null symbol.
353344 try self.locals.append(allocator, .{
......@@ -357,13 +348,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
357348 .n_desc = 0,
358349 .n_value = 0,
359350 });
351 try self.strtab.append(allocator, 0);
360352
361353 try self.populateMissingMetadata();
362 try self.writeLocalSymbol(0);
363354
364355 if (self.d_sym) |*ds| {
365356 try ds.populateMissingMetadata(allocator);
366 try ds.writeLocalSymbol(0);
367357 }
368358
369359 return self;
......@@ -403,179 +393,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
403393 }
404394 }
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 {
579396 const tracy = trace(@src());
580397 defer tracy.end();
581398
......@@ -584,11 +401,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
584401 const arena = &arena_allocator.allocator;
585402
586403 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
588406 // If there is no Zig code to compile, then we should skip flushing the output file because it
589407 // will not be part of the linker line anyway.
590408 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;
592409 if (use_stage1) {
593410 const obj_basename = try std.zig.binNameAlloc(arena, .{
594411 .root_name = self.base.options.root_name,
......@@ -600,8 +417,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
600417 break :blk full_obj_path;
601418 }
602419
420 const obj_basename = self.base.intermediary_basename orelse break :blk null;
603421 try self.flushModule(comp);
604 const obj_basename = self.base.intermediary_basename.?;
605422 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
606423 break :blk full_obj_path;
607424 } else null;
......@@ -617,8 +434,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
617434 defer if (!self.base.options.disable_lld_caching) man.deinit();
618435
619436 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) {
622442 man = comp.cache_parent.obtain();
623443
624444 // 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 {
652472 id_symlink_basename,
653473 &prev_digest_buf,
654474 ) 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 });
656479 // Handle this as a cache miss.
657480 break :blk prev_digest_buf[0..0];
658481 };
659482 if (mem.eql(u8, prev_digest, &digest)) {
660 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
661483 // Hot diggity dog! The output binary is already there.
662 self.base.lock = man.toOwnedLock();
663 return;
484
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 }
664500 }
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
667506 // We are about to change the output file to be different, so we invalidate the build hash now.
668507 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
......@@ -670,7 +509,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
670509 else => |e| return e,
671510 };
672511 }
673
674512 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
675513
676514 if (self.base.options.output_mode == .Obj) {
......@@ -697,270 +535,456 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
697535 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
698536 }
699537 } else {
700 // Positional arguments to the linker such as object files and static archives.
701 var positionals = std.ArrayList([]const u8).init(arena);
702
703 try positionals.appendSlice(self.base.options.objects);
538 if (use_stage1) {
539 const sub_path = self.base.options.emit.?.sub_path;
540 self.base.file = try directory.handle.createFile(sub_path, .{
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| {
706 try positionals.append(key.status.success.object_path);
547 // TODO mimicking insertion of null symbol from incremental linker.
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);
707557 }
708558
709 if (module_obj_path) |p| {
710 try positionals.append(p);
711 }
559 if (needs_full_relink) {
560 self.objects.clearRetainingCapacity();
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++ dep
716 if (self.base.options.link_libcpp) {
717 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
718 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
719 }
568 // Positional arguments to the linker such as object files and static archives.
569 var positionals = std.ArrayList([]const u8).init(arena);
720570
721 // Shared and static libraries passed via `-l` flag.
722 var search_lib_names = std.ArrayList([]const u8).init(arena);
571 try positionals.appendSlice(self.base.options.objects);
723572
724 const system_libs = self.base.options.system_libs.keys();
725 for (system_libs) |link_lib| {
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;
573 for (comp.c_object_table.keys()) |key| {
574 try positionals.append(key.status.success.object_path);
732575 }
733576
734 try search_lib_names.append(link_lib);
735 }
577 if (module_obj_path) |p| {
578 try positionals.append(p);
579 }
736580
737 var lib_dirs = std.ArrayList([]const u8).init(arena);
738 for (self.base.options.lib_dirs) |dir| {
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});
581 if (comp.compiler_rt_static_lib) |lib| {
582 try positionals.append(lib.full_object_path);
743583 }
744 }
745584
746 var libs = std.ArrayList([]const u8).init(arena);
747 var lib_not_found = false;
748 for (search_lib_names.items) |lib_name| {
749 // Assume ld64 default: -search_paths_first
750 // Look in each directory for a dylib (stub first), and then for archive
751 // TODO implement alternative: -search_dylibs_first
752 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
753 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
754 try libs.append(full_path);
755 break;
585 // libc++ dep
586 if (self.base.options.link_libcpp) {
587 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
588 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
589 }
590
591 // Shared and static libraries passed via `-l` flag.
592 var search_lib_names = std.ArrayList([]const u8).init(arena);
593
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;
756602 }
757 } else {
758 log.warn("library not found for '-l{s}'", .{lib_name});
759 lib_not_found = true;
603
604 try search_lib_names.append(link_lib);
760605 }
761 }
762606
763 if (lib_not_found) {
764 log.warn("Library search paths:", .{});
765 for (lib_dirs.items) |dir| {
766 log.warn(" {s}", .{dir});
607 var lib_dirs = std.ArrayList([]const u8).init(arena);
608 for (self.base.options.lib_dirs) |dir| {
609 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
610 try lib_dirs.append(search_dir);
611 } else {
612 log.warn("directory not found for '-L{s}'", .{dir});
613 }
767614 }
768 }
769615
770 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
771 var libsystem_available = false;
772 if (self.base.options.sysroot != null) blk: {
773 // Try stub file first. If we hit it, then we're done as the stub file
774 // re-exports every single symbol definition.
775 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
776 try libs.append(full_path);
777 libsystem_available = true;
778 break :blk;
616 var libs = std.ArrayList([]const u8).init(arena);
617 var lib_not_found = false;
618 for (search_lib_names.items) |lib_name| {
619 // Assume ld64 default: -search_paths_first
620 // Look in each directory for a dylib (stub first), and then for archive
621 // TODO implement alternative: -search_dylibs_first
622 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
623 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
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 }
779638 }
780 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
781 // doesn't export libc.dylib which we'll need to resolve subsequently also.
782 if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
783 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
784 try libs.append(libsystem_path);
785 try libs.append(libc_path);
639
640 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
641 var libsystem_available = false;
642 if (self.base.options.sysroot != null) blk: {
643 // Try stub file first. If we hit it, then we're done as the stub file
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);
786647 libsystem_available = true;
787648 break :blk;
788649 }
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);
789666 }
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 // frameworks
799 var framework_dirs = std.ArrayList([]const u8).init(arena);
800 for (self.base.options.framework_dirs) |dir| {
801 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
802 try framework_dirs.append(search_dir);
803 } else {
804 log.warn("directory not found for '-F{s}'", .{dir});
668 // frameworks
669 var framework_dirs = std.ArrayList([]const u8).init(arena);
670 for (self.base.options.framework_dirs) |dir| {
671 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
672 try framework_dirs.append(search_dir);
673 } else {
674 log.warn("directory not found for '-F{s}'", .{dir});
675 }
805676 }
806 }
807677
808 var framework_not_found = false;
809 for (self.base.options.frameworks) |framework| {
810 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
811 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
812 try libs.append(full_path);
813 break;
678 var framework_not_found = false;
679 for (self.base.options.frameworks) |framework| {
680 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
681 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
682 try libs.append(full_path);
683 break;
684 }
685 } else {
686 log.warn("framework not found for '-framework {s}'", .{framework});
687 framework_not_found = true;
814688 }
815 } else {
816 log.warn("framework not found for '-framework {s}'", .{framework});
817 framework_not_found = true;
818689 }
819 }
820690
821 if (framework_not_found) {
822 log.warn("Framework search paths:", .{});
823 for (framework_dirs.items) |dir| {
824 log.warn(" {s}", .{dir});
691 if (framework_not_found) {
692 log.warn("Framework search paths:", .{});
693 for (framework_dirs.items) |dir| {
694 log.warn(" {s}", .{dir});
695 }
825696 }
826 }
827697
828 // rpaths
829 var rpath_table = std.StringArrayHashMap(void).init(arena);
830 for (self.base.options.rpath_list) |rpath| {
831 if (rpath_table.contains(rpath)) continue;
832 try rpath_table.putNoClobber(rpath, {});
833 }
698 // rpaths
699 var rpath_table = std.StringArrayHashMap(void).init(arena);
700 for (self.base.options.rpath_list) |rpath| {
701 if (rpath_table.contains(rpath)) continue;
702 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
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);
836 try rpaths.ensureCapacity(rpath_table.count());
837 for (rpath_table.keys()) |*key| {
838 rpaths.appendAssumeCapacity(key.*);
839 }
720 if (self.base.options.verbose_link) {
721 var argv = std.ArrayList([]const u8).init(arena);
840722
841 if (self.base.options.verbose_link) {
842 var argv = std.ArrayList([]const u8).init(arena);
723 try argv.append("zig");
724 try argv.append("ld");
843725
844 try argv.append("zig");
845 try argv.append("ld");
726 if (is_exe_or_dyn_lib) {
727 try argv.append("-dynamic");
728 }
846729
847 if (is_exe_or_dyn_lib) {
848 try argv.append("-dynamic");
849 }
730 if (is_dyn_lib) {
731 try argv.append("-dylib");
850732
851 if (is_dyn_lib) {
852 try argv.append("-dylib");
733 const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{
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}", .{
855 self.base.options.emit.?.sub_path,
856 });
857 try argv.append("-install_name");
858 try argv.append(install_name);
859 }
740 if (self.base.options.sysroot) |syslibroot| {
741 try argv.append("-syslibroot");
742 try argv.append(syslibroot);
743 }
860744
861 if (self.base.options.sysroot) |syslibroot| {
862 try argv.append("-syslibroot");
863 try argv.append(syslibroot);
864 }
745 for (rpath_table.keys()) |rpath| {
746 try argv.append("-rpath");
747 try argv.append(rpath);
748 }
865749
866 for (rpaths.items) |rpath| {
867 try argv.append("-rpath");
868 try argv.append(rpath);
869 }
750 try argv.appendSlice(positionals.items);
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");
874 try argv.append(full_out_path);
755 try argv.append("-lSystem");
756 try argv.append("-lc");
875757
876 try argv.append("-lSystem");
877 try argv.append("-lc");
758 for (search_lib_names.items) |l_name| {
759 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
760 }
878761
879 for (search_lib_names.items) |l_name| {
880 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
881 }
762 for (self.base.options.lib_dirs) |lib_dir| {
763 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
764 }
882765
883 for (self.base.options.lib_dirs) |lib_dir| {
884 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
885 }
766 for (self.base.options.frameworks) |framework| {
767 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));
768 }
886769
887 for (self.base.options.frameworks) |framework| {
888 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));
889 }
770 for (self.base.options.framework_dirs) |framework_dir| {
771 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
772 }
890773
891 for (self.base.options.framework_dirs) |framework_dir| {
892 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
774 Compilation.dump_argv(argv.items);
893775 }
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);
896779 }
897780
898 const sub_path = self.base.options.emit.?.sub_path;
899 self.base.file = try directory.handle.createFile(sub_path, .{
900 .truncate = true,
901 .read = true,
902 .mode = link.determineMode(self.base.options),
903 });
904
905 // TODO mimicking insertion of null symbol from incremental linker.
906 // This will need to moved.
907 try self.locals.append(self.base.allocator, .{
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();
781 if (self.bss_section_index) |idx| {
782 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
783 const sect = &seg.sections.items[idx];
784 sect.offset = self.bss_file_offset;
785 }
786 if (self.tlv_bss_section_index) |idx| {
787 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
788 const sect = &seg.sections.items[idx];
789 sect.offset = self.tlv_bss_file_offset;
790 }
925791
926 {
927 // Add dyld_stub_binder as the final GOT entry.
928 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "dyld_stub_binder"), StringIndexAdapter{
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);
792 for (self.objects.items) |*object, object_id| {
793 if (object.analyzed) continue;
794 try self.resolveSymbolsInObject(@intCast(u16, object_id));
939795 }
940796
941 try self.sortSections();
942 try self.allocateTextSegment();
943 try self.allocateDataConstSegment();
944 try self.allocateDataSegment();
945 self.allocateLinkeditSegment();
946 try self.allocateTextBlocks();
947 try self.flushZld();
797 try self.resolveSymbolsInArchives();
798 try self.resolveDyldStubBinder();
799 try self.createDyldPrivateAtom();
800 try self.createStubHelperPreambleAtom();
801 try self.resolveSymbolsInDylibs();
802 try self.createDsoHandleAtom();
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);
948836 }
949837
950 if (!self.base.options.disable_lld_caching) {
838 cache: {
839 if (use_stage1 and self.base.options.disable_lld_caching) break :cache;
951840 // Update the file with the digest. If it fails we can continue; it only
952841 // means that the next invocation will have an unnecessary cache miss.
953842 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)});
955844 };
956845 // Again failure here only means an unnecessary cache miss.
957846 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)});
959848 };
960849 // We hang on to this lock so that the output file path can be used without
961850 // other processes clobbering it.
962851 self.base.lock = man.toOwnedLock();
963852 }
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;
964988}
965989
966990fn parseObject(self: *MachO, path: []const u8) !bool {
......@@ -1080,6 +1104,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
10801104 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
10811105
10821106 if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {
1107 try self.addLoadDylibLC(dylib_id);
10831108 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
10841109 }
10851110
......@@ -1098,6 +1123,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
10981123 break :full_path try self.base.allocator.dupe(u8, path);
10991124 };
11001125 defer self.base.allocator.free(full_path);
1126 log.debug("parsing input file path '{s}'", .{full_path});
11011127
11021128 if (try self.parseObject(full_path)) continue;
11031129 if (try self.parseArchive(full_path)) continue;
......@@ -1111,6 +1137,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
11111137
11121138fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
11131139 for (libs) |lib| {
1140 log.debug("parsing lib path '{s}'", .{lib});
11141141 if (try self.parseDylib(lib, .{
11151142 .syslibroot = syslibroot,
11161143 })) continue;
......@@ -1126,18 +1153,19 @@ pub const MatchingSection = struct {
11261153};
11271154
11281155pub 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;
11321156 const segname = commands.segmentName(sect);
11331157 const sectname = commands.sectionName(sect);
1134
11351158 const res: ?MatchingSection = blk: {
11361159 switch (commands.sectionType(sect)) {
11371160 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
11381161 if (self.text_const_section_index == null) {
1139 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
1140 try text_seg.addSection(self.base.allocator, "__const", .{});
1162 self.text_const_section_index = try self.allocateSection(
1163 self.text_segment_cmd_index.?,
1164 "__const",
1165 sect.size,
1166 sect.@"align",
1167 .{},
1168 );
11411169 }
11421170
11431171 break :blk .{
......@@ -1150,10 +1178,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
11501178 // TODO it seems the common values within the sections in objects are deduplicated/merged
11511179 // on merging the sections' contents.
11521180 if (self.objc_methname_section_index == null) {
1153 self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len);
1154 try text_seg.addSection(self.base.allocator, "__objc_methname", .{
1155 .flags = macho.S_CSTRING_LITERALS,
1156 });
1181 self.objc_methname_section_index = try self.allocateSection(
1182 self.text_segment_cmd_index.?,
1183 "__objc_methname",
1184 sect.size,
1185 sect.@"align",
1186 .{},
1187 );
11571188 }
11581189
11591190 break :blk .{
......@@ -1162,10 +1193,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
11621193 };
11631194 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
11641195 if (self.objc_methtype_section_index == null) {
1165 self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len);
1166 try text_seg.addSection(self.base.allocator, "__objc_methtype", .{
1167 .flags = macho.S_CSTRING_LITERALS,
1168 });
1196 self.objc_methtype_section_index = try self.allocateSection(
1197 self.text_segment_cmd_index.?,
1198 "__objc_methtype",
1199 sect.size,
1200 sect.@"align",
1201 .{},
1202 );
11691203 }
11701204
11711205 break :blk .{
......@@ -1174,8 +1208,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
11741208 };
11751209 } else if (mem.eql(u8, sectname, "__objc_classname")) {
11761210 if (self.objc_classname_section_index == null) {
1177 self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len);
1178 try text_seg.addSection(self.base.allocator, "__objc_classname", .{});
1211 self.objc_classname_section_index = try self.allocateSection(
1212 self.text_segment_cmd_index.?,
1213 "__objc_classname",
1214 sect.size,
1215 sect.@"align",
1216 .{},
1217 );
11791218 }
11801219
11811220 break :blk .{
......@@ -1185,10 +1224,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
11851224 }
11861225
11871226 if (self.cstring_section_index == null) {
1188 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
1189 try text_seg.addSection(self.base.allocator, "__cstring", .{
1190 .flags = macho.S_CSTRING_LITERALS,
1191 });
1227 self.cstring_section_index = try self.allocateSection(
1228 self.text_segment_cmd_index.?,
1229 "__cstring",
1230 sect.size,
1231 sect.@"align",
1232 .{
1233 .flags = macho.S_CSTRING_LITERALS,
1234 },
1235 );
11921236 }
11931237
11941238 break :blk .{
......@@ -1199,27 +1243,37 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
11991243 macho.S_LITERAL_POINTERS => {
12001244 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
12011245 if (self.objc_selrefs_section_index == null) {
1202 self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len);
1203 try data_seg.addSection(self.base.allocator, "__objc_selrefs", .{
1204 .flags = macho.S_LITERAL_POINTERS,
1205 });
1246 self.objc_selrefs_section_index = try self.allocateSection(
1247 self.data_segment_cmd_index.?,
1248 "__objc_selrefs",
1249 sect.size,
1250 sect.@"align",
1251 .{
1252 .flags = macho.S_LITERAL_POINTERS,
1253 },
1254 );
12061255 }
12071256
12081257 break :blk .{
12091258 .seg = self.data_segment_cmd_index.?,
12101259 .sect = self.objc_selrefs_section_index.?,
12111260 };
1261 } else {
1262 // TODO investigate
1263 break :blk null;
12121264 }
1213
1214 // TODO investigate
1215 break :blk null;
12161265 },
12171266 macho.S_MOD_INIT_FUNC_POINTERS => {
12181267 if (self.mod_init_func_section_index == null) {
1219 self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
1220 try data_const_seg.addSection(self.base.allocator, "__mod_init_func", .{
1221 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
1222 });
1268 self.mod_init_func_section_index = try self.allocateSection(
1269 self.data_const_segment_cmd_index.?,
1270 "__mod_init_func",
1271 sect.size,
1272 sect.@"align",
1273 .{
1274 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
1275 },
1276 );
12231277 }
12241278
12251279 break :blk .{
......@@ -1229,10 +1283,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
12291283 },
12301284 macho.S_MOD_TERM_FUNC_POINTERS => {
12311285 if (self.mod_term_func_section_index == null) {
1232 self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
1233 try data_const_seg.addSection(self.base.allocator, "__mod_term_func", .{
1234 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
1235 });
1286 self.mod_term_func_section_index = try self.allocateSection(
1287 self.data_const_segment_cmd_index.?,
1288 "__mod_term_func",
1289 sect.size,
1290 sect.@"align",
1291 .{
1292 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
1293 },
1294 );
12361295 }
12371296
12381297 break :blk .{
......@@ -1241,38 +1300,34 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
12411300 };
12421301 },
12431302 macho.S_ZEROFILL => {
1244 if (mem.eql(u8, sectname, "__common")) {
1245 if (self.common_section_index == null) {
1246 self.common_section_index = @intCast(u16, data_seg.sections.items.len);
1247 try data_seg.addSection(self.base.allocator, "__common", .{
1248 .flags = macho.S_ZEROFILL,
1249 });
1250 }
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", .{
1303 if (self.bss_section_index == null) {
1304 self.bss_section_index = try self.allocateSection(
1305 self.data_segment_cmd_index.?,
1306 "__bss",
1307 sect.size,
1308 sect.@"align",
1309 .{
12601310 .flags = macho.S_ZEROFILL,
1261 });
1262 }
1263
1264 break :blk .{
1265 .seg = self.data_segment_cmd_index.?,
1266 .sect = self.bss_section_index.?,
1267 };
1311 },
1312 );
12681313 }
1314
1315 break :blk .{
1316 .seg = self.data_segment_cmd_index.?,
1317 .sect = self.bss_section_index.?,
1318 };
12691319 },
12701320 macho.S_THREAD_LOCAL_VARIABLES => {
12711321 if (self.tlv_section_index == null) {
1272 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
1273 try data_seg.addSection(self.base.allocator, "__thread_vars", .{
1274 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1275 });
1322 self.tlv_section_index = try self.allocateSection(
1323 self.data_segment_cmd_index.?,
1324 "__thread_vars",
1325 sect.size,
1326 sect.@"align",
1327 .{
1328 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1329 },
1330 );
12761331 }
12771332
12781333 break :blk .{
......@@ -1282,10 +1337,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
12821337 },
12831338 macho.S_THREAD_LOCAL_REGULAR => {
12841339 if (self.tlv_data_section_index == null) {
1285 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
1286 try data_seg.addSection(self.base.allocator, "__thread_data", .{
1287 .flags = macho.S_THREAD_LOCAL_REGULAR,
1288 });
1340 self.tlv_data_section_index = try self.allocateSection(
1341 self.data_segment_cmd_index.?,
1342 "__thread_data",
1343 sect.size,
1344 sect.@"align",
1345 .{
1346 .flags = macho.S_THREAD_LOCAL_REGULAR,
1347 },
1348 );
12891349 }
12901350
12911351 break :blk .{
......@@ -1295,10 +1355,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
12951355 },
12961356 macho.S_THREAD_LOCAL_ZEROFILL => {
12971357 if (self.tlv_bss_section_index == null) {
1298 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
1299 try data_seg.addSection(self.base.allocator, "__thread_bss", .{
1300 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
1301 });
1358 self.tlv_bss_section_index = try self.allocateSection(
1359 self.data_segment_cmd_index.?,
1360 "__thread_bss",
1361 sect.size,
1362 sect.@"align",
1363 .{
1364 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
1365 },
1366 );
13021367 }
13031368
13041369 break :blk .{
......@@ -1311,8 +1376,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13111376 // TODO I believe __eh_frame is currently part of __unwind_info section
13121377 // in the latest ld64 output.
13131378 if (self.eh_frame_section_index == null) {
1314 self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len);
1315 try text_seg.addSection(self.base.allocator, "__eh_frame", .{});
1379 self.eh_frame_section_index = try self.allocateSection(
1380 self.text_segment_cmd_index.?,
1381 "__eh_frame",
1382 sect.size,
1383 sect.@"align",
1384 .{},
1385 );
13161386 }
13171387
13181388 break :blk .{
......@@ -1323,8 +1393,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13231393
13241394 // TODO audit this: is this the right mapping?
13251395 if (self.data_const_section_index == null) {
1326 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
1327 try data_const_seg.addSection(self.base.allocator, "__const", .{});
1396 self.data_const_section_index = try self.allocateSection(
1397 self.data_const_segment_cmd_index.?,
1398 "__const",
1399 sect.size,
1400 sect.@"align",
1401 .{},
1402 );
13281403 }
13291404
13301405 break :blk .{
......@@ -1335,10 +1410,17 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13351410 macho.S_REGULAR => {
13361411 if (commands.sectionIsCode(sect)) {
13371412 if (self.text_section_index == null) {
1338 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
1339 try text_seg.addSection(self.base.allocator, "__text", .{
1340 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
1341 });
1413 self.text_section_index = try self.allocateSection(
1414 self.text_segment_cmd_index.?,
1415 "__text",
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 );
13421424 }
13431425
13441426 break :blk .{
......@@ -1359,8 +1441,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13591441 if (mem.eql(u8, segname, "__TEXT")) {
13601442 if (mem.eql(u8, sectname, "__ustring")) {
13611443 if (self.ustring_section_index == null) {
1362 self.ustring_section_index = @intCast(u16, text_seg.sections.items.len);
1363 try text_seg.addSection(self.base.allocator, "__ustring", .{});
1444 self.ustring_section_index = try self.allocateSection(
1445 self.text_segment_cmd_index.?,
1446 "__ustring",
1447 sect.size,
1448 sect.@"align",
1449 .{},
1450 );
13641451 }
13651452
13661453 break :blk .{
......@@ -1369,8 +1456,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13691456 };
13701457 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
13711458 if (self.gcc_except_tab_section_index == null) {
1372 self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len);
1373 try text_seg.addSection(self.base.allocator, "__gcc_except_tab", .{});
1459 self.gcc_except_tab_section_index = try self.allocateSection(
1460 self.text_segment_cmd_index.?,
1461 "__gcc_except_tab",
1462 sect.size,
1463 sect.@"align",
1464 .{},
1465 );
13741466 }
13751467
13761468 break :blk .{
......@@ -1379,8 +1471,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13791471 };
13801472 } else if (mem.eql(u8, sectname, "__objc_methlist")) {
13811473 if (self.objc_methlist_section_index == null) {
1382 self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len);
1383 try text_seg.addSection(self.base.allocator, "__objc_methlist", .{});
1474 self.objc_methlist_section_index = try self.allocateSection(
1475 self.text_segment_cmd_index.?,
1476 "__objc_methlist",
1477 sect.size,
1478 sect.@"align",
1479 .{},
1480 );
13841481 }
13851482
13861483 break :blk .{
......@@ -1394,8 +1491,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
13941491 mem.eql(u8, sectname, "__gopclntab"))
13951492 {
13961493 if (self.data_const_section_index == null) {
1397 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
1398 try data_const_seg.addSection(self.base.allocator, "__const", .{});
1494 self.data_const_section_index = try self.allocateSection(
1495 self.data_const_segment_cmd_index.?,
1496 "__const",
1497 sect.size,
1498 sect.@"align",
1499 .{},
1500 );
13991501 }
14001502
14011503 break :blk .{
......@@ -1404,8 +1506,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14041506 };
14051507 } else {
14061508 if (self.text_const_section_index == null) {
1407 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
1408 try text_seg.addSection(self.base.allocator, "__const", .{});
1509 self.text_const_section_index = try self.allocateSection(
1510 self.text_segment_cmd_index.?,
1511 "__const",
1512 sect.size,
1513 sect.@"align",
1514 .{},
1515 );
14091516 }
14101517
14111518 break :blk .{
......@@ -1417,8 +1524,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14171524
14181525 if (mem.eql(u8, segname, "__DATA_CONST")) {
14191526 if (self.data_const_section_index == null) {
1420 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
1421 try data_const_seg.addSection(self.base.allocator, "__const", .{});
1527 self.data_const_section_index = try self.allocateSection(
1528 self.data_const_segment_cmd_index.?,
1529 "__const",
1530 sect.size,
1531 sect.@"align",
1532 .{},
1533 );
14221534 }
14231535
14241536 break :blk .{
......@@ -1430,8 +1542,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14301542 if (mem.eql(u8, segname, "__DATA")) {
14311543 if (mem.eql(u8, sectname, "__const")) {
14321544 if (self.data_const_section_index == null) {
1433 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
1434 try data_const_seg.addSection(self.base.allocator, "__const", .{});
1545 self.data_const_section_index = try self.allocateSection(
1546 self.data_const_segment_cmd_index.?,
1547 "__const",
1548 sect.size,
1549 sect.@"align",
1550 .{},
1551 );
14351552 }
14361553
14371554 break :blk .{
......@@ -1440,8 +1557,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14401557 };
14411558 } else if (mem.eql(u8, sectname, "__cfstring")) {
14421559 if (self.objc_cfstring_section_index == null) {
1443 self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len);
1444 try data_const_seg.addSection(self.base.allocator, "__cfstring", .{});
1560 self.objc_cfstring_section_index = try self.allocateSection(
1561 self.data_const_segment_cmd_index.?,
1562 "__cfstring",
1563 sect.size,
1564 sect.@"align",
1565 .{},
1566 );
14451567 }
14461568
14471569 break :blk .{
......@@ -1450,8 +1572,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14501572 };
14511573 } else if (mem.eql(u8, sectname, "__objc_classlist")) {
14521574 if (self.objc_classlist_section_index == null) {
1453 self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len);
1454 try data_const_seg.addSection(self.base.allocator, "__objc_classlist", .{});
1575 self.objc_classlist_section_index = try self.allocateSection(
1576 self.data_const_segment_cmd_index.?,
1577 "__objc_classlist",
1578 sect.size,
1579 sect.@"align",
1580 .{},
1581 );
14551582 }
14561583
14571584 break :blk .{
......@@ -1460,8 +1587,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14601587 };
14611588 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
14621589 if (self.objc_imageinfo_section_index == null) {
1463 self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len);
1464 try data_const_seg.addSection(self.base.allocator, "__objc_imageinfo", .{});
1590 self.objc_imageinfo_section_index = try self.allocateSection(
1591 self.data_const_segment_cmd_index.?,
1592 "__objc_imageinfo",
1593 sect.size,
1594 sect.@"align",
1595 .{},
1596 );
14651597 }
14661598
14671599 break :blk .{
......@@ -1470,8 +1602,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14701602 };
14711603 } else if (mem.eql(u8, sectname, "__objc_const")) {
14721604 if (self.objc_const_section_index == null) {
1473 self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len);
1474 try data_seg.addSection(self.base.allocator, "__objc_const", .{});
1605 self.objc_const_section_index = try self.allocateSection(
1606 self.data_segment_cmd_index.?,
1607 "__objc_const",
1608 sect.size,
1609 sect.@"align",
1610 .{},
1611 );
14751612 }
14761613
14771614 break :blk .{
......@@ -1480,8 +1617,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14801617 };
14811618 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
14821619 if (self.objc_classrefs_section_index == null) {
1483 self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len);
1484 try data_seg.addSection(self.base.allocator, "__objc_classrefs", .{});
1620 self.objc_classrefs_section_index = try self.allocateSection(
1621 self.data_segment_cmd_index.?,
1622 "__objc_classrefs",
1623 sect.size,
1624 sect.@"align",
1625 .{},
1626 );
14851627 }
14861628
14871629 break :blk .{
......@@ -1490,8 +1632,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
14901632 };
14911633 } else if (mem.eql(u8, sectname, "__objc_data")) {
14921634 if (self.objc_data_section_index == null) {
1493 self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len);
1494 try data_seg.addSection(self.base.allocator, "__objc_data", .{});
1635 self.objc_data_section_index = try self.allocateSection(
1636 self.data_segment_cmd_index.?,
1637 "__objc_data",
1638 sect.size,
1639 sect.@"align",
1640 .{},
1641 );
14951642 }
14961643
14971644 break :blk .{
......@@ -1500,8 +1647,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
15001647 };
15011648 } else {
15021649 if (self.data_section_index == null) {
1503 self.data_section_index = @intCast(u16, data_seg.sections.items.len);
1504 try data_seg.addSection(self.base.allocator, "__data", .{});
1650 self.data_section_index = try self.allocateSection(
1651 self.data_segment_cmd_index.?,
1652 "__data",
1653 sect.size,
1654 sect.@"align",
1655 .{},
1656 );
15051657 }
15061658
15071659 break :blk .{
......@@ -1522,563 +1674,605 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
15221674 else => break :blk null,
15231675 }
15241676 };
1525
1526 if (res) |match| {
1527 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
1528 }
1529
15301677 return res;
15311678}
15321679
1533fn sortSections(self: *MachO) !void {
1534 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
1535 defer text_index_mapping.deinit();
1536 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
1537 defer data_const_index_mapping.deinit();
1538 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
1539 defer data_index_mapping.deinit();
1540
1541 {
1542 // __TEXT segment
1543 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1544 var sections = seg.sections.toOwnedSlice(self.base.allocator);
1545 defer self.base.allocator.free(sections);
1546 try seg.sections.ensureCapacity(self.base.allocator, sections.len);
1547
1548 const indices = &[_]*?u16{
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 }
1680pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*Atom {
1681 const code = try self.base.allocator.alloc(u8, size);
1682 defer self.base.allocator.free(code);
1683 mem.set(u8, code, 0);
1684
1685 const atom = try self.base.allocator.create(Atom);
1686 errdefer self.base.allocator.destroy(atom);
1687 atom.* = Atom.empty;
1688 atom.local_sym_index = local_sym_index;
1689 atom.size = size;
1690 atom.alignment = alignment;
1691 try atom.code.appendSlice(self.base.allocator, code);
1692 try self.managed_atoms.append(self.base.allocator, atom);
1693
1694 return atom;
1695}
15721696
1573 {
1574 // __DATA_CONST segment
1575 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1576 var sections = seg.sections.toOwnedSlice(self.base.allocator);
1577 defer self.base.allocator.free(sections);
1578 try seg.sections.ensureCapacity(self.base.allocator, sections.len);
1579
1580 const indices = &[_]*?u16{
1581 &self.got_section_index,
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 }
1697pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
1698 const seg = self.load_commands.items[match.seg].Segment;
1699 const sect = seg.sections.items[match.sect];
1700 const sym = self.locals.items[atom.local_sym_index];
1701 const file_offset = sect.offset + sym.n_value - sect.addr;
1702 try atom.resolveRelocs(self);
1703 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
1704 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1705}
15991706
1600 {
1601 // __DATA segment
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 }
1707fn allocateLocalSymbols(self: *MachO, match: MatchingSection, offset: i64) !void {
1708 var atom = self.atoms.get(match) orelse return;
16311709
1632 {
1633 var transient: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{};
1634 try transient.ensureCapacity(self.base.allocator, self.blocks.count());
1710 while (true) {
1711 const atom_sym = &self.locals.items[atom.local_sym_index];
1712 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
16351713
1636 var it = self.blocks.iterator();
1637 while (it.next()) |entry| {
1638 const old = entry.key_ptr.*;
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.*);
1714 for (atom.aliases.items) |index| {
1715 const alias_sym = &self.locals.items[index];
1716 alias_sym.n_value = @intCast(u64, @intCast(i64, alias_sym.n_value) + offset);
16491717 }
16501718
1651 self.blocks.clearAndFree(self.base.allocator);
1652 self.blocks.deinit(self.base.allocator);
1653 self.blocks = transient;
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);
1719 for (atom.contained.items) |sym_at_off| {
1720 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
1721 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
16821722 }
1723
1724 if (atom.prev) |prev| {
1725 atom = prev;
1726 } else break;
16831727 }
16841728}
16851729
1686fn allocateTextSegment(self: *MachO) !void {
1687 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1688 const nstubs = @intCast(u32, self.stubs.items.len);
1730fn allocateGlobalSymbols(self: *MachO) !void {
1731 var sym_it = self.symbol_resolver.valueIterator();
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;
1691 seg.inner.fileoff = 0;
1692 seg.inner.vmaddr = base_vmaddr;
1735 assert(resolv.local_sym_index != 0);
1736 const local_sym = self.locals.items[resolv.local_sym_index];
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 sizes
1695 const stubs = &seg.sections.items[self.stubs_section_index.?];
1696 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
1697 stubs.size += nstubs * stubs.reserved2;
1744fn writeAtoms(self: *MachO) !void {
1745 var buffer = std.ArrayList(u8).init(self.base.allocator);
1746 defer buffer.deinit();
1747 var file_offset: ?u64 = null;
16981748
1699 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1700 .x86_64 => 10,
1701 .aarch64 => 3 * @sizeOf(u32),
1702 else => unreachable,
1703 };
1704 stub_helper.size += nstubs * stub_size;
1749 var it = self.atoms.iterator();
1750 while (it.next()) |entry| {
1751 const match = entry.key_ptr.*;
1752 const seg = self.load_commands.items[match.seg].Segment;
1753 const sect = seg.sections.items[match.sect];
1754 var atom: *Atom = entry.value_ptr.*;
17051755
1706 var sizeofcmds: u64 = 0;
1707 for (self.load_commands.items) |lc| {
1708 sizeofcmds += lc.cmdsize();
1709 }
1756 log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
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.
1714 var min_alignment: u32 = 0;
1715 for (seg.sections.items) |sect| {
1716 const alignment = try math.powi(u32, 2, sect.@"align");
1717 min_alignment = math.max(min_alignment, alignment);
1718 }
1762 while (true) {
1763 if (atom.dirty) {
1764 const atom_sym = self.locals.items[atom.local_sym_index];
1765 const padding_size: u64 = if (atom.next) |next| blk: {
1766 const next_sym = self.locals.items[next.local_sym_index];
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);
1721 const last_sect_idx = seg.sections.items.len - 1;
1722 const last_sect = seg.sections.items[last_sect_idx];
1723 const shift: u32 = blk: {
1724 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
1725 const factor = @divTrunc(diff, min_alignment);
1726 break :blk @intCast(u32, factor * min_alignment);
1727 };
1781 if (file_offset == null) {
1782 file_offset = sect.offset + atom_sym.n_value - sect.addr;
1783 }
1784 atom.dirty = false;
1785 } else {
1786 if (file_offset) |off| {
1787 try self.base.file.?.pwriteAll(buffer.items, off);
1788 }
1789 file_offset = null;
1790 buffer.clearRetainingCapacity();
1791 }
17281792
1729 if (shift > 0) {
1730 for (seg.sections.items) |*sect| {
1731 sect.offset += shift;
1732 sect.addr += shift;
1793 if (atom.next) |next| {
1794 atom = next;
1795 } else {
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 }
17331803 }
17341804 }
17351805}
17361806
1737fn allocateDataConstSegment(self: *MachO) !void {
1738 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1739 const nentries = @intCast(u32, self.got_entries.items.len);
1740
1741 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1742 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
1743 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
1807pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
1808 const local_sym_index = @intCast(u32, self.locals.items.len);
1809 try self.locals.append(self.base.allocator, .{
1810 .n_strx = try self.makeString("l_zld_got_entry"),
1811 .n_type = macho.N_SECT,
1812 .n_sect = 0,
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 size
1746 const got = &seg.sections.items[self.got_section_index.?];
1747 got.size += nentries * @sizeOf(u64);
1748
1749 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
1750}
1751
1752fn allocateDataSegment(self: *MachO) !void {
1753 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1754 const nstubs = @intCast(u32, self.stubs.items.len);
1755
1756 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1757 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
1758 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
1759
1760 // Set la_symbol_ptr and data size
1761 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];
1762 const data = &seg.sections.items[self.data_section_index.?];
1763 la_symbol_ptr.size += nstubs * @sizeOf(u64);
1764 data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder
1765
1766 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
1843fn createDyldPrivateAtom(self: *MachO) !void {
1844 if (self.dyld_private_atom != null) return;
1845 const local_sym_index = @intCast(u32, self.locals.items.len);
1846 const sym = try self.locals.addOne(self.base.allocator);
1847 sym.* = .{
1848 .n_strx = try self.makeString("l_zld_dyld_private"),
1849 .n_type = macho.N_SECT,
1850 .n_sect = 0,
1851 .n_desc = 0,
1852 .n_value = 0,
1853 };
1854 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
1855 self.dyld_private_atom = atom;
1856 const match = MatchingSection{
1857 .seg = self.data_segment_cmd_index.?,
1858 .sect = self.data_section_index.?,
1859 };
1860 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
1861 sym.n_value = vaddr;
1862 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1863 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
17671864}
17681865
1769fn allocateLinkeditSegment(self: *MachO) void {
1770 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1771 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1772 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
1773 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
1866fn createStubHelperPreambleAtom(self: *MachO) !void {
1867 if (self.stub_helper_preamble_atom != null) return;
1868 const arch = self.base.options.target.cpu.arch;
1869 const size: u64 = switch (arch) {
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 });
17742008}
17752009
1776fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
1777 const seg = &self.load_commands.items[index].Segment;
2010pub fn createStubHelperAtom(self: *MachO) !*Atom {
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.
1780 var start: u64 = offset;
1781 for (seg.sections.items) |*sect| {
1782 const alignment = try math.powi(u32, 2, sect.@"align");
1783 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
1784 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
1785 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
1786 sect.addr = seg.inner.vmaddr + start_aligned;
1787 start = end_aligned;
2033 switch (arch) {
2034 .x86_64 => {
2035 // pushq
2036 atom.code.items[0] = 0x68;
2037 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2038 // jmpq
2039 atom.code.items[5] = 0xe9;
2040 atom.relocs.appendAssumeCapacity(.{
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,
17882071 }
17892072
1790 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size);
1791 seg.inner.filesize = seg_size_aligned;
1792 seg.inner.vmsize = seg_size_aligned;
2073 return atom;
17932074}
17942075
1795fn allocateTextBlocks(self: *MachO) !void {
1796 var it = self.blocks.iterator();
1797 while (it.next()) |entry| {
1798 const match = entry.key_ptr.*;
1799 var block: *TextBlock = entry.value_ptr.*;
1800
1801 // Find the first block
1802 while (block.prev) |prev| {
1803 block = prev;
1804 }
1805
1806 const seg = self.load_commands.items[match.seg].Segment;
1807 const sect = seg.sections.items[match.sect];
1808
1809 var base_addr: u64 = sect.addr;
1810 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1811
1812 log.debug(" within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
1813 log.debug(" {}", .{sect});
1814
1815 while (true) {
1816 const block_alignment = try math.powi(u32, 2, block.alignment);
1817 base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment);
1818
1819 const sym = &self.locals.items[block.local_sym_index];
1820 sym.n_value = base_addr;
1821 sym.n_sect = n_sect;
2076pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {
2077 const local_sym_index = @intCast(u32, self.locals.items.len);
2078 try self.locals.append(self.base.allocator, .{
2079 .n_strx = try self.makeString("l_zld_lazy_ptr"),
2080 .n_type = macho.N_SECT,
2081 .n_sect = 0,
2082 .n_desc = 0,
2083 .n_value = 0,
2084 });
2085 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2086 try atom.relocs.append(self.base.allocator, .{
2087 .offset = 0,
2088 .where = .local,
2089 .where_index = stub_sym_index,
2090 .payload = .{
2091 .unsigned = .{
2092 .subtractor = null,
2093 .addend = 0,
2094 .is_64bit = true,
2095 },
2096 },
2097 });
2098 try atom.rebases.append(self.base.allocator, 0);
2099 try atom.lazy_bindings.append(self.base.allocator, .{
2100 .local_sym_index = lazy_binding_sym_index,
2101 .offset = 0,
2102 });
2103 return atom;
2104}
18222105
1823 log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
1824 self.getString(sym.n_strx),
1825 base_addr,
1826 base_addr + block.size,
1827 block.size,
1828 block.alignment,
2106pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2107 const arch = self.base.options.target.cpu.arch;
2108 const alignment: u2 = switch (arch) {
2109 .x86_64 => 0,
2110 .aarch64 => 2,
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 },
18292139 });
1830
1831 // Update each alias (if any)
1832 for (block.aliases.items) |index| {
1833 const alias_sym = &self.locals.items[index];
1834 alias_sym.n_value = base_addr;
1835 alias_sym.n_sect = n_sect;
1836 }
1837
1838 // Update each symbol contained within the TextBlock
1839 for (block.contained.items) |sym_at_off| {
1840 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
1841 contained_sym.n_value = base_addr + sym_at_off.offset;
1842 contained_sym.n_sect = n_sect;
1843 }
1844
1845 base_addr += block.size;
1846
1847 if (block.next) |next| {
1848 block = next;
1849 } else break;
1850 }
1851 }
1852
1853 // Update globals
1854 {
1855 var sym_it = self.symbol_resolver.valueIterator();
1856 while (sym_it.next()) |resolv| {
1857 if (resolv.where != .global) continue;
1858
1859 assert(resolv.local_sym_index != 0);
1860 const local_sym = self.locals.items[resolv.local_sym_index];
1861 const sym = &self.globals.items[resolv.where_index];
1862 sym.n_value = local_sym.n_value;
1863 sym.n_sect = local_sym.n_sect;
1864 }
2140 },
2141 .aarch64 => {
2142 try atom.relocs.ensureTotalCapacity(self.base.allocator, 2);
2143 // adrp x16, pages
2144 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2145 atom.relocs.appendAssumeCapacity(.{
2146 .offset = 0,
2147 .where = .local,
2148 .where_index = laptr_sym_index,
2149 .payload = .{
2150 .page = .{
2151 .kind = .page,
2152 .addend = 0,
2153 },
2154 },
2155 });
2156 // ldr x16, x16, offset
2157 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(.x16, .{
2158 .register = .{
2159 .rn = .x16,
2160 .offset = aarch64.Instruction.LoadStoreOffset.imm(0),
2161 },
2162 }).toU32());
2163 atom.relocs.appendAssumeCapacity(.{
2164 .offset = 4,
2165 .where = .local,
2166 .where_index = laptr_sym_index,
2167 .payload = .{
2168 .page_off = .{
2169 .kind = .page,
2170 .addend = 0,
2171 .op_kind = .load,
2172 },
2173 },
2174 });
2175 // br x16
2176 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());
2177 },
2178 else => unreachable,
18652179 }
2180 return atom;
18662181}
18672182
1868fn writeTextBlocks(self: *MachO) !void {
1869 var it = self.blocks.iterator();
1870 while (it.next()) |entry| {
1871 const match = entry.key_ptr.*;
1872 var block: *TextBlock = entry.value_ptr.*;
1873
1874 while (block.prev) |prev| {
1875 block = prev;
1876 }
1877
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;
2183fn createTentativeDefAtoms(self: *MachO) !void {
2184 if (self.tentatives.count() == 0) return;
2185 // Convert any tentative definition into a regular symbol and allocate
2186 // text blocks for each tentative defintion.
2187 while (self.tentatives.popOrNull()) |entry| {
2188 const match = MatchingSection{
2189 .seg = self.data_segment_cmd_index.?,
2190 .sect = self.bss_section_index.?,
2191 };
2192 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
18922193
1893 while (true) {
1894 const block_alignment = try math.powi(u32, 2, block.alignment);
1895 const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment);
2194 const global_sym = &self.globals.items[entry.key];
2195 const size = global_sym.n_value;
2196 const alignment = (global_sym.n_desc >> 8) & 0x0f;
18962197
1897 const sym = self.locals.items[block.local_sym_index];
1898 log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
1899 self.getString(sym.n_strx),
1900 aligned_base_off,
1901 aligned_base_off + block.size,
1902 block.size,
1903 block.alignment,
1904 });
2198 global_sym.n_value = 0;
2199 global_sym.n_desc = 0;
2200 global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
19052201
1906 try block.resolveRelocs(self);
1907 mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items);
2202 const local_sym_index = @intCast(u32, self.locals.items.len);
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 out
1910 const padding_len = aligned_base_off - base_off;
1911 mem.set(u8, code[base_off..][0..padding_len], 0);
2212 const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable;
2213 resolv.local_sym_index = local_sym_index;
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| {
1916 block = next;
1917 } else break;
1918 }
2223fn createDsoHandleAtom(self: *MachO) !void {
2224 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
2225 .bytes = &self.strtab,
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);
1921 }
2230 const undef = &self.undefs.items[resolv.where_index];
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);
1924 }
1925}
2249 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
19262250
1927fn writeStubHelperCommon(self: *MachO) !void {
1928 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1929 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
1930 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1931 const got = &data_const_segment.sections.items[self.got_section_index.?];
1932 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1933 const data = &data_segment.sections.items[self.data_section_index.?];
1934
1935 self.stub_helper_stubs_start_off = blk: {
1936 switch (self.base.options.target.cpu.arch) {
1937 .x86_64 => {
1938 const code_size = 15;
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 };
2251 undef.* = .{
2252 .n_strx = 0,
2253 .n_type = macho.N_UNDF,
2254 .n_sect = 0,
2255 .n_desc = 0,
2256 .n_value = 0,
2257 };
2258 resolv.* = .{
2259 .where = .global,
2260 .where_index = global_sym_index,
2261 .local_sym_index = local_sym_index,
2262 };
20662263
2067 for (self.stubs.items) |_, i| {
2068 const index = @intCast(u32, i);
2069 // TODO weak bound pointers
2070 try self.writeLazySymbolPointer(index);
2071 try self.writeStub(index);
2072 try self.writeStubInStubHelper(index);
2264 // We create an empty atom for this symbol.
2265 // TODO perhaps we should special-case special symbols? Create a separate
2266 // linked list of atoms?
2267 const atom = try self.createEmptyAtom(local_sym_index, 0, 0);
2268 const sym = &self.locals.items[local_sym_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.
20732272 }
20742273}
20752274
2076fn resolveSymbolsInObject(
2077 self: *MachO,
2078 object_id: u16,
2079 tentatives: *std.AutoArrayHashMap(u32, void),
2080 unresolved: *std.AutoArrayHashMap(u32, void),
2081) !void {
2275fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
20822276 const object = &self.objects.items[object_id];
20832277
20842278 log.debug("resolving symbols in '{s}'", .{object.name});
......@@ -2150,7 +2344,7 @@ fn resolveSymbolsInObject(
21502344 const global = &self.globals.items[resolv.where_index];
21512345
21522346 if (symbolIsTentative(global.*)) {
2153 _ = tentatives.fetchSwapRemove(resolv.where_index);
2347 _ = self.tentatives.fetchSwapRemove(resolv.where_index);
21542348 } else if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and
21552349 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
21562350 {
......@@ -2168,15 +2362,7 @@ fn resolveSymbolsInObject(
21682362 continue;
21692363 },
21702364 .undef => {
2171 const undef = &self.undefs.items[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);
2365 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
21802366 },
21812367 }
21822368
......@@ -2210,7 +2396,7 @@ fn resolveSymbolsInObject(
22102396 .where_index = global_sym_index,
22112397 .file = object_id,
22122398 });
2213 _ = try tentatives.getOrPut(global_sym_index);
2399 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
22142400 continue;
22152401 };
22162402
......@@ -2234,7 +2420,7 @@ fn resolveSymbolsInObject(
22342420 .n_desc = sym.n_desc,
22352421 .n_value = sym.n_value,
22362422 });
2237 _ = try tentatives.getOrPut(global_sym_index);
2423 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
22382424 resolv.* = .{
22392425 .where = .global,
22402426 .where_index = global_sym_index,
......@@ -2247,7 +2433,7 @@ fn resolveSymbolsInObject(
22472433 .n_desc = 0,
22482434 .n_value = 0,
22492435 };
2250 _ = unresolved.fetchSwapRemove(resolv.where_index);
2436 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
22512437 },
22522438 }
22532439 } else {
......@@ -2267,27 +2453,17 @@ fn resolveSymbolsInObject(
22672453 .where_index = undef_sym_index,
22682454 .file = object_id,
22692455 });
2270 _ = try unresolved.getOrPut(undef_sym_index);
2456 try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none);
22712457 }
22722458 }
22732459}
22742460
2275fn resolveSymbols(self: *MachO) !void {
2276 var tentatives = std.AutoArrayHashMap(u32, void).init(self.base.allocator);
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 }
2461fn resolveSymbolsInArchives(self: *MachO) !void {
2462 if (self.archives.items.len == 0) return;
22862463
2287 // Second pass, resolve symbols in static libraries.
22882464 var next_sym: usize = 0;
2289 loop: while (next_sym < unresolved.count()) {
2290 const sym = self.undefs.items[unresolved.keys()[next_sym]];
2465 loop: while (next_sym < self.unresolved.count()) {
2466 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
22912467 const sym_name = self.getString(sym.n_strx);
22922468
22932469 for (self.archives.items) |archive| {
......@@ -2301,102 +2477,21 @@ fn resolveSymbols(self: *MachO) !void {
23012477 const object_id = @intCast(u16, self.objects.items.len);
23022478 const object = try self.objects.addOne(self.base.allocator);
23032479 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
23062482 continue :loop;
23072483 }
23082484
23092485 next_sym += 1;
23102486 }
2487}
23112488
2312 // Convert any tentative definition into a regular symbol and allocate
2313 // text blocks for each tentative defintion.
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 }
2489fn resolveSymbolsInDylibs(self: *MachO) !void {
2490 if (self.dylibs.items.len == 0) return;
23962491
2397 next_sym = 0;
2398 loop: while (next_sym < unresolved.count()) {
2399 const sym = self.undefs.items[unresolved.keys()[next_sym]];
2492 var next_sym: usize = 0;
2493 loop: while (next_sym < self.unresolved.count()) {
2494 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
24002495 const sym_name = self.getString(sym.n_strx);
24012496
24022497 for (self.dylibs.items) |dylib, id| {
......@@ -2404,6 +2499,7 @@ fn resolveSymbols(self: *MachO) !void {
24042499
24052500 const dylib_id = @intCast(u16, id);
24062501 if (!self.referenced_dylibs.contains(dylib_id)) {
2502 try self.addLoadDylibLC(dylib_id);
24072503 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
24082504 }
24092505
......@@ -2413,3115 +2509,1859 @@ fn resolveSymbols(self: *MachO) !void {
24132509 undef.n_type |= macho.N_EXT;
24142510 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
24182565 continue :loop;
24192566 }
24202567
24212568 next_sym += 1;
24222569 }
2570}
24232571
2424 // Fourth pass, handle synthetic symbols and flag any undefined references.
2425 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
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);
2572fn resolveDyldStubBinder(self: *MachO) !void {
2573 if (self.dyld_stub_binder_index != null) return;
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.* = .{
2453 .n_strx = 0,
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 };
2591 for (self.dylibs.items) |dylib, id| {
2592 if (!dylib.symbols.contains(sym_name)) continue;
24642593
2465 // We create an empty atom for this symbol.
2466 // TODO perhaps we should special-case special symbols? Create a separate
2467 // linked list of atoms?
2468 const block = try self.base.allocator.create(TextBlock);
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);
2594 const dylib_id = @intCast(u16, id);
2595 if (!self.referenced_dylibs.contains(dylib_id)) {
2596 try self.addLoadDylibLC(dylib_id);
2597 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
24812598 }
2482 }
24832599
2484 for (unresolved.keys()) |index| {
2485 const sym = self.undefs.items[index];
2486 const sym_name = self.getString(sym.n_strx);
2487 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
2600 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
2601 sym.n_type |= macho.N_EXT;
2602 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
2603 self.dyld_stub_binder_index = sym_index;
24882604
2489 log.err("undefined reference to symbol '{s}'", .{sym_name});
2490 log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name});
2605 break;
24912606 }
24922607
2493 if (unresolved.count() > 0)
2608 if (self.dyld_stub_binder_index == null) {
2609 log.err("undefined reference to symbol '{s}'", .{sym_name});
24942610 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 });
24952629}
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
24982647 for (self.objects.items) |*object, object_id| {
2499 try object.parseTextBlocks(self.base.allocator, @intCast(u16, object_id), self);
2500 }
2501}
2648 if (object.analyzed) continue;
25022649
2503fn populateMetadata(self: *MachO) !void {
2504 if (self.pagezero_segment_cmd_index == null) {
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 }
2650 var atoms_in_objects = try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_id), self);
2651 defer atoms_in_objects.deinit();
25122652
2513 if (self.text_segment_cmd_index == null) {
2514 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2515 try self.load_commands.append(self.base.allocator, .{
2516 .Segment = SegmentCommand.empty("__TEXT", .{
2517 .vmaddr = 0x100000000, // always starts at 4GB
2518 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
2519 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
2520 }),
2521 });
2522 }
2653 var it = atoms_in_objects.iterator();
2654 while (it.next()) |entry| {
2655 const match = entry.key_ptr.*;
2656 const last_atom = entry.value_ptr.*;
2657 var atom = last_atom;
2658
2659 const metadata = try section_metadata.getOrPut(match);
2660 if (!metadata.found_existing) {
2661 metadata.value_ptr.* = .{
2662 .size = 0,
2663 .alignment = 0,
2664 };
2665 }
25232666
2524 if (self.text_section_index == null) {
2525 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2526 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
2527 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
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 }
2667 while (true) {
2668 const alignment = try math.powi(u32, 2, atom.alignment);
2669 metadata.value_ptr.size += mem.alignForwardGeneric(u64, atom.size, alignment);
2670 metadata.value_ptr.alignment = math.max(metadata.value_ptr.alignment, atom.alignment);
25412671
2542 if (self.stubs_section_index == null) {
2543 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2544 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
2545 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
2546 .x86_64 => 0,
2547 .aarch64 => 2,
2548 else => unreachable, // unhandled architecture type
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 }
2672 const sym = self.locals.items[atom.local_sym_index];
2673 log.debug(" {s}: n_value=0x{x}, size=0x{x}, alignment=0x{x}", .{
2674 self.getString(sym.n_strx),
2675 sym.n_value,
2676 atom.size,
2677 atom.alignment,
2678 });
25652679
2566 if (self.stub_helper_section_index == null) {
2567 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2568 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
2569 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
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 }
2680 if (atom.prev) |prev| {
2681 atom = prev;
2682 } else break;
2683 }
25892684
2590 if (self.data_const_segment_cmd_index == null) {
2591 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2592 try self.load_commands.append(self.base.allocator, .{
2593 .Segment = SegmentCommand.empty("__DATA_CONST", .{
2594 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2595 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2596 }),
2597 });
2598 }
2685 if (parsed_atoms.getPtr(match)) |last| {
2686 last.*.next = atom;
2687 atom.prev = last.*;
2688 last.* = atom;
2689 }
2690 _ = try parsed_atoms.put(match, last_atom);
25992691
2600 if (self.got_section_index == null) {
2601 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2602 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
2603 try data_const_seg.addSection(self.base.allocator, "__got", .{
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 }
2692 if (!first_atoms.contains(match)) {
2693 try first_atoms.putNoClobber(match, atom);
2694 }
2695 }
26122696
2613 if (self.data_segment_cmd_index == null) {
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 });
2697 object.analyzed = true;
26212698 }
26222699
2623 if (self.la_symbol_ptr_section_index == null) {
2624 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2625 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
2626 try data_seg.addSection(self.base.allocator, "__la_symbol_ptr", .{
2627 .@"align" = 3, // 2^3 = @sizeOf(u64)
2628 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2629 });
2630 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{
2631 .seg = self.data_segment_cmd_index.?,
2632 .sect = self.la_symbol_ptr_section_index.?,
2700 var it = section_metadata.iterator();
2701 while (it.next()) |entry| {
2702 const match = entry.key_ptr.*;
2703 const metadata = entry.value_ptr.*;
2704 const seg = &self.load_commands.items[match.seg].Segment;
2705 const sect = &seg.sections.items[match.sect];
2706 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
2707 commands.segmentName(sect.*),
2708 commands.sectionName(sect.*),
2709 metadata.size,
2710 metadata.alignment,
26332711 });
2634 }
26352712
2636 if (self.data_section_index == null) {
2637 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2638 self.data_section_index = @intCast(u16, data_seg.sections.items.len);
2639 try data_seg.addSection(self.base.allocator, "__data", .{
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 }
2713 const sect_size = if (self.atoms.get(match)) |last| blk: {
2714 const last_atom_sym = self.locals.items[last.local_sym_index];
2715 break :blk last_atom_sym.n_value + last.size - sect.addr;
2716 } else 0;
26472717
2648 if (self.linkedit_segment_cmd_index == null) {
2649 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2650 try self.load_commands.append(self.base.allocator, .{
2651 .Segment = SegmentCommand.empty("__LINKEDIT", .{
2652 .maxprot = macho.VM_PROT_READ,
2653 .initprot = macho.VM_PROT_READ,
2654 }),
2655 });
2656 }
2718 sect.@"align" = math.max(sect.@"align", metadata.alignment);
2719 const needed_size = @intCast(u32, metadata.size + sect_size);
2720 try self.growSection(match, needed_size);
2721 sect.size = needed_size;
26572722
2658 if (self.dyld_info_cmd_index == null) {
2659 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
2660 try self.load_commands.append(self.base.allocator, .{
2661 .DyldInfoOnly = .{
2662 .cmd = macho.LC_DYLD_INFO_ONLY,
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 }
2723 var base_vaddr = if (self.atoms.get(match)) |last| blk: {
2724 const last_atom_sym = self.locals.items[last.local_sym_index];
2725 break :blk last_atom_sym.n_value + last.size;
2726 } else sect.addr;
2727 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
26772728
2678 if (self.symtab_cmd_index == null) {
2679 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2680 try self.load_commands.append(self.base.allocator, .{
2681 .Symtab = .{
2682 .cmd = macho.LC_SYMTAB,
2683 .cmdsize = @sizeOf(macho.symtab_command),
2684 .symoff = 0,
2685 .nsyms = 0,
2686 .stroff = 0,
2687 .strsize = 0,
2688 },
2689 });
2729 var atom = first_atoms.get(match).?;
2730 while (true) {
2731 const alignment = try math.powi(u32, 2, atom.alignment);
2732 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
2733
2734 const sym = &self.locals.items[atom.local_sym_index];
2735 sym.n_value = base_vaddr;
2736 sym.n_sect = n_sect;
2737
2738 log.debug(" {s}: start=0x{x}, end=0x{x}, size=0x{x}, alignment=0x{x}", .{
2739 self.getString(sym.n_strx),
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).?);
26902774 }
2775}
26912776
2692 if (self.dysymtab_cmd_index == null) {
2693 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2694 try self.load_commands.append(self.base.allocator, .{
2695 .Dysymtab = .{
2696 .cmd = macho.LC_DYSYMTAB,
2697 .cmdsize = @sizeOf(macho.dysymtab_command),
2698 .ilocalsym = 0,
2699 .nlocalsym = 0,
2700 .iextdefsym = 0,
2701 .nextdefsym = 0,
2702 .iundefsym = 0,
2703 .nundefsym = 0,
2704 .tocoff = 0,
2705 .ntoc = 0,
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 }
2777fn addLoadDylibLC(self: *MachO, id: u16) !void {
2778 const dylib = self.dylibs.items[id];
2779 const dylib_id = dylib.id orelse unreachable;
2780 var dylib_cmd = try commands.createLoadDylibCommand(
2781 self.base.allocator,
2782 dylib_id.name,
2783 dylib_id.timestamp,
2784 dylib_id.current_version,
2785 dylib_id.compatibility_version,
2786 );
2787 errdefer dylib_cmd.deinit(self.base.allocator);
2788 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
2789 self.load_commands_dirty = true;
2790}
27192791
2720 if (self.dylinker_cmd_index == null) {
2721 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
2722 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2723 u64,
2724 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
2725 @sizeOf(u64),
2726 ));
2727 var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
2728 .cmd = macho.LC_LOAD_DYLINKER,
2729 .cmdsize = cmdsize,
2730 .name = @sizeOf(macho.dylinker_command),
2731 });
2732 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
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 }
2792fn addCodeSignatureLC(self: *MachO) !void {
2793 if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;
2794 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2795 try self.load_commands.append(self.base.allocator, .{
2796 .LinkeditData = .{
2797 .cmd = macho.LC_CODE_SIGNATURE,
2798 .cmdsize = @sizeOf(macho.linkedit_data_command),
2799 .dataoff = 0,
2800 .datasize = 0,
2801 },
2802 });
2803 self.load_commands_dirty = true;
2804}
27372805
2738 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
2739 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
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 }
2806fn setEntryPoint(self: *MachO) !void {
2807 if (self.base.options.output_mode != .Exe) return;
27492808
2750 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {
2751 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
2752 const install_name = try std.fmt.allocPrint(self.base.allocator, "@rpath/{s}", .{
2753 self.base.options.emit.?.sub_path,
2754 });
2755 defer self.base.allocator.free(install_name);
2756 var dylib_cmd = try commands.createLoadDylibCommand(
2757 self.base.allocator,
2758 install_name,
2759 2,
2760 0x10000, // TODO forward user-provided versions
2761 0x10000,
2762 );
2763 errdefer dylib_cmd.deinit(self.base.allocator);
2764 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
2765 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
2809 // TODO we should respect the -entry flag passed in by the user to set a custom
2810 // entrypoint. For now, assume default of `_main`.
2811 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2812 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
2813 .bytes = &self.strtab,
2814 }) orelse {
2815 log.err("'_main' export not found", .{});
2816 return error.MissingMainEntrypoint;
2817 };
2818 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
2819 assert(resolv.where == .global);
2820 const sym = self.globals.items[resolv.where_index];
2821 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2822 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
2823 ec.stacksize = self.base.options.stack_size_override orelse 0;
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);
27662831 }
27672832
2768 if (self.source_version_cmd_index == null) {
2769 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
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 });
2833 if (self.d_sym) |*ds| {
2834 ds.deinit(self.base.allocator);
27772835 }
27782836
2779 if (self.build_version_cmd_index == null) {
2780 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
2781 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2782 u64,
2783 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
2784 @sizeOf(u64),
2785 ));
2786 const ver = self.base.options.target.os.version_range.semver.min;
2787 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
2788 const is_simulator_abi = self.base.options.target.abi == .simulator;
2789 var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
2790 .cmd = macho.LC_BUILD_VERSION,
2791 .cmdsize = cmdsize,
2792 .platform = switch (self.base.options.target.os.tag) {
2793 .macos => macho.PLATFORM_MACOS,
2794 .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS,
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 });
2837 self.section_ordinals.deinit(self.base.allocator);
2838 self.got_entries_map.deinit(self.base.allocator);
2839 self.stubs_map.deinit(self.base.allocator);
2840 self.strtab_dir.deinit(self.base.allocator);
2841 self.strtab.deinit(self.base.allocator);
2842 self.undefs.deinit(self.base.allocator);
2843 self.globals.deinit(self.base.allocator);
2844 self.globals_free_list.deinit(self.base.allocator);
2845 self.locals.deinit(self.base.allocator);
2846 self.locals_free_list.deinit(self.base.allocator);
2847 self.symbol_resolver.deinit(self.base.allocator);
2848 self.unresolved.deinit(self.base.allocator);
2849 self.tentatives.deinit(self.base.allocator);
2850
2851 for (self.objects.items) |*object| {
2852 object.deinit(self.base.allocator);
28112853 }
2854 self.objects.deinit(self.base.allocator);
28122855
2813 if (self.uuid_cmd_index == null) {
2814 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
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 });
2856 for (self.archives.items) |*archive| {
2857 archive.deinit(self.base.allocator);
28222858 }
2823}
2859 self.archives.deinit(self.base.allocator);
28242860
2825fn addDataInCodeLC(self: *MachO) !void {
2826 if (self.data_in_code_cmd_index == null) {
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 });
2861 for (self.dylibs.items) |*dylib| {
2862 dylib.deinit(self.base.allocator);
28362863 }
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 {
2840 if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) {
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 });
2868 for (self.load_commands.items) |*lc| {
2869 lc.deinit(self.base.allocator);
28502870 }
2851}
2871 self.load_commands.deinit(self.base.allocator);
28522872
2853fn addRpathLCs(self: *MachO, rpaths: []const []const u8) !void {
2854 for (rpaths) |rpath| {
2855 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2856 u64,
2857 @sizeOf(macho.rpath_command) + rpath.len + 1,
2858 @sizeOf(u64),
2859 ));
2860 var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{
2861 .cmd = macho.LC_RPATH,
2862 .cmdsize = cmdsize,
2863 .path = @sizeOf(macho.rpath_command),
2864 });
2865 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
2866 mem.set(u8, rpath_cmd.data, 0);
2867 mem.copy(u8, rpath_cmd.data, rpath);
2868 try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd });
2873 for (self.managed_atoms.items) |atom| {
2874 atom.deinit(self.base.allocator);
2875 self.base.allocator.destroy(atom);
2876 }
2877 self.managed_atoms.deinit(self.base.allocator);
2878 self.atoms.deinit(self.base.allocator);
2879 {
2880 var it = self.atom_free_lists.valueIterator();
2881 while (it.next()) |free_list| {
2882 free_list.deinit(self.base.allocator);
2883 }
2884 self.atom_free_lists.deinit(self.base.allocator);
2885 }
2886 for (self.decls.keys()) |decl| {
2887 decl.link.macho.deinit(self.base.allocator);
28692888 }
2889 self.decls.deinit(self.base.allocator);
28702890}
28712891
2872fn addLoadDylibLCs(self: *MachO) !void {
2873 for (self.referenced_dylibs.keys()) |id| {
2874 const dylib = self.dylibs.items[id];
2875 const dylib_id = dylib.id orelse unreachable;
2876 var dylib_cmd = try commands.createLoadDylibCommand(
2877 self.base.allocator,
2878 dylib_id.name,
2879 dylib_id.timestamp,
2880 dylib_id.current_version,
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 });
2892pub fn closeFiles(self: MachO) void {
2893 for (self.objects.items) |object| {
2894 object.file.close();
2895 }
2896 for (self.archives.items) |archive| {
2897 archive.file.close();
2898 }
2899 for (self.dylibs.items) |dylib| {
2900 dylib.file.close();
28852901 }
28862902}
28872903
2888fn flushZld(self: *MachO) !void {
2889 self.load_commands_dirty = true;
2890 try self.writeTextBlocks();
2891 try self.writeStubHelperCommon();
2904fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection) void {
2905 log.debug("freeAtom {*}", .{atom});
2906 atom.deinit(self.base.allocator);
28922907
2893 if (self.common_section_index) |index| {
2894 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2895 const sect = &seg.sections.items[index];
2896 sect.offset = 0;
2908 const free_list = self.atom_free_lists.getPtr(match).?;
2909 var already_have_free_list_node = false;
2910 {
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 }
28972923 }
2924 // TODO process free list for dbg info just like we do above for vaddrs
28982925
2899 if (self.bss_section_index) |index| {
2900 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2901 const sect = &seg.sections.items[index];
2902 sect.offset = 0;
2926 if (self.atoms.getPtr(match)) |last_atom| {
2927 if (last_atom.* == atom) {
2928 if (atom.prev) |prev| {
2929 // TODO shrink the section size here
2930 last_atom.* = prev;
2931 } else {
2932 _ = self.atoms.fetchRemove(match);
2933 }
2934 }
29032935 }
29042936
2905 if (self.tlv_bss_section_index) |index| {
2906 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2907 const sect = &seg.sections.items[index];
2908 sect.offset = 0;
2937 if (self.d_sym) |*ds| {
2938 if (ds.dbg_info_decl_first == atom) {
2939 ds.dbg_info_decl_first = atom.dbg_info_next;
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 }
29092945 }
29102946
2911 try self.writeGotEntries();
2912 try self.setEntryPoint();
2913 try self.writeRebaseInfoTableZld();
2914 try self.writeBindInfoTableZld();
2915 try self.writeLazyBindInfoTableZld();
2916 try self.writeExportInfoZld();
2917 try self.writeDices();
2947 if (atom.prev) |prev| {
2948 prev.next = atom.next;
29182949
2919 {
2920 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2921 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2922 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2950 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
2951 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
2952 // the OOM here.
2953 free_list.append(self.base.allocator, prev) catch {};
2954 }
2955 } else {
2956 atom.prev = null;
29232957 }
29242958
2925 try self.writeSymbolTable();
2926 try self.writeStringTableZld();
2959 if (atom.next) |next| {
2960 next.prev = atom.prev;
2961 } else {
2962 atom.next = null;
2963 }
29272964
2928 {
2929 // Seal __LINKEDIT size
2930 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2931 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
2965 if (atom.dbg_info_prev) |prev| {
2966 prev.dbg_info_next = atom.dbg_info_next;
2967
2968 // TODO the free list logic like we do for atoms above
2969 } else {
2970 atom.dbg_info_prev = null;
29322971 }
29332972
2934 if (self.requires_adhoc_codesig) {
2935 try self.writeCodeSignaturePadding();
2973 if (atom.dbg_info_next) |next| {
2974 next.dbg_info_prev = atom.dbg_info_prev;
2975 } else {
2976 atom.dbg_info_next = null;
29362977 }
2978}
29372979
2938 try self.writeLoadCommands();
2939 try self.writeHeader();
2940
2941 if (self.requires_adhoc_codesig) {
2942 try self.writeCodeSignature();
2943 }
2980fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSection) void {
2981 _ = self;
2982 _ = atom;
2983 _ = new_block_size;
2984 _ = match;
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.
29442987}
29452988
2946fn writeGotEntries(self: *MachO) !void {
2947 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2948 const sect = seg.sections.items[self.got_section_index.?];
2989fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
2990 const sym = self.locals.items[atom.local_sym_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));
2951 defer self.base.allocator.free(buffer);
2997pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
2998 if (decl.link.macho.local_sym_index != 0) return;
29522999
2953 var stream = std.io.fixedBufferStream(buffer);
2954 var writer = stream.writer();
3000 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
3001 try self.decls.putNoClobber(self.base.allocator, decl, {});
29553002
2956 for (self.got_entries.items) |key| {
2957 const address: u64 = switch (key.where) {
2958 .local => self.locals.items[key.where_index].n_value,
2959 .undef => 0,
2960 };
2961 try writer.writeIntLittle(u64, address);
3003 if (self.locals_free_list.popOrNull()) |i| {
3004 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
3005 decl.link.macho.local_sym_index = i;
3006 } else {
3007 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
3008 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
3009 _ = self.locals.addOneAssumeCapacity();
29623010 }
29633011
2964 log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len });
2965
2966 try self.base.file.?.pwriteAll(buffer, sect.offset);
2967}
2968
2969fn setEntryPoint(self: *MachO) !void {
2970 if (self.base.options.output_mode != .Exe) return;
3012 self.locals.items[decl.link.macho.local_sym_index] = .{
3013 .n_strx = 0,
3014 .n_type = 0,
3015 .n_sect = 0,
3016 .n_desc = 0,
3017 .n_value = 0,
3018 };
29713019
2972 // TODO we should respect the -entry flag passed in by the user to set a custom
2973 // entrypoint. For now, assume default of `_main`.
2974 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2975 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
2976 .bytes = &self.strtab,
2977 }) orelse {
2978 log.err("'_main' export not found", .{});
2979 return error.MissingMainEntrypoint;
3020 // TODO try popping from free list first before allocating a new GOT atom.
3021 const key = GotIndirectionKey{
3022 .where = .local,
3023 .where_index = decl.link.macho.local_sym_index,
29803024 };
2981 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
2982 assert(resolv.where == .global);
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;
3025 const got_atom = try self.createGotAtom(key);
3026 try self.got_entries_map.put(self.base.allocator, key, got_atom);
29873027}
29883028
2989fn writeRebaseInfoTableZld(self: *MachO) !void {
2990 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
2991 defer pointers.deinit();
2992
2993 {
2994 var it = self.blocks.iterator();
2995 while (it.next()) |entry| {
2996 const match = entry.key_ptr.*;
2997 var block: *TextBlock = entry.value_ptr.*;
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;
3029pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
3030 if (build_options.skip_non_native and builtin.object_format != .macho) {
3031 @panic("Attempted to compile for object format that was disabled by build configuration");
3032 }
3033 if (build_options.have_llvm) {
3034 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
3035 }
3036 const tracy = trace(@src());
3037 defer tracy.end();
30023038
3003 while (true) {
3004 const sym = self.locals.items[block.local_sym_index];
3005 const base_offset = sym.n_value - seg.inner.vmaddr;
3039 const decl = func.owner_decl;
3040 // TODO clearing the code and relocs buffer should probably be orchestrated
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| {
3008 try pointers.append(.{
3009 .offset = base_offset + offset,
3010 .segment_id = match.seg,
3011 });
3012 }
3047 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3048 defer code_buffer.deinit();
30133049
3014 if (block.prev) |prev| {
3015 block = prev;
3016 } else break;
3050 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3051 const debug_buffers = if (self.d_sym) |*ds| blk: {
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);
30173062 }
3063 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
30183064 }
30193065 }
30203066
3021 if (self.got_section_index) |idx| {
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 }
3067 self.active_decl = decl;
30363068
3037 if (self.la_symbol_ptr_section_index) |idx| {
3038 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3039 const sect = seg.sections.items[idx];
3040 const base_offset = sect.addr - seg.inner.vmaddr;
3041 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3042
3043 try pointers.ensureUnusedCapacity(self.stubs.items.len);
3044 for (self.stubs.items) |_, i| {
3045 pointers.appendAssumeCapacity(.{
3046 .offset = base_offset + i * @sizeOf(u64),
3047 .segment_id = segment_id,
3048 });
3049 }
3069 const res = if (debug_buffers) |dbg|
3070 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
3071 .dwarf = .{
3072 .dbg_line = &dbg.dbg_line_buffer,
3073 .dbg_info = &dbg.dbg_info_buffer,
3074 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3075 },
3076 })
3077 else
3078 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
3079 switch (res) {
3080 .appended => {
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 },
30503088 }
30513089
3052 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
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;
3090 _ = try self.placeDecl(decl, decl.link.macho.code.items.len);
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);
30703106}
30713107
3072fn writeBindInfoTableZld(self: *MachO) !void {
3073 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3074 defer pointers.deinit();
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 }
3108pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3109 if (build_options.skip_non_native and builtin.object_format != .macho) {
3110 @panic("Attempted to compile for object format that was disabled by build configuration");
30933111 }
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 {
3096 var it = self.blocks.iterator();
3097 while (it.next()) |entry| {
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;
3118 if (decl.val.tag() == .extern_fn) {
3119 return; // TODO Should we do more when front-end analyzed extern decl?
3120 }
31083121
3109 for (block.bindings.items) |binding| {
3110 const bind_sym = self.undefs.items[binding.local_sym_index];
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 }
3122 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3123 defer code_buffer.deinit();
31183124
3119 if (block.prev) |prev| {
3120 block = prev;
3121 } else break;
3125 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3126 const debug_buffers = if (self.d_sym) |*ds| blk: {
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);
31223137 }
3138 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
31233139 }
31243140 }
31253141
3126 const size = try bind.bindInfoSize(pointers.items);
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());
3142 self.active_decl = decl;
31323143
3133 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3134 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3135 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3136 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
3137 seg.inner.filesize += dyld_info.bind_size;
3144 const res = if (debug_buffers) |dbg|
3145 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3146 .ty = decl.ty,
3147 .val = decl.val,
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);
31423187}
31433188
3144fn writeLazyBindInfoTableZld(self: *MachO) !void {
3145 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3146 defer pointers.deinit();
3189fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
3190 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
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| {
3149 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3150 const sect = seg.sections.items[idx];
3151 const base_offset = sect.addr - seg.inner.vmaddr;
3152 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3153
3154 try pointers.ensureUnusedCapacity(self.stubs.items.len);
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),
3194 if (decl.link.macho.size != 0) {
3195 const capacity = decl.link.macho.capacity(self.*);
3196 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3197 if (need_realloc) {
3198 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, .{
3199 .seg = self.text_segment_cmd_index.?,
3200 .sect = self.text_section_index.?,
31633201 });
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);
3172 try bind.writeLazyBindInfo(pointers.items, stream.writer());
3203 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
31733204
3174 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3175 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3176 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3177 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
3178 seg.inner.filesize += dyld_info.lazy_bind_size;
3205 if (vaddr != symbol.n_value) {
3206 log.debug(" (writing new GOT entry)", .{});
3207 const got_atom = self.got_entries_map.get(.{
3208 .where = .local,
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);
3183 try self.populateLazyBindOffsetsInStubHelper(buffer);
3184}
3233 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
3234 defer self.base.allocator.free(new_name);
31853235
3186fn writeExportInfoZld(self: *MachO) !void {
3187 var trie: Trie = .{};
3188 defer trie.deinit(self.base.allocator);
3236 symbol.n_strx = try self.makeString(new_name);
3237 symbol.n_type = macho.N_SECT;
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;
3191 const base_address = text_segment.inner.vmaddr;
3244 const name_str_index = try self.makeString(decl_name);
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.
3194 log.debug("writing export trie", .{});
3250 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, addr });
31953251
3196 for (self.globals.items) |sym| {
3197 const sym_name = self.getString(sym.n_strx);
3198 log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
3252 errdefer self.freeAtom(&decl.link.macho, .{
3253 .seg = self.text_segment_cmd_index.?,
3254 .sect = self.text_section_index.?,
3255 });
31993256
3200 try trie.put(self.base.allocator, .{
3201 .name = sym_name,
3202 .vmaddr_offset = sym.n_value - base_address,
3203 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
3257 symbol.* = .{
3258 .n_strx = name_str_index,
3259 .n_type = macho.N_SECT,
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.?,
32043272 });
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);
32053278 }
32063279
3207 try trie.finalize(self.base.allocator);
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);
3280 return symbol;
32253281}
32263282
3227fn writeSymbolTable(self: *MachO) !void {
3228 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3229 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3283pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
3284 if (self.d_sym) |*ds| {
3285 try ds.updateDeclLineNumber(module, decl);
3286 }
3287}
32303288
3231 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
3232 defer locals.deinit();
3233 try locals.appendSlice(self.locals.items);
3289pub fn updateDeclExports(
3290 self: *MachO,
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) {
3236 for (self.objects.items) |object| {
3237 if (object.debug_info == null) continue;
3306 try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len);
3307 if (decl.link.macho.local_sym_index == 0) return;
3308 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
32383309
3239 // Open scope
3240 try locals.ensureUnusedCapacity(3);
3241 locals.appendAssumeCapacity(.{
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 });
3310 for (exports) |exp| {
3311 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
3312 defer self.base.allocator.free(exp_name);
32623313
3263 for (object.text_blocks.items) |block| {
3264 if (block.stab) |stab| {
3265 const nlists = try stab.asNlists(block.local_sym_index, self);
3266 defer self.base.allocator.free(nlists);
3267 try locals.appendSlice(nlists);
3268 } else {
3269 for (block.contained.items) |sym_at_off| {
3270 const stab = sym_at_off.stab orelse 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 }
3314 if (exp.options.section) |section_name| {
3315 if (!mem.eql(u8, section_name, "__text")) {
3316 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3317 module.failed_exports.putAssumeCapacityNoClobber(
3318 exp,
3319 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
3320 );
3321 continue;
32763322 }
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 });
32863323 }
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);
3341 defer self.base.allocator.free(buf);
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 }
3325 var n_type: u8 = macho.N_SECT | macho.N_EXT;
3326 var n_desc: u16 = 0;
33503327
3351 got.reserved1 = nstubs;
3352 for (self.got_entries.items) |entry| {
3353 switch (entry.where) {
3354 .undef => {
3355 try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index);
3328 switch (exp.options.linkage) {
3329 .Internal => {
3330 // Symbol should be hidden, or in MachO lingo, private extern.
3331 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
3332 // TODO work out when to add N_WEAK_REF.
3333 n_type |= macho.N_PEXT;
3334 n_desc |= macho.N_WEAK_DEF;
33563335 },
3357 .local => {
3358 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
3336 .Strong => {},
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;
33593349 },
33603350 }
3361 }
33623351
3363 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
3364 for (self.stubs.items) |id| {
3365 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3366 }
3352 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
3353 const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
3354 _ = self.globals.addOneAssumeCapacity();
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);
3369}
3360 const n_strx = try self.makeString(exp_name);
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 {
3372 if (build_options.have_llvm) {
3373 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
3371 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, n_strx);
3372 resolv.value_ptr.* = .{
3373 .where = .global,
3374 .where_index = global_sym_index,
3375 .local_sym_index = decl.link.macho.local_sym_index,
3376 };
33743377 }
3378}
33753379
3376 if (self.d_sym) |*ds| {
3377 ds.deinit(self.base.allocator);
3378 }
3380pub fn deleteExport(self: *MachO, exp: Export) void {
3381 const sym_index = exp.sym_index orelse return;
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);
3381 self.pending_updates.deinit(self.base.allocator);
3382 self.got_entries.deinit(self.base.allocator);
3383 self.got_entries_map.deinit(self.base.allocator);
3384 self.got_entries_free_list.deinit(self.base.allocator);
3385 self.stubs.deinit(self.base.allocator);
3386 self.stubs_map.deinit(self.base.allocator);
3387 self.strtab_dir.deinit(self.base.allocator);
3388 self.strtab.deinit(self.base.allocator);
3389 self.undefs.deinit(self.base.allocator);
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);
3388pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3389 log.debug("freeDecl {*}", .{decl});
3390 _ = self.decls.swapRemove(decl);
3391 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3392 self.freeAtom(&decl.link.macho, .{
3393 .seg = self.text_segment_cmd_index.?,
3394 .sect = self.text_section_index.?,
3395 });
3396 if (decl.link.macho.local_sym_index != 0) {
3397 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
33953398
3396 for (self.objects.items) |*object| {
3397 object.deinit(self.base.allocator);
3398 }
3399 self.objects.deinit(self.base.allocator);
3399 // TODO free GOT atom here.
34003400
3401 for (self.archives.items) |*archive| {
3402 archive.deinit(self.base.allocator);
3401 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
3402 decl.link.macho.local_sym_index = 0;
34033403 }
3404 self.archives.deinit(self.base.allocator);
3405
3406 for (self.dylibs.items) |*dylib| {
3407 dylib.deinit(self.base.allocator);
3404 if (self.d_sym) |*ds| {
3405 // TODO make this logic match freeAtom. Maybe abstract the logic
3406 // out since the same thing is desired for both.
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 }
34083426 }
3409 self.dylibs.deinit(self.base.allocator);
3410 self.dylibs_map.deinit(self.base.allocator);
3411 self.referenced_dylibs.deinit(self.base.allocator);
3427}
34123428
3413 for (self.load_commands.items) |*lc| {
3414 lc.deinit(self.base.allocator);
3415 }
3416 self.load_commands.deinit(self.base.allocator);
3429pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
3430 assert(decl.link.macho.local_sym_index != 0);
3431 return self.locals.items[decl.link.macho.local_sym_index].n_value;
3432}
34173433
3418 for (self.managed_blocks.items) |block| {
3419 block.deinit(self.base.allocator);
3420 self.base.allocator.destroy(block);
3434pub fn populateMissingMetadata(self: *MachO) !void {
3435 if (self.pagezero_segment_cmd_index == null) {
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;
34213446 }
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| {
3427 decl.link.macho.deinit(self.base.allocator);
3448 if (self.text_segment_cmd_index == null) {
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;
34283470 }
3429 self.decls.deinit(self.base.allocator);
3430}
34313471
3432pub fn closeFiles(self: MachO) void {
3433 for (self.objects.items) |object| {
3434 object.file.close();
3472 if (self.text_section_index == null) {
3473 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
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 );
34353488 }
3436 for (self.archives.items) |archive| {
3437 archive.file.close();
3489
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 );
34383512 }
3439 for (self.dylibs.items) |dylib| {
3440 dylib.file.close();
3513
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 );
34413540 }
3442}
34433541
3444fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
3445 log.debug("freeTextBlock {*}", .{text_block});
3446 text_block.deinit(self.base.allocator);
3542 if (self.data_const_segment_cmd_index == null) {
3543 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
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;
3449 {
3450 var i: usize = 0;
3451 // TODO turn text_block_free_list into a hash map
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
3548 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
3549 address_and_offset.offset,
3550 address_and_offset.offset + needed_size,
3551 });
34643552
3465 if (self.last_text_block == text_block) {
3466 // TODO shrink the __text section size here
3467 self.last_text_block = text_block.prev;
3553 try self.load_commands.append(self.base.allocator, .{
3554 .Segment = .{
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;
34683567 }
3469 if (self.d_sym) |*ds| {
3470 if (ds.dbg_info_decl_first == text_block) {
3471 ds.dbg_info_decl_first = text_block.dbg_info_next;
3472 }
3473 if (ds.dbg_info_decl_last == text_block) {
3474 // TODO shrink the .debug_info section size here
3475 ds.dbg_info_decl_last = text_block.dbg_info_prev;
3476 }
3568
3569 if (self.got_section_index == null) {
3570 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3571 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3572 self.got_section_index = try self.allocateSection(
3573 self.data_const_segment_cmd_index.?,
3574 "__got",
3575 needed_size,
3576 alignment,
3577 .{
3578 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3579 },
3580 );
34773581 }
34783582
3479 if (text_block.prev) |prev| {
3480 prev.next = text_block.next;
3583 if (self.data_segment_cmd_index == null) {
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.*)) {
3483 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
3484 // the OOM here.
3485 self.text_block_free_list.append(self.base.allocator, prev) catch {};
3486 }
3487 } else {
3488 text_block.prev = null;
3589 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
3590
3591 try self.load_commands.append(self.base.allocator, .{
3592 .Segment = .{
3593 .inner = .{
3594 .segname = makeStaticString("__DATA"),
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;
34893605 }
34903606
3491 if (text_block.next) |next| {
3492 next.prev = text_block.prev;
3493 } else {
3494 text_block.next = null;
3607 if (self.la_symbol_ptr_section_index == null) {
3608 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3609 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
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 );
34953619 }
34963620
3497 if (text_block.dbg_info_prev) |prev| {
3498 prev.dbg_info_next = text_block.dbg_info_next;
3621 if (self.data_section_index == null) {
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 above
3501 } else {
3502 text_block.dbg_info_prev = null;
3633 if (self.tlv_section_index == null) {
3634 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
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 );
35033645 }
35043646
3505 if (text_block.dbg_info_next) |next| {
3506 next.dbg_info_prev = text_block.dbg_info_prev;
3507 } else {
3508 text_block.dbg_info_next = null;
3647 if (self.tlv_data_section_index == null) {
3648 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3649 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
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 );
35093659 }
3510}
35113660
3512fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void {
3513 _ = self;
3514 _ = text_block;
3515 _ = new_block_size;
3516 // TODO check the new capacity, and if it crosses the size threshold into a big enough
3517 // capacity, insert a free list node for it.
3518}
3661 if (self.tlv_bss_section_index == null) {
3662 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3663 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3664 self.tlv_bss_section_index = try self.allocateSection(
3665 self.data_segment_cmd_index.?,
3666 "__thread_bss",
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 {
3521 const sym = self.locals.items[text_block.local_sym_index];
3522 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3523 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
3524 if (!need_realloc) return sym.n_value;
3525 return self.allocateTextBlock(text_block, new_block_size, alignment);
3526}
3678 if (self.bss_section_index == null) {
3679 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3680 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3681 self.bss_section_index = try self.allocateSection(
3682 self.data_segment_cmd_index.?,
3683 "__bss",
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 {
3529 if (decl.link.macho.local_sym_index != 0) return;
3695 if (self.linkedit_segment_cmd_index == null) {
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);
3532 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);
3699 log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset});
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| {
3537 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
3538 decl.link.macho.local_sym_index = i;
3539 } else {
3540 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
3541 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
3542 _ = self.locals.addOneAssumeCapacity();
3715 if (self.dyld_info_cmd_index == null) {
3716 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
3717 try self.load_commands.append(self.base.allocator, .{
3718 .DyldInfoOnly = .{
3719 .cmd = macho.LC_DYLD_INFO_ONLY,
3720 .cmdsize = @sizeOf(macho.dyld_info_command),
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;
35433734 }
35443735
3545 const got_index: u32 = blk: {
3546 if (self.got_entries_free_list.popOrNull()) |i| {
3547 log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name });
3548 break :blk i;
3549 } else {
3550 const got_index = @intCast(u32, self.got_entries.items.len);
3551 log.debug("allocating GOT entry index {d} for {s}", .{ got_index, decl.name });
3552 _ = self.got_entries.addOneAssumeCapacity();
3553 self.got_entries_count_dirty = true;
3554 self.rebase_info_dirty = true;
3555 break :blk got_index;
3556 }
3557 };
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,
3736 if (self.symtab_cmd_index == null) {
3737 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
3738 try self.load_commands.append(self.base.allocator, .{
3739 .Symtab = .{
3740 .cmd = macho.LC_SYMTAB,
3741 .cmdsize = @sizeOf(macho.symtab_command),
3742 .symoff = 0,
3743 .nsyms = 0,
3744 .stroff = 0,
3745 .strsize = 0,
3746 },
3747 });
3748 self.load_commands_dirty = true;
50363749 }
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) {
5055 .x86_64 => {
5056 const displacement = try math.cast(
5057 i32,
5058 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
5059 );
5060 // pushq
5061 code[0] = 0x68;
5062 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
5063 // jmpq
5064 code[5] = 0xe9;
5065 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
5066 },
5067 .aarch64 => {
5068 const literal = blk: {
5069 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
5070 break :blk try math.cast(u18, div_res);
5071 };
5072 // ldr w16, literal
5073 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
5074 .literal = literal,
5075 }).toU32());
5076 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
5077 // b disp
5078 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
5079 // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
5080 mem.writeIntLittle(u32, code[8..12], 0x0);
5081 },
5082 else => unreachable,
3751 if (self.dysymtab_cmd_index == null) {
3752 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
3753 try self.load_commands.append(self.base.allocator, .{
3754 .Dysymtab = .{
3755 .cmd = macho.LC_DYSYMTAB,
3756 .cmdsize = @sizeOf(macho.dysymtab_command),
3757 .ilocalsym = 0,
3758 .nlocalsym = 0,
3759 .iextdefsym = 0,
3760 .nextdefsym = 0,
3761 .iundefsym = 0,
3762 .nundefsym = 0,
3763 .tocoff = 0,
3764 .ntoc = 0,
3765 .modtaboff = 0,
3766 .nmodtab = 0,
3767 .extrefsymoff = 0,
3768 .nextrefsyms = 0,
3769 .indirectsymoff = 0,
3770 .nindirectsyms = 0,
3771 .extreloff = 0,
3772 .nextrel = 0,
3773 .locreloff = 0,
3774 .nlocrel = 0,
3775 },
3776 });
3777 self.load_commands_dirty = true;
50833778 }
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);
5109 if (amt != existing_size) return error.InputOutput;
5110 symtab.symoff = @intCast(u32, new_symoff);
5111 self.strtab_needs_relocation = true;
5112 }
5113 symtab.nsyms = @intCast(u32, nsyms);
3780 if (self.dylinker_cmd_index == null) {
3781 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
3782 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3783 u64,
3784 @sizeOf(macho.dylinker_command) + mem.lenZ(default_dyld_path),
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 });
51143796 self.load_commands_dirty = true;
51153797 }
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();
5133 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5134 const nlocals = self.locals.items.len;
5135 const nglobals = self.globals.items.len;
5136 const nundefs = self.undefs.items.len;
5137
5138 const locals_off = symtab.symoff;
5139 const locals_size = nlocals * @sizeOf(macho.nlist_64);
5140
5141 const globals_off = locals_off + locals_size;
5142 const globals_size = nglobals * @sizeOf(macho.nlist_64);
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));
3799 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
3800 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
3801 try self.load_commands.append(self.base.allocator, .{
3802 .Main = .{
3803 .cmd = macho.LC_MAIN,
3804 .cmdsize = @sizeOf(macho.entry_point_command),
3805 .entryoff = 0x0,
3806 .stacksize = 0,
3807 },
3808 });
3809 self.load_commands_dirty = true;
51843810 }
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;
5197 for (self.stubs.items) |id| {
5198 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3812 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {
3813 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
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;
51993829 }
52003830
5201 got.reserved1 = nstubs;
5202 for (self.got_entries.items) |entry| {
5203 switch (entry.where) {
5204 .undef => {
5205 try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index);
5206 },
5207 .local => {
5208 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
3831 if (self.source_version_cmd_index == null) {
3832 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
3833 try self.load_commands.append(self.base.allocator, .{
3834 .SourceVersion = .{
3835 .cmd = macho.LC_SOURCE_VERSION,
3836 .cmdsize = @sizeOf(macho.source_version_command),
3837 .version = 0x0,
52093838 },
5210 }
3839 });
3840 self.load_commands_dirty = true;
52113841 }
52123842
5213 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
5214 for (self.stubs.items) |id| {
5215 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3843 if (self.build_version_cmd_index == null) {
3844 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
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;
52163876 }
52173877
5218 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
5219 self.load_commands_dirty = true;
5220}
5221
5222fn writeDices(self: *MachO) !void {
5223 if (!self.has_dices) return;
5224
5225 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5226 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
5227 const fileoff = seg.inner.fileoff + seg.inner.filesize;
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;
3878 if (self.uuid_cmd_index == null) {
3879 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
3880 var uuid_cmd: macho.uuid_command = .{
3881 .cmd = macho.LC_UUID,
3882 .cmdsize = @sizeOf(macho.uuid_command),
3883 .uuid = undefined,
3884 };
3885 std.crypto.random.bytes(&uuid_cmd.uuid);
3886 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
3887 self.load_commands_dirty = true;
52393888 }
52403889
5241 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5242 const text_sect = text_seg.sections.items[self.text_section_index.?];
5243
5244 while (true) {
5245 if (block.dices.items.len > 0) {
5246 const sym = self.locals.items[block.local_sym_index];
5247 const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset);
5248
5249 try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry));
5250 for (block.dices.items) |dice| {
5251 const rebased_dice = macho.data_in_code_entry{
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;
3890 if (self.data_in_code_cmd_index == null) {
3891 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
3892 try self.load_commands.append(self.base.allocator, .{
3893 .LinkeditData = .{
3894 .cmd = macho.LC_DATA_IN_CODE,
3895 .cmdsize = @sizeOf(macho.linkedit_data_command),
3896 .dataoff = 0,
3897 .datasize = 0,
3898 },
3899 });
3900 self.load_commands_dirty = true;
52633901 }
52643902
5265 const datasize = @intCast(u32, buf.items.len);
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);
3903 self.cold_start = true;
52743904}
52753905
5276fn writeCodeSignaturePadding(self: *MachO) !void {
5277 // TODO figure out how not to rewrite padding every single time.
5278 const tracy = trace(@src());
5279 defer tracy.end();
5280
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;
3906const AllocateSectionOpts = struct {
3907 flags: u32 = macho.S_REGULAR,
3908 reserved1: u32 = 0,
3909 reserved2: u32 = 0,
3910};
52913911
5292 // Advance size of __LINKEDIT segment
5293 linkedit_segment.inner.filesize += needed_size;
5294 if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) {
5295 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size);
5296 }
5297 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
5298 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
5299 // except for code signature data.
5300 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
5301 self.load_commands_dirty = true;
5302}
3912fn allocateSection(
3913 self: *MachO,
3914 segment_id: u16,
3915 sectname: []const u8,
3916 size: u64,
3917 alignment: u32,
3918 opts: AllocateSectionOpts,
3919) !u16 {
3920 const seg = &self.load_commands.items[segment_id].Segment;
3921 var sect = macho.section_64{
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 {
5305 const tracy = trace(@src());
5306 defer tracy.end();
3931 const alignment_pow_2 = try math.powi(u32, 2, alignment);
3932 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;
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;
5309 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3935 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
3936 commands.segmentName(sect),
3937 commands.sectionName(sect),
3938 off,
3939 off + size,
3940 });
53103941
5311 var code_sig: CodeSignature = .{};
5312 defer code_sig.deinit(self.base.allocator);
3942 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
3943 sect.offset = @intCast(u32, off);
53133944
5314 try code_sig.calcAdhocSignature(
5315 self.base.allocator,
5316 self.base.file.?,
5317 self.base.options.emit.?.sub_path,
5318 text_segment.inner,
5319 code_sig_cmd,
5320 self.base.options.output_mode,
5321 self.page_size,
5322 );
3945 const index = @intCast(u16, seg.sections.items.len);
3946 try seg.sections.append(self.base.allocator, sect);
3947 seg.inner.cmdsize += @sizeOf(macho.section_64);
3948 seg.inner.nsects += 1;
53233949
5324 var buffer = try self.base.allocator.alloc(u8, code_sig.size());
5325 defer self.base.allocator.free(buffer);
5326 var stream = std.io.fixedBufferStream(buffer);
5327 try code_sig.write(stream.writer());
3950 const match = MatchingSection{
3951 .seg = segment_id,
3952 .sect = index,
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;
53323961}
53333962
5334fn writeExportInfo(self: *MachO) !void {
5335 if (!self.export_info_dirty) return;
5336 if (self.globals.items.len == 0) return;
3963fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {
3964 const seg = self.load_commands.items[segment_id].Segment;
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 {
53383974 const tracy = trace(@src());
53393975 defer tracy.end();
53403976
5341 var trie: Trie = .{};
5342 defer trie.deinit(self.base.allocator);
3977 const seg = &self.load_commands.items[match.seg].Segment;
3978 const sect = &seg.sections.items[match.sect];
53433979
5344 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
5345 const base_address = text_segment.inner.vmaddr;
3980 const alignment = try math.powi(u32, 2, sect.@"align");
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.
5348 log.debug("writing export trie", .{});
3985 if (needed_size > max_size) blk: {
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| {
5351 const sym_name = self.getString(sym.n_strx);
5352 log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
3993 // Before we commit to this, check if the segment needs to grow too.
3994 // We assume that each section header is growing linearly with the increasing
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, .{
5355 .name = sym_name,
5356 .vmaddr_offset = sym.n_value - base_address,
5357 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
5358 });
5359 }
5360 try trie.finalize(self.base.allocator);
4016 // TODO We should probably nop the expanded by distance, or put 0s.
4017
4018 // TODO copyRangeAll doesn't automatically extend the file on macOS.
4019 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4020 const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
4021 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
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));
5363 defer self.base.allocator.free(buffer);
5364 var stream = std.io.fixedBufferStream(buffer);
5365 const nwritten = try trie.write(stream.writer());
5366 assert(nwritten == trie.size);
4043 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
4044 moved_sect.offset += @intCast(u32, seg_offset_amt);
4045 moved_sect.addr += seg_offset_amt;
4046
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;
5369 const allocated_size = self.allocatedSizeLinkedit(dyld_info.export_off);
5370 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
4056 try self.allocateLocalSymbols(.{
4057 .seg = @intCast(u16, next),
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) {
5373 dyld_info.export_off = 0;
5374 dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
5375 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
4092 try self.allocateLocalSymbols(.{
4093 .seg = match.seg,
4094 .sect = next,
4095 }, @intCast(i64, offset_amt));
4096 }
53764097 }
5377 dyld_info.export_size = @intCast(u32, needed_size);
5378 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
4098}
53794099
5380 try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);
5381 self.load_commands_dirty = true;
5382 self.export_info_dirty = false;
4100fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
4101 const seg = self.load_commands.items[segment_id].Segment;
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;
53834110}
53844111
5385fn writeRebaseInfoTable(self: *MachO) !void {
5386 if (!self.rebase_info_dirty) return;
4112fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
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 {
53884125 const tracy = trace(@src());
53894126 defer tracy.end();
53904127
5391 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
5392 defer pointers.deinit();
4128 const seg = &self.load_commands.items[match.seg].Segment;
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 {
5395 var it = self.blocks.iterator();
5396 while (it.next()) |entry| {
5397 const match = entry.key_ptr.*;
5398 var block: *TextBlock = entry.value_ptr.*;
5399
5400 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
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;
4134 // We use these to indicate our intention to update metadata, placing the new atom,
4135 // and possibly removing a free list node.
4136 // It would be simpler to do it inside the for loop below, but that would cause a
4137 // problem if an error was returned later in the function. So this action
4138 // is actually carried out at the end of the function, when errors are no longer possible.
4139 var atom_placement: ?*Atom = null;
4140 var free_list_removal: ?usize = null;
54074141
5408 for (block.rebases.items) |offset| {
5409 try pointers.append(.{
5410 .offset = base_offset + offset,
5411 .segment_id = match.seg,
5412 });
4142 // First we look for an appropriately sized free list node.
4143 // The list is unordered. We'll just take the first thing that works.
4144 var vaddr = blk: {
4145 var i: usize = 0;
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;
54134166 }
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| {
5416 block = prev;
5417 } else break;
4175 // Set up the metadata to be updated, after errors are no longer possible.
4176 atom_placement = big_atom;
4177 if (!keep_free_list_node) {
4178 free_list_removal = i;
54184179 }
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);
54194190 }
5420 }
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.?);
4191 };
54274192
5428 for (self.got_entries.items) |entry, i| {
5429 if (entry.where == .undef) continue;
4193 const expand_section = atom_placement == null or atom_placement.?.next == null;
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(.{
5432 .offset = base_offset + i * @sizeOf(u64),
5433 .segment_id = segment_id,
5434 });
5435 }
4209 if (atom.prev) |prev| {
4210 prev.next = atom.next;
4211 }
4212 if (atom.next) |next| {
4213 next.prev = atom.prev;
54364214 }
54374215
5438 if (self.la_symbol_ptr_section_index) |idx| {
5439 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5440 const sect = seg.sections.items[idx];
5441 const base_offset = sect.addr - seg.inner.vmaddr;
5442 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
5443
5444 try pointers.ensureUnusedCapacity(self.stubs.items.len);
5445 for (self.stubs.items) |_, i| {
5446 pointers.appendAssumeCapacity(.{
5447 .offset = base_offset + i * @sizeOf(u64),
5448 .segment_id = segment_id,
5449 });
5450 }
4216 if (atom_placement) |big_atom| {
4217 atom.prev = big_atom;
4218 atom.next = big_atom.next;
4219 big_atom.next = atom;
4220 } else {
4221 atom.prev = null;
4222 atom.next = null;
4223 }
4224 if (free_list_removal) |i| {
4225 _ = free_list.swapRemove(i);
54514226 }
54524227
5453 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
4228 return vaddr;
4229}
54544230
5455 const size = try bind.rebaseInfoSize(pointers.items);
5456 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
5457 defer self.base.allocator.free(buffer);
4231pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
4232 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4233 defer self.base.allocator.free(sym_name);
54584234
5459 var stream = std.io.fixedBufferStream(buffer);
5460 try bind.writeRebaseInfo(pointers.items, stream.writer());
4235 if (self.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
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;
5463 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
5464 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
4242 log.debug("adding new extern function '{s}'", .{sym_name});
4243 const sym_index = @intCast(u32, self.undefs.items.len);
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) {
5467 dyld_info.rebase_off = 0;
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 }
4258 return sym_index;
4259}
54714260
5472 dyld_info.rebase_size = @intCast(u32, needed_size);
5473 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
4261const NextSegmentAddressAndOffset = struct {
4262 address: u64,
4263 offset: u64,
4264};
54744265
5475 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
5476 self.load_commands_dirty = true;
5477 self.rebase_info_dirty = false;
4266fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
4267 var prev_segment_idx: ?usize = null; // We use optional here for safety.
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 };
54784280}
54794281
5480fn writeBindInfoTable(self: *MachO) !void {
5481 if (!self.binding_info_dirty) return;
4282fn updateSectionOrdinals(self: *MachO) !void {
4283 if (!self.sections_order_dirty) return;
54824284
54834285 const tracy = trace(@src());
54844286 defer tracy.end();
54854287
5486 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
5487 defer pointers.deinit();
4288 var ordinal_remap = std.AutoHashMap(u8, u8).init(self.base.allocator);
4289 defer ordinal_remap.deinit();
4290 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
54884291
5489 if (self.got_section_index) |idx| {
5490 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
5491 const sect = seg.sections.items[idx];
5492 const base_offset = sect.addr - seg.inner.vmaddr;
5493 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
4292 var new_ordinal: u8 = 0;
4293 for (self.load_commands.items) |lc, lc_id| {
4294 if (lc != .Segment) break;
54944295
5495 for (self.got_entries.items) |entry, i| {
5496 if (entry.where == .local) continue;
5497
5498 const sym = self.undefs.items[entry.where_index];
5499 try pointers.append(.{
5500 .offset = base_offset + i * @sizeOf(u64),
5501 .segment_id = segment_id,
5502 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
5503 .name = self.getString(sym.n_strx),
5504 });
4296 for (lc.Segment.sections.items) |_, sect_id| {
4297 const match = MatchingSection{
4298 .seg = @intCast(u16, lc_id),
4299 .sect = @intCast(u16, sect_id),
4300 };
4301 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
4302 new_ordinal += 1;
4303 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
4304 try ordinals.putNoClobber(self.base.allocator, match, {});
55054305 }
55064306 }
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
55084331 {
5509 var it = self.blocks.iterator();
4332 var it = self.atoms.iterator();
55104333 while (it.next()) |entry| {
55114334 const match = entry.key_ptr.*;
5512 var block: *TextBlock = entry.value_ptr.*;
4335 var atom: *Atom = entry.value_ptr.*;
55134336
55144337 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
55154338
55164339 const seg = self.load_commands.items[match.seg].Segment;
55174340
55184341 while (true) {
5519 const sym = self.locals.items[block.local_sym_index];
4342 const sym = self.locals.items[atom.local_sym_index];
55204343 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| {
55234363 const bind_sym = self.undefs.items[binding.local_sym_index];
5524 try pointers.append(.{
4364 try lazy_bind_pointers.append(.{
55254365 .offset = binding.offset + base_offset,
55264366 .segment_id = match.seg,
55274367 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
......@@ -5529,94 +4369,105 @@ fn writeBindInfoTable(self: *MachO) !void {
55294369 });
55304370 }
55314371
5532 if (block.prev) |prev| {
5533 block = prev;
4372 if (atom.prev) |prev| {
4373 atom = prev;
55344374 } else break;
55354375 }
55364376 }
55374377 }
55384378
5539 const size = try bind.bindInfoSize(pointers.items);
5540 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
5541 defer self.base.allocator.free(buffer);
5542
5543 var stream = std.io.fixedBufferStream(buffer);
5544 try bind.writeBindInfo(pointers.items, stream.writer());
4379 var trie: Trie = .{};
4380 defer trie.deinit(self.base.allocator);
55454381
5546 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
5547 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
5548 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
4382 {
4383 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
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) {
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.
4399 try trie.finalize(self.base.allocator);
55544400 }
55554401
5556 dyld_info.bind_size = @intCast(u32, needed_size);
5557 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
5558
5559 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
5560 self.load_commands_dirty = true;
5561 self.binding_info_dirty = false;
5562}
5563
5564fn writeLazyBindInfoTable(self: *MachO) !void {
5565 if (!self.lazy_binding_info_dirty) return;
4402 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4403 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
4404 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
4405 const bind_size = try bind.bindInfoSize(bind_pointers.items);
4406 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
4407 const export_size = trie.size;
55664408
5567 const tracy = trace(@src());
5568 defer tracy.end();
4409 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
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);
5571 defer pointers.deinit();
4413 dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size;
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| {
5574 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5575 const sect = seg.sections.items[idx];
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 }
4417 dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size;
4418 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64)));
4419 seg.inner.filesize += dyld_info.lazy_bind_size;
55914420
5592 const size = try bind.lazyBindInfoSize(pointers.items);
5593 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
4421 dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_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);
55944427 defer self.base.allocator.free(buffer);
4428 mem.set(u8, buffer, 0);
55954429
55964430 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;
5600 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
5601 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
4433 try bind.writeRebaseInfo(rebase_pointers.items, writer);
4434 try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size));
56024435
5603 if (needed_size > allocated_size) {
5604 dyld_info.lazy_bind_off = 0;
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 }
4436 try bind.writeBindInfo(bind_pointers.items, writer);
4437 try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size));
56084438
5609 dyld_info.lazy_bind_size = @intCast(u32, needed_size);
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 });
4439 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
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);
5613 try self.populateLazyBindOffsetsInStubHelper(buffer);
4442 _ = try trie.write(writer);
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 );
56144453 self.load_commands_dirty = true;
5615 self.lazy_binding_info_dirty = false;
56164454}
56174455
56184456fn 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
56214472 var stream = std.io.fixedBufferStream(buffer);
56224473 var reader = stream.reader();
......@@ -5661,50 +4512,245 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
56614512 else => {},
56624513 }
56634514 }
5664 assert(self.stubs.items.len <= offsets.items.len);
56654515
5666 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
5667 .x86_64 => 10,
5668 .aarch64 => 3 * @sizeOf(u32),
5669 else => unreachable,
5670 };
5671 const off: u4 = switch (self.base.options.target.cpu.arch) {
4516 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4517 const sect = seg.sections.items[self.stub_helper_section_index.?];
4518 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
56724519 .x86_64 => 1,
56734520 .aarch64 => 2 * @sizeOf(u32),
56744521 else => unreachable,
56754522 };
56764523 var buf: [@sizeOf(u32)]u8 = undefined;
5677 for (self.stubs.items) |_, index| {
5678 const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off;
5679 mem.writeIntLittle(u32, &buf, offsets.items[index]);
5680 try self.base.file.?.pwriteAll(&buf, placeholder_off);
4524 _ = offsets.pop();
4525 while (offsets.popOrNull()) |bind_offset| {
4526 const sym = self.locals.items[atom.local_sym_index];
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;
56814539 }
56824540}
56834541
5684fn writeStringTable(self: *MachO) !void {
5685 if (!self.strtab_dirty) return;
4542fn writeDices(self: *MachO) !void {
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 {
56874602 const tracy = trace(@src());
56884603 defer tracy.end();
56894604
4605 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
56904606 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
5691 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
5692 const needed_size = mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64));
4607 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
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) {
5695 symtab.strsize = 0;
5696 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));
5697 self.strtab_needs_relocation = false;
4656 // Close scope
4657 try locals.append(.{
4658 .n_strx = 0,
4659 .n_type = macho.N_SO,
4660 .n_sect = 0,
4661 .n_desc = 0,
4662 .n_value = 0,
4663 });
4664 }
56984665 }
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);
57034747 self.load_commands_dirty = true;
5704 self.strtab_dirty = false;
57054748}
57064749
5707fn writeStringTableZld(self: *MachO) !void {
4750fn writeStringTable(self: *MachO) !void {
4751 const tracy = trace(@src());
4752 defer tracy.end();
4753
57084754 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
57094755 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
57104756 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
......@@ -5719,55 +4765,81 @@ fn writeStringTableZld(self: *MachO) !void {
57194765 // This is potentially the last section, so we need to pad it out.
57204766 try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
57214767 }
4768 self.load_commands_dirty = true;
57224769}
57234770
5724fn updateLinkeditSegmentSizes(self: *MachO) !void {
5725 if (!self.load_commands_dirty) return;
4771fn writeLinkeditSegment(self: *MachO) !void {
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 {
57274787 const tracy = trace(@src());
57284788 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.
57324790 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
5733 var final_offset = linkedit_segment.inner.fileoff;
5734
5735 if (self.dyld_info_cmd_index) |idx| {
5736 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
5737 final_offset = std.math.max(final_offset, dyld_info.rebase_off + dyld_info.rebase_size);
5738 final_offset = std.math.max(final_offset, dyld_info.bind_off + dyld_info.bind_size);
5739 final_offset = std.math.max(final_offset, dyld_info.weak_bind_off + dyld_info.weak_bind_size);
5740 final_offset = std.math.max(final_offset, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size);
5741 final_offset = std.math.max(final_offset, dyld_info.export_off + dyld_info.export_size);
5742 }
5743 if (self.function_starts_cmd_index) |idx| {
5744 const fstart = self.load_commands.items[idx].LinkeditData;
5745 final_offset = std.math.max(final_offset, fstart.dataoff + fstart.datasize);
5746 }
5747 if (self.data_in_code_cmd_index) |idx| {
5748 const dic = self.load_commands.items[idx].LinkeditData;
5749 final_offset = std.math.max(final_offset, dic.dataoff + dic.datasize);
5750 }
5751 if (self.dysymtab_cmd_index) |idx| {
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);
4791 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
4792 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
4793 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
4794 self.base.options.emit.?.sub_path,
4795 fileoff,
4796 self.page_size,
4797 );
4798 code_sig_cmd.dataoff = @intCast(u32, fileoff);
4799 code_sig_cmd.datasize = needed_size;
4800
4801 // Advance size of __LINKEDIT segment
4802 linkedit_segment.inner.filesize += needed_size;
4803 if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) {
4804 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size);
4805 }
4806 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
4807 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
4808 // except for code signature data.
4809 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
57684810 self.load_commands_dirty = true;
57694811}
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
57714843/// Writes all load commands and section headers.
57724844fn writeLoadCommands(self: *MachO) !void {
57734845 if (!self.load_commands_dirty) return;
......@@ -5844,6 +4916,13 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
58444916 std.math.maxInt(@TypeOf(actual_size));
58454917}
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
58474926pub fn makeString(self: *MachO, string: []const u8) !u32 {
58484927 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{
58494928 .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;
55const fs = std.fs;
66const log = std.log.scoped(.dsym);
77const macho = std.macho;
8const math = std.math;
89const mem = std.mem;
910const DW = std.dwarf;
1011const leb = std.leb;
1112const Allocator = mem.Allocator;
1213
1314const build_options = @import("build_options");
15const commands = @import("commands.zig");
1416const trace = @import("../../tracy.zig").trace;
17const LoadCommand = commands.LoadCommand;
1518const Module = @import("../../Module.zig");
1619const Type = @import("../../type.zig").Type;
1720const link = @import("../../link.zig");
1821const MachO = @import("../MachO.zig");
19const SrcFn = MachO.SrcFn;
2022const TextBlock = MachO.TextBlock;
21const padToIdeal = MachO.padToIdeal;
22
23const commands = @import("commands.zig");
24const emptyHeader = commands.emptyHeader;
25const LoadCommand = commands.LoadCommand;
2623const SegmentCommand = commands.SegmentCommand;
24const SrcFn = MachO.SrcFn;
25const makeStaticString = MachO.makeStaticString;
26const padToIdeal = MachO.padToIdeal;
2727
2828const page_size: u16 = 0x1000;
2929
......@@ -188,105 +188,84 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
188188 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
189189
190190 try self.load_commands.append(allocator, .{
191 .Segment = SegmentCommand.empty("__DWARF", .{
192 .vmaddr = vmaddr,
193 .vmsize = needed_size,
194 .fileoff = off,
195 .filesize = needed_size,
196 }),
191 .Segment = .{
192 .inner = .{
193 .segname = makeStaticString("__DWARF"),
194 .vmaddr = vmaddr,
195 .vmsize = needed_size,
196 .fileoff = off,
197 .filesize = needed_size,
198 },
199 },
197200 });
198201 self.load_commands_dirty = true;
199202 }
200203 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);
203204 assert(self.debug_string_table.items.len == 0);
204
205 try dwarf_segment.addSection(allocator, "__debug_str", .{
206 .addr = dwarf_segment.inner.vmaddr,
207 .size = @intCast(u32, self.debug_string_table.items.len),
208 .offset = @intCast(u32, dwarf_segment.inner.fileoff),
209 .@"align" = 1,
210 });
211 self.load_commands_dirty = true;
205 self.debug_str_section_index = try self.allocateSection(
206 "__debug_str",
207 @intCast(u32, self.debug_string_table.items.len),
208 0,
209 );
212210 self.debug_string_table_dirty = true;
213211 }
214212 if (self.debug_info_section_index == null) {
215 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
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;
213 self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0);
231214 self.debug_info_header_dirty = true;
232215 }
233216 if (self.debug_abbrev_section_index == null) {
234 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
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;
217 self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0);
250218 self.debug_abbrev_section_dirty = true;
251219 }
252220 if (self.debug_aranges_section_index == null) {
253 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
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;
221 self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4);
269222 self.debug_aranges_section_dirty = true;
270223 }
271224 if (self.debug_line_section_index == null) {
272 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
273 self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len);
225 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
226 self.debug_line_header_dirty = true;
227 }
228}
274229
275 const file_size_hint = 250;
276 const p_align = 1;
277 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
230fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
231 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
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", .{
282 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
283 .size = file_size_hint,
284 .offset = @intCast(u32, off),
285 .@"align" = p_align,
286 });
287 self.load_commands_dirty = true;
288 self.debug_line_header_dirty = true;
289 }
243 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
244 commands.segmentName(sect),
245 commands.sectionName(sect),
246 off,
247 off + size,
248 });
249
250 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
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;
290269}
291270
292271pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
......@@ -614,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
614593}
615594
616595fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
617 var cmd = SegmentCommand.empty("", .{
618 .cmdsize = base_cmd.inner.cmdsize,
619 .vmaddr = base_cmd.inner.vmaddr,
620 .vmsize = base_cmd.inner.vmsize,
621 .maxprot = base_cmd.inner.maxprot,
622 .initprot = base_cmd.inner.initprot,
623 .nsects = base_cmd.inner.nsects,
624 .flags = base_cmd.inner.flags,
625 });
596 var cmd = SegmentCommand{
597 .inner = .{
598 .segname = undefined,
599 .cmdsize = base_cmd.inner.cmdsize,
600 .vmaddr = base_cmd.inner.vmaddr,
601 .vmsize = base_cmd.inner.vmsize,
602 .maxprot = base_cmd.inner.maxprot,
603 .initprot = base_cmd.inner.initprot,
604 .nsects = base_cmd.inner.nsects,
605 .flags = base_cmd.inner.flags,
606 },
607 };
626608 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
627609
628610 try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);
......@@ -692,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
692674}
693675
694676fn writeHeader(self: *DebugSymbols) !void {
695 var header = emptyHeader(.{
677 var header = commands.emptyHeader(.{
696678 .filetype = macho.MH_DSYM,
697679 });
698680
src/link/MachO/Object.zig+130-140
......@@ -1,6 +1,7 @@
11const Object = @This();
22
33const std = @import("std");
4const build_options = @import("build_options");
45const assert = std.debug.assert;
56const dwarf = std.dwarf;
67const fs = std.fs;
......@@ -13,11 +14,12 @@ const sort = std.sort;
1314const commands = @import("commands.zig");
1415const segmentName = commands.segmentName;
1516const sectionName = commands.sectionName;
17const trace = @import("../../tracy.zig").trace;
1618
1719const Allocator = mem.Allocator;
20const Atom = @import("Atom.zig");
1821const LoadCommand = commands.LoadCommand;
1922const MachO = @import("../MachO.zig");
20const TextBlock = @import("TextBlock.zig");
2123
2224file: fs.File,
2325name: []const u8,
......@@ -54,7 +56,7 @@ tu_name: ?[]const u8 = null,
5456tu_comp_dir: ?[]const u8 = null,
5557mtime: ?u64 = null,
5658
57text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
59atoms: std.ArrayListUnmanaged(*Atom) = .{},
5860sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5961
6062// TODO symbol mapping and its inverse can probably be simple arrays
......@@ -62,6 +64,8 @@ sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
6264symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
6365reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
6466
67analyzed: bool = false,
68
6569const DebugInfo = struct {
6670 inner: dwarf.DwarfInfo,
6771 debug_info: []u8,
......@@ -134,7 +138,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
134138 self.data_in_code_entries.deinit(allocator);
135139 self.symtab.deinit(allocator);
136140 self.strtab.deinit(allocator);
137 self.text_blocks.deinit(allocator);
141 self.atoms.deinit(allocator);
138142 self.sections_as_symbols.deinit(allocator);
139143 self.symbol_mapping.deinit(allocator);
140144 self.reverse_symbol_mapping.deinit(allocator);
......@@ -316,16 +320,17 @@ const Context = struct {
316320 object: *Object,
317321 macho_file: *MachO,
318322 match: MachO.MatchingSection,
323 parsed_atoms: *ParsedAtoms,
319324};
320325
321const TextBlockParser = struct {
326const AtomParser = struct {
322327 section: macho.section_64,
323328 code: []u8,
324329 relocs: []macho.relocation_info,
325330 nlists: []NlistWithIndex,
326331 index: u32 = 0,
327332
328 fn peek(self: TextBlockParser) ?NlistWithIndex {
333 fn peek(self: AtomParser) ?NlistWithIndex {
329334 return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
330335 }
331336
......@@ -339,9 +344,12 @@ const TextBlockParser = struct {
339344 }
340345 }
341346
342 pub fn next(self: *TextBlockParser, context: Context) !?*TextBlock {
347 pub fn next(self: *AtomParser, context: Context) !?*Atom {
343348 if (self.index == self.nlists.len) return null;
344349
350 const tracy = trace(@src());
351 defer tracy.end();
352
345353 var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);
346354 defer aliases.deinit();
347355
......@@ -364,12 +372,12 @@ const TextBlockParser = struct {
364372 }
365373
366374 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.
368376 sort.sort(
369377 NlistWithIndex,
370378 aliases.items,
371379 context,
372 TextBlockParser.lessThanBySeniority,
380 AtomParser.lessThanBySeniority,
373381 );
374382 }
375383
......@@ -389,12 +397,12 @@ const TextBlockParser = struct {
389397 else
390398 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: {
393401 // TODO there has to be a better to handle this.
394402 for (di.inner.func_list.items) |func| {
395403 if (func.pc_range) |range| {
396404 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{
398406 .function = range.end - range.start,
399407 };
400408 }
......@@ -405,28 +413,31 @@ const TextBlockParser = struct {
405413 break :blk .static;
406414 } else null;
407415
408 const block = try context.allocator.create(TextBlock);
409 block.* = TextBlock.empty;
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);
416 const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align);
417 atom.stab = stab;
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);
419428 for (aliases.items) |alias| {
420 block.aliases.appendAssumeCapacity(alias.index);
429 atom.aliases.appendAssumeCapacity(alias.index);
421430 const sym = &context.macho_file.locals.items[alias.index];
422431 sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
423432 }
424433
425 try block.parseRelocs(self.relocs, .{
426 .base_addr = start_addr,
434 try atom.parseRelocs(self.relocs, .{
435 .base_addr = self.section.addr,
436 .base_offset = start_addr,
427437 .allocator = context.allocator,
428438 .object = context.object,
429439 .macho_file = context.macho_file,
440 .parsed_atoms = context.parsed_atoms,
430441 });
431442
432443 if (context.macho_file.has_dices) {
......@@ -435,10 +446,10 @@ const TextBlockParser = struct {
435446 senior_nlist.nlist.n_value,
436447 senior_nlist.nlist.n_value + size,
437448 );
438 try block.dices.ensureTotalCapacity(context.allocator, dices.len);
449 try atom.dices.ensureTotalCapacity(context.allocator, dices.len);
439450
440451 for (dices) |dice| {
441 block.dices.appendAssumeCapacity(.{
452 atom.dices.appendAssumeCapacity(.{
442453 .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),
443454 .length = dice.length,
444455 .kind = dice.kind,
......@@ -448,16 +459,22 @@ const TextBlockParser = struct {
448459
449460 self.index += 1;
450461
451 return block;
462 return atom;
452463 }
453464};
454465
455pub fn parseTextBlocks(
466pub const ParsedAtoms = std.AutoHashMap(MachO.MatchingSection, *Atom);
467
468pub fn parseIntoAtoms(
456469 self: *Object,
457470 allocator: *Allocator,
458471 object_id: u16,
459472 macho_file: *MachO,
460) !void {
473) !ParsedAtoms {
474 const tracy = trace(@src());
475 defer tracy.end();
476
477 var parsed_atoms = ParsedAtoms.init(allocator);
461478 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
462479
463480 log.debug("analysing {s}", .{self.name});
......@@ -498,7 +515,7 @@ pub fn parseTextBlocks(
498515
499516 for (seg.sections.items) |sect, id| {
500517 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", .{
502519 segmentName(sect),
503520 sectionName(sect),
504521 });
......@@ -523,14 +540,17 @@ pub fn parseTextBlocks(
523540 // Symbols within this section only.
524541 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.
526545 // In release mode, if the object file was generated with dead code stripping optimisations,
527546 // note it now and parse sections as atoms.
528 const is_splittable = blk: {
529 if (macho_file.base.options.optimize_mode == .Debug) break :blk false;
530 break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
531 };
547 // const is_splittable = blk: {
548 // if (macho_file.base.options.optimize_mode == .Debug) break :blk false;
549 // break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
550 // };
551 const is_splittable = false;
532552
533 macho_file.has_dices = blk: {
553 macho_file.has_dices = macho_file.has_dices or blk: {
534554 if (self.text_section_index) |index| {
535555 if (index != id) break :blk false;
536556 if (self.data_in_code_entries.items.len == 0) break :blk false;
......@@ -541,12 +561,12 @@ pub fn parseTextBlocks(
541561 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
542562
543563 next: {
544 if (is_splittable) blocks: {
545 if (filtered_nlists.len == 0) break :blocks;
564 if (is_splittable) atoms: {
565 if (filtered_nlists.len == 0) break :atoms;
546566
547567 // If the first nlist does not match the start of the section,
548568 // 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.
550570 const first_nlist = filtered_nlists[0].nlist;
551571 if (first_nlist.n_value > sect.addr) {
552572 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
......@@ -556,44 +576,45 @@ pub fn parseTextBlocks(
556576 });
557577 defer allocator.free(sym_name);
558578
559 const block_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);
579 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
580 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
561581 try macho_file.locals.append(allocator, .{
562582 .n_strx = try macho_file.makeString(sym_name),
563583 .n_type = macho.N_SECT,
564584 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
565585 .n_desc = 0,
566 .n_value = sect.addr,
586 .n_value = 0,
567587 });
568 try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);
569 break :blk block_local_sym_index;
588 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
589 break :blk atom_local_sym_index;
570590 };
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];
573 const block_size = block_code.len;
574
575 const block = try allocator.create(TextBlock);
576 block.* = TextBlock.empty;
577 block.local_sym_index = block_local_sym_index;
578 block.size = block_size;
579 block.alignment = sect.@"align";
580 try macho_file.managed_blocks.append(allocator, block);
581
582 try block.code.appendSlice(allocator, block_code);
595 const is_zerofill = blk: {
596 const section_type = commands.sectionType(sect);
597 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
598 };
599 if (!is_zerofill) {
600 mem.copy(u8, atom.code.items, atom_code);
601 }
583602
584 try block.parseRelocs(relocs, .{
585 .base_addr = 0,
603 try atom.parseRelocs(relocs, .{
604 .base_addr = sect.addr,
605 .base_offset = 0,
586606 .allocator = allocator,
587607 .object = self,
588608 .macho_file = macho_file,
609 .parsed_atoms = &parsed_atoms,
589610 });
590611
591612 if (macho_file.has_dices) {
592 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
593 try block.dices.ensureTotalCapacity(allocator, dices.len);
613 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + atom_size);
614 try atom.dices.ensureTotalCapacity(allocator, dices.len);
594615
595616 for (dices) |dice| {
596 block.dices.appendAssumeCapacity(.{
617 atom.dices.appendAssumeCapacity(.{
597618 .offset = dice.offset - try math.cast(u32, sect.addr),
598619 .length = dice.length,
599620 .kind = dice.kind,
......@@ -601,29 +622,17 @@ pub fn parseTextBlocks(
601622 }
602623 }
603624
604 // Update target section's metadata
605 // TODO should we update segment's size here too?
606 // How does it tie with incremental space allocs?
607 const tseg = &macho_file.load_commands.items[match.seg].Segment;
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;
625 if (parsed_atoms.getPtr(match)) |last| {
626 last.*.next = atom;
627 atom.prev = last.*;
628 last.* = atom;
619629 } else {
620 try macho_file.blocks.putNoClobber(allocator, match, block);
630 try parsed_atoms.putNoClobber(match, atom);
621631 }
622
623 try self.text_blocks.append(allocator, block);
632 try self.atoms.append(allocator, atom);
624633 }
625634
626 var parser = TextBlockParser{
635 var parser = AtomParser{
627636 .section = sect,
628637 .code = code,
629638 .relocs = relocs,
......@@ -635,10 +644,11 @@ pub fn parseTextBlocks(
635644 .object = self,
636645 .macho_file = macho_file,
637646 .match = match,
638 })) |block| {
639 const sym = macho_file.locals.items[block.local_sym_index];
647 .parsed_atoms = &parsed_atoms,
648 })) |atom| {
649 const sym = macho_file.locals.items[atom.local_sym_index];
640650 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;
642652 break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]);
643653 };
644654 if (is_ext) {
......@@ -662,38 +672,26 @@ pub fn parseTextBlocks(
662672 // In x86_64 relocs, it can so happen that the compiler refers to the same
663673 // atom by both the actual assigned symbol and the start of the section. In this
664674 // 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);
666676 }
667677 }
668678
669 // Update target section's metadata
670 // TODO should we update segment's size here too?
671 // How does it tie with incremental space allocs?
672 const tseg = &macho_file.load_commands.items[match.seg].Segment;
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;
679 if (parsed_atoms.getPtr(match)) |last| {
680 last.*.next = atom;
681 atom.prev = last.*;
682 last.* = atom;
684683 } else {
685 try macho_file.blocks.putNoClobber(allocator, match, block);
684 try parsed_atoms.putNoClobber(match, atom);
686685 }
687
688 try self.text_blocks.append(allocator, block);
686 try self.atoms.append(allocator, atom);
689687 }
690688
691689 break :next;
692690 }
693691
694 // Since there is no symbol to refer to this block, we create
692 // Since there is no symbol to refer to this atom, we create
695693 // a temp one, unless we already did that when working out the relocations
696 // of other text blocks.
694 // of other atoms.
697695 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
698696 self.name,
699697 segmentName(sect),
......@@ -701,41 +699,43 @@ pub fn parseTextBlocks(
701699 });
702700 defer allocator.free(sym_name);
703701
704 const block_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);
702 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
703 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
706704 try macho_file.locals.append(allocator, .{
707705 .n_strx = try macho_file.makeString(sym_name),
708706 .n_type = macho.N_SECT,
709707 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
710708 .n_desc = 0,
711 .n_value = sect.addr,
709 .n_value = 0,
712710 });
713 try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);
714 break :blk block_local_sym_index;
711 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
712 break :blk atom_local_sym_index;
715713 };
714 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align");
716715
717 const block = try allocator.create(TextBlock);
718 block.* = TextBlock.empty;
719 block.local_sym_index = block_local_sym_index;
720 block.size = sect.size;
721 block.alignment = sect.@"align";
722 try macho_file.managed_blocks.append(allocator, block);
723
724 try block.code.appendSlice(allocator, code);
716 const is_zerofill = blk: {
717 const section_type = commands.sectionType(sect);
718 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
719 };
720 if (!is_zerofill) {
721 mem.copy(u8, atom.code.items, code);
722 }
725723
726 try block.parseRelocs(relocs, .{
727 .base_addr = 0,
724 try atom.parseRelocs(relocs, .{
725 .base_addr = sect.addr,
726 .base_offset = 0,
728727 .allocator = allocator,
729728 .object = self,
730729 .macho_file = macho_file,
730 .parsed_atoms = &parsed_atoms,
731731 });
732732
733733 if (macho_file.has_dices) {
734734 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
737737 for (dices) |dice| {
738 block.dices.appendAssumeCapacity(.{
738 atom.dices.appendAssumeCapacity(.{
739739 .offset = dice.offset - try math.cast(u32, sect.addr),
740740 .length = dice.length,
741741 .kind = dice.kind,
......@@ -743,12 +743,12 @@ pub fn parseTextBlocks(
743743 }
744744 }
745745
746 // Since this is block gets a helper local temporary symbol that didn't exist
746 // Since this is atom gets a helper local temporary symbol that didn't exist
747747 // in the object file which encompasses the entire section, we need traverse
748748 // the filtered symbols and note which symbol is contained within so that
749749 // we can properly allocate addresses down the line.
750750 // 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
753753 for (filtered_nlists) |nlist_with_index| {
754754 const nlist = nlist_with_index.nlist;
......@@ -756,12 +756,12 @@ pub fn parseTextBlocks(
756756 const local = &macho_file.locals.items[local_sym_index];
757757 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: {
760760 // TODO there has to be a better to handle this.
761761 for (di.inner.func_list.items) |func| {
762762 if (func.pc_range) |range| {
763763 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
764 break :blk TextBlock.Stab{
764 break :blk Atom.Stab{
765765 .function = range.end - range.start,
766766 };
767767 }
......@@ -772,35 +772,25 @@ pub fn parseTextBlocks(
772772 break :blk .static;
773773 } else null;
774774
775 block.contained.appendAssumeCapacity(.{
775 atom.contained.appendAssumeCapacity(.{
776776 .local_sym_index = local_sym_index,
777777 .offset = nlist.n_value - sect.addr,
778778 .stab = stab,
779779 });
780780 }
781781
782 // Update target section's metadata
783 // TODO should we update segment's size here too?
784 // How does it tie with incremental space allocs?
785 const tseg = &macho_file.load_commands.items[match.seg].Segment;
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;
782 if (parsed_atoms.getPtr(match)) |last| {
783 last.*.next = atom;
784 atom.prev = last.*;
785 last.* = atom;
797786 } else {
798 try macho_file.blocks.putNoClobber(allocator, match, block);
787 try parsed_atoms.putNoClobber(match, atom);
799788 }
800
801 try self.text_blocks.append(allocator, block);
789 try self.atoms.append(allocator, atom);
802790 }
803791 }
792
793 return parsed_atoms;
804794}
805795
806796fn 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 {
99 name: ?[]const u8 = null,
1010};
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
2112pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
2213 var stream = std.io.countingWriter(std.io.null_writer);
2314 var writer = stream.writer();
src/link/MachO/commands.zig+8-93
......@@ -9,6 +9,7 @@ const assert = std.debug.assert;
99
1010const Allocator = std.mem.Allocator;
1111const MachO = @import("../MachO.zig");
12const makeStaticString = MachO.makeStaticString;
1213const padToIdeal = MachO.padToIdeal;
1314
1415pub const HeaderArgs = struct {
......@@ -217,75 +218,6 @@ pub const SegmentCommand = struct {
217218 inner: macho.segment_command_64,
218219 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
289221 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
290222 const inner = try reader.readStruct(macho.segment_command_64);
291223 var segment = SegmentCommand{
......@@ -314,10 +246,8 @@ pub const SegmentCommand = struct {
314246 }
315247
316248 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
317 assert(start > 0);
318 if (start == self.inner.fileoff)
319 return 0;
320 var min_pos: u64 = std.math.maxInt(u64);
249 assert(start >= self.inner.fileoff);
250 var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
321251 for (self.sections.items) |section| {
322252 if (section.offset <= start) continue;
323253 if (section.offset < min_pos) min_pos = section.offset;
......@@ -337,12 +267,12 @@ pub const SegmentCommand = struct {
337267 return null;
338268 }
339269
340 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16, start: ?u64) u64 {
341 var st: u64 = if (start) |v| v else self.inner.fileoff;
342 while (self.detectAllocCollision(st, object_size)) |item_end| {
343 st = mem.alignForwardGeneric(u64, item_end, min_alignment);
270 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
271 var offset: u64 = if (start) |v| v else self.inner.fileoff;
272 while (self.detectAllocCollision(offset, object_size)) |item_end| {
273 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
344274 }
345 return st;
275 return offset;
346276 }
347277
348278 fn eql(self: SegmentCommand, other: SegmentCommand) bool {
......@@ -427,13 +357,6 @@ pub fn createLoadDylibCommand(
427357 return dylib_cmd;
428358}
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
437360fn parseName(name: *const [16]u8) []const u8 {
438361 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
439362 return name[0..len];
......@@ -514,17 +437,14 @@ test "read-write segment command" {
514437 };
515438 var cmd = SegmentCommand{
516439 .inner = .{
517 .cmd = macho.LC_SEGMENT_64,
518440 .cmdsize = 152,
519441 .segname = makeStaticString("__TEXT"),
520442 .vmaddr = 4294967296,
521443 .vmsize = 294912,
522 .fileoff = 0,
523444 .filesize = 294912,
524445 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
525446 .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
526447 .nsects = 1,
527 .flags = 0,
528448 },
529449 };
530450 try cmd.sections.append(gpa, .{
......@@ -534,12 +454,7 @@ test "read-write segment command" {
534454 .size = 448,
535455 .offset = 16384,
536456 .@"align" = 2,
537 .reloff = 0,
538 .nreloc = 0,
539457 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
540 .reserved1 = 0,
541 .reserved2 = 0,
542 .reserved3 = 0,
543458 });
544459 defer cmd.deinit(gpa);
545460 try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
test/stage2/darwin.zig+11-11
......@@ -27,8 +27,8 @@ pub fn addCases(ctx: *TestContext) !void {
2727
2828 // Regular old hello world
2929 case.addCompareOutput(
30 \\extern "c" fn write(usize, usize, usize) usize;
31 \\extern "c" fn exit(usize) noreturn;
30 \\extern fn write(usize, usize, usize) usize;
31 \\extern fn exit(usize) noreturn;
3232 \\
3333 \\pub export fn main() noreturn {
3434 \\ print();
......@@ -47,8 +47,8 @@ pub fn addCases(ctx: *TestContext) !void {
4747
4848 // Print it 4 times and force growth and realloc.
4949 case.addCompareOutput(
50 \\extern "c" fn write(usize, usize, usize) usize;
51 \\extern "c" fn exit(usize) noreturn;
50 \\extern fn write(usize, usize, usize) usize;
51 \\extern fn exit(usize) noreturn;
5252 \\
5353 \\pub export fn main() noreturn {
5454 \\ print();
......@@ -74,8 +74,8 @@ pub fn addCases(ctx: *TestContext) !void {
7474
7575 // Print it once, and change the message.
7676 case.addCompareOutput(
77 \\extern "c" fn write(usize, usize, usize) usize;
78 \\extern "c" fn exit(usize) noreturn;
77 \\extern fn write(usize, usize, usize) usize;
78 \\extern fn exit(usize) noreturn;
7979 \\
8080 \\pub export fn main() noreturn {
8181 \\ print();
......@@ -94,8 +94,8 @@ pub fn addCases(ctx: *TestContext) !void {
9494
9595 // Now we print it twice.
9696 case.addCompareOutput(
97 \\extern "c" fn write(usize, usize, usize) usize;
98 \\extern "c" fn exit(usize) noreturn;
97 \\extern fn write(usize, usize, usize) usize;
98 \\extern fn exit(usize) noreturn;
9999 \\
100100 \\pub export fn main() noreturn {
101101 \\ print();
......@@ -121,7 +121,7 @@ pub fn addCases(ctx: *TestContext) !void {
121121 // This test case also covers an infrequent scenarion where the string table *may* be relocated
122122 // into the position preceeding the symbol table which results in a dyld error.
123123 case.addCompareOutput(
124 \\extern "c" fn exit(usize) noreturn;
124 \\extern fn exit(usize) noreturn;
125125 \\
126126 \\pub export fn main() noreturn {
127127 \\ exit(0);
......@@ -131,8 +131,8 @@ pub fn addCases(ctx: *TestContext) !void {
131131 );
132132
133133 case.addCompareOutput(
134 \\extern "c" fn exit(usize) noreturn;
135 \\extern "c" fn write(usize, usize, usize) usize;
134 \\extern fn exit(usize) noreturn;
135 \\extern fn write(usize, usize, usize) usize;
136136 \\
137137 \\pub export fn main() noreturn {
138138 \\ _ = write(1, @ptrToInt("Hey!\n"), 5);